diff --git a/src/specificClass.js b/src/specificClass.js index d7b2ca2..a9da992 100644 --- a/src/specificClass.js +++ b/src/specificClass.js @@ -21,7 +21,13 @@ class pumpingStation { // init basin object in pumping station this.basin = {}; - this.state = { direction:"", netDownstream:0, netUpstream:0, seconds:0}; // init state object of pumping station to see whats going on + this.state = { + direction: "steady", + netFlow: 0, + flowSource: null, + seconds: null, + remainingSource: null + }; // init state object of pumping station to see whats going on // Initialize basin-specific properties and calculate used parameters this.initBasinProperties(); @@ -211,12 +217,80 @@ class pumpingStation { //go through all the functions that require time based checks or updates this._updateVolumePrediction("out"); //check for changes in outgoing flow this._updateVolumePrediction("in"); // check for changes in incomming flow - //calc the most important values back to determine state and net up or downstream flow + + //calc the most important values back to determine state and net up or downstream flow this._calcNetFlow(); - this._calcTimeRemaining(); - + const {time:timeleft, source:variant} = this._calcTimeRemaining(); + + + this.logger.debug(`Remaining time ${timeleft}, based on variant ${variant} `); + } + + _calcTimeRemaining(){ + //init timeRemaining + const winningTime = {time:0,source:""}; + + //calculate time left prioritise flow based variant + const { time: flowTime, variant: flowVariant } = this._selectBestRemainingTimeFlowVariant(); + + //if flow doesnt work then use level based varianti to calc timeleft + if(flowVariant == null){ + const {time: levelTime, variant: levelVariant} = this._selectBestRemainingTimeLevelVariant(); + winningTime.time = levelTime; + winningTime.source = levelVariant; + if(levelVariant == null){ + winningTime.time = null; + winningTime.source = null; + } + } + else{ + winningTime.time = flowTime; + winningTime.source = flowVariant; + } + + return winningTime; + } + + // Select remaining time based on flow + level variation measured or predicted and give back {time:0,variant:null}; + _selectBestRemainingTimeFlowVariant(){ + + //define variants + const remainingTimeVariants = [ + { flowVariant: "measured", levelVariant: "measured" }, + { flowVariant: "measured", levelVariant: "predicted" }, + { flowVariant: "predicted", levelVariant: "measured" }, + { flowVariant: "predicted", levelVariant: "predicted" } + ]; + + let remainingT = null; + + for (const variant of remainingTimeVariants) { + const candidate = this._calcRemainingTimeBasedOnFlow(variant); + if (candidate != null) { + remainingT = candidate; + return {time:remainingT,variant:variant}; + } + } + return {time:0,variant:null}; + } + + // Select remaining time based only on level variation measured or predicted and give back {time:0,variant:null}; + _selectBestRemainingTimeLevelVariant(){ + + //define variants (in sequence of priority first measured then predicted etc...) + const remainingTimeVariants = ["measured","predicted"]; + + let remainingT = null; + + for (const variant of remainingTimeVariants) { + const candidate = this._calcRemainingTimeBasedOnLevel(variant); + if (candidate != null) { + remainingT = candidate; + return {time:remainingT,variant:variant}; + } + } + return {time:0,variant:null}; } - _callMeasurementHandler(measurementType, value, position, context) { switch (measurementType) { @@ -319,33 +393,96 @@ class pumpingStation { this.logger.warn(`Can't calculate netflow without the proper measurements or predictions`); return null; } - } - _calcRemainingTime(level,variant){ + //@params : params : example {flowVariant: "predicted",levelVariant: "measured"}; + _calcRemainingTimeBasedOnFlow(params){ + const {flowVariant,levelVariant} = params; + this.logger.debug(`${flowVariant} - ${levelVariant} `); + if( flowVariant === null || levelVariant === null ){ + this.logger.warn(`Cant calculate remaining time without needed variants`); + return 0; + } + const { heightOverflow, heightOutlet, surfaceArea } = this.basin; - const flowDiff = this.measurements.type("flow").variant(variant).difference({ from: "downstream", to: "upstream", unit: "m3/s" }); + const levelexists = this.measurements.type("level").variant(levelVariant).exists({ position: "atEquipment", requireValues: true }); + const flowOutExists = this.measurements.type("flow").variant(flowVariant).exists({ position: "out", requireValues: true }); + const flowInExists = this.measurements.type("flow").variant(flowVariant).exists({ position: "in", requireValues: true }); + let secondsRemaining = 0; + + if( ! flowOutExists || ! flowInExists || ! levelexists){ + this.logger.warn(`Cant calculate remaining time without needed parameters ${flowOutExists} , ${flowInExists} , ${levelexists}`); + return null; + } + + const flowDiff = this.measurements.type("flow").variant(flowVariant).difference({ from: "downstream", to: "upstream", unit: "m3/s" }); + const level = this.measurements.type("level").variant(levelVariant).type('atEquipment').getCurrentValue('m'); + let remainingHeight = 0; switch(true){ + case(flowDiff>0): remainingHeight = Math.max(heightOverflow - level, 0); - this.state.seconds = remainingHeight * surfaceArea / flowDiff; - break; + secondsRemaining = remainingHeight * surfaceArea / flowDiff; + return secondsRemaining; case(flowDiff<0): remainingHeight = Math.max(level - heightOutlet, 0); - this.state.seconds = remainingHeight * surfaceArea / Math.abs(flowDiff); - break; + secondsRemaining = remainingHeight * surfaceArea / Math.abs(flowDiff); + return secondsRemaining; default: - this.logger.debug(`doing nothing with level calc`) - + this.logger.debug(`Flowdiff is 0 not doing anything.`); + return secondsRemaining; } } - _calcDirection(flowDiff){ + //@params : variant : example "predicted","measured" + _calcRemainingTimeBasedOnLevel(variant){ + + const levelObj = this.measurements.type("level").variant(variant).position("atEquipment"); + const level = levelObj.getCurrentValue("m"); + const prevLevel = levelObj.getLaggedValue(2, "m"); // { value, timestamp, unit } + const measurement = levelObj.get(); + const latestTimestamp = measurement?.getLatestTimestamp(); + + if (level === null || !prevLevel || latestTimestamp == null) { + this.logger.warn(`no flowdiff ${level}, previous level ${prevLevel}, latestTimestamp ${latestTimestamp} found escaping`); + return null; + } + + const deltaSeconds = (latestTimestamp - prevLevel.timestamp) / 1000; + if (deltaSeconds <= 0) { + this.logger.warn(`Level fallback: invalid Δt=${deltaSeconds} , LatestTimestamp : ${latestTimestamp}, PrevTimestamp : ${prevLevel.timestamp}`); + return null; + } + + const lvlDiff = level - prevLevel.value; + const lvlRate = Math.abs(lvlDiff / deltaSeconds); // m/s + + switch(true){ + + case(lvlRate>0): + remainingHeight = Math.max(heightOverflow - level, 0); + secondsRemaining = remainingHeight / lvlRate; // seconds + return secondsRemaining; + + case(lvlRate<0): + remainingHeight = Math.max(level - heightOutlet, 0); + secondsRemaining = remainingHeight / lvlRate; + return secondsRemaining; + + default: + this.logger.debug(`Flowdiff is 0 not doing anything.`); + return secondsRemaining; + } + + } + + //Give a flowDifference and calculate direction => spits out filling , draining or stable + _calcDirectionBasedOnFlow(flowDiff){ let direction = null; @@ -370,9 +507,9 @@ class pumpingStation { return direction; } - _calcNetFlowFromLevelDiff() { + _calcNetFlowFromLevelDiff(variant) { const { surfaceArea } = this.basin; - const levelObj = this.measurements.type("level").variant("measured").position("atEquipment"); + const levelObj = this.measurements.type("level").variant(variant).position("atEquipment"); const level = levelObj.getCurrentValue("m"); const prevLevel = levelObj.getLaggedValue(2, "m"); // { value, timestamp, unit } const measurement = levelObj.get();