diff --git a/src/specificClass.js b/src/specificClass.js index d4dbdfe..d2a79bb 100644 --- a/src/specificClass.js +++ b/src/specificClass.js @@ -23,7 +23,9 @@ class PumpingStation { //variants in determining what gets priority this.flowVariants = ['measured', 'predicted']; this.levelVariants = ['measured', 'predicted']; + this.volVariants = ['measured', 'predicted']; this.flowPositions = { inflow: ['in', 'upstream'], outflow: ['out', 'downstream'] }; + this.predictedFlowChildren = new Map(); // childId -> { in: 0, out: 0 } this.basin = {}; this.state = { @@ -49,7 +51,7 @@ class PumpingStation { return; } - if (softwareType === 'machine' || softwareType === 'pumpingStation') { + if (softwareType === 'machine' || softwareType === 'pumpingStation' || softwareType === 'machineGroupController') { this._registerPredictedFlowChild(child); return; } @@ -57,16 +59,38 @@ class PumpingStation { this.logger.warn(`Unsupported child software type: ${softwareType}`); } + _safeGuardSystem(snapshot,remainingTime){ + let vol = null; + + for (const variant of this.volVariants){ + const volsnap = snapshot.vols[variant]; + //go through with variants until we find one that exists + if (!volsnap.samples.exists){ continue}; + + const vol = volsnap.samples.current?.value ?? null; + } + + if(vol == null){ + //if we cant get a volume, we must force whole system off. + + }; +/* + if(remainingTime < timeThreshhold || vol > maxVolume || vol < minVolume){} + */ + + } + tick() { const snapshot = this._takeMeasurementSnapshot(); this._updatePredictedVolume(snapshot); + const netFlow = this._selectBestNetFlow(snapshot); - //write netflow in measurment container - const remaining = this._computeRemainingTime(snapshot, netFlow); + this._safeGuardSystem(snapshot,remaining.seconds); + this.state = { direction: netFlow.direction, netFlow: netFlow.value, @@ -100,32 +124,63 @@ class PumpingStation { }); } + //register machines or pumping stations that can provide predicted flow data _registerPredictedFlowChild(child) { const position = child.config.functionality.positionVsParent; const childName = child.config.general.name; + const childId = child.config.general.id ?? childName; - const listener = (eventName, posKey) => { - child.measurements.emitter.on(eventName, (eventData) => { - this.logger.debug( - `Predicted flow update from ${childName} (${position}) -> ${eventData.value} ${eventData.unit}` - ); - this.measurements - .type('flow') - .variant('predicted') - .position(posKey) - .value(eventData.value, eventData.timestamp, eventData.unit); - }); + const posKey = + position === 'downstream' || position === 'out' || position === 'atequipment' + ? 'out' + : position === 'upstream' || position === 'in' + ? 'in' + : null; + + if (!posKey) { + this.logger.warn(`Unsupported predicted flow position "${position}" from ${childName}`); + return; + } + + if (!this.predictedFlowChildren.has(childId)) { + this.predictedFlowChildren.set(childId, { in: 0, out: 0 }); + } + + const handler = (eventData = {}) => { + const value = Number.isFinite(eventData.value) ? eventData.value : 0; + const timestamp = eventData.timestamp ?? Date.now(); + const unit = eventData.unit ?? 'm3/s'; + + this.logger.debug( + `Predicted flow update from ${childName} (${childId}, ${posKey}) -> ${value} ${unit}` + ); + + this.predictedFlowChildren.get(childId)[posKey] = value; + this._refreshAggregatedPredictedFlow(posKey, timestamp, unit); }; - if (position === 'downstream' || position === 'atequipment' || position === 'out') { - listener('flow.predicted.downstream', 'out'); - } else if (position === 'upstream' || position === 'in') { - listener('flow.predicted.downstream', 'in'); - } else { - this.logger.warn(`Unsupported predicted flow position "${position}" from ${childName}`); + const eventNames = + posKey === 'in' + ? ['flow.predicted.downstream', 'flow.predicted.upstream'] + : ['flow.predicted.downstream']; + + for (const eventName of eventNames) { + child.measurements.emitter.on(eventName, handler); } } + _refreshAggregatedPredictedFlow(direction, timestamp = Date.now(), unit = 'm3/s') { + const sum = Array.from(this.predictedFlowChildren.values()) + .map((entry) => (Number.isFinite(entry[direction]) ? entry[direction] : 0)) + .reduce((acc, val) => acc + val, 0); + + this.measurements + .type('flow') + .variant('predicted') + .position(direction) + .value(sum, timestamp, unit); + } + _handleMeasurement(measurementType, value, position, context) { switch (measurementType) { case 'level': @@ -213,13 +268,18 @@ class PumpingStation { const snapshot = { flows: {}, levels: {}, - levelRates: {} + levelRates: {}, + vols:{}, }; for (const variant of this.flowVariants) { snapshot.flows[variant] = this._snapshotFlowsForVariant(variant); } + for (const variant of this.volVariants){ + snapshot.vols[variant] = this._snapshotVolsForVariant(variant); + } + for (const variant of this.levelVariants) { snapshot.levels[variant] = this._snapshotLevelForVariant(variant); snapshot.levelRates[variant] = this._estimateLevelRate(snapshot.levels[variant]); @@ -228,15 +288,17 @@ class PumpingStation { return snapshot; } + _snapshotVolsForVariant(variant) { + const volumeSeries = this._locateSeries('volume', variant, ['atequipment']); + + return {variant,samples: this._seriesSamples(volumeSeries)}; + } + _snapshotFlowsForVariant(variant) { const inflowSeries = this._locateSeries('flow', variant, this.flowPositions.inflow); const outflowSeries = this._locateSeries('flow', variant, this.flowPositions.outflow); - return { - variant, - inflow: this._seriesSamples(inflowSeries), - outflow: this._seriesSamples(outflowSeries) - }; + return {variant, inflow: this._seriesSamples(inflowSeries), outflow: this._seriesSamples(outflowSeries) }; } _snapshotLevelForVariant(variant) { @@ -342,6 +404,7 @@ class PumpingStation { return { seconds: null, source: null }; } + for (const variant of this.levelVariants) { const levelSnap = snapshot.levels[variant]; const current = levelSnap.samples.current?.value ?? null; @@ -405,10 +468,7 @@ class PumpingStation { const writeTimestamp = timestampPrev + Math.max(deltaSeconds, 0) * 1000; - const volumeSeries = this.measurements - .type('volume') - .variant('predicted') - .position('atEquipment'); + const volumeSeries = this.measurements.type('volume').variant('predicted').position('atEquipment'); const currentVolume = volumeSeries.getCurrentValue('m3') ?? this.basin.minVol;