diff --git a/pumpingStation.html b/pumpingStation.html
index 97b42ae..8c56986 100644
--- a/pumpingStation.html
+++ b/pumpingStation.html
@@ -58,7 +58,7 @@
icon: "font-awesome/fa-tint",
label: function () {
- return this.positionIcon + " " + this.assetType || "pumpingStation";
+ return this.positionIcon + " PumpingStation";
},
oneditprepare: function() {
diff --git a/src/nodeClass.js b/src/nodeClass.js
index 404686a..b25598a 100644
--- a/src/nodeClass.js
+++ b/src/nodeClass.js
@@ -88,10 +88,7 @@ class nodeClass {
}
- /**
- * Register this node as a child upstream and downstream.
- * Delayed to avoid Node-RED startup race conditions.
- */
+ // init registration msg
_registerChild() {
setTimeout(() => {
this.node.send([
@@ -102,12 +99,78 @@ class nodeClass {
}, 100);
}
- /**
- * Start the periodic tick loop to drive the Measurement class.
- */
+ _updateNodeStatus() {
+ const ps = this.source;
+ try {
+ // --- Basin & measurements -------------------------------------------------
+ const maxVolBeforeOverflow = ps.basin?.maxVolOverflow ?? ps.basin?.maxVol ?? 0;
+ const volumeMeasurement = ps.measurements.type("volume").variant("measured").position("atEquipment");
+ const currentVolume = volumeMeasurement.getCurrentValue("m3") ?? 0;
+ const netFlowMeasurement = ps.measurements.type("netFlowRate").variant("predicted").position("atEquipment");
+ const netFlowM3s = netFlowMeasurement?.getCurrentValue("m3/s") ?? 0;
+ const netFlowM3h = netFlowM3s * 3600;
+ const percentFull = ps.measurements.type("volume").variant("procent").position("atEquipment").getCurrentValue() ?? 0;
+
+ // --- State information ----------------------------------------------------
+ const direction = ps.state?.direction || "unknown";
+ const secondsRemaining = ps.state?.seconds ?? null;
+
+ const timeRemaining = secondsRemaining ? `${Math.round(secondsRemaining / 60)}` : 0 + " min";
+
+ // --- Icon / colour selection ---------------------------------------------
+ let symbol = "❔";
+ let fill = "grey";
+
+ switch (direction) {
+ case "filling":
+ symbol = "⬆️";
+ fill = "blue";
+ break;
+ case "draining":
+ symbol = "⬇️";
+ fill = "orange";
+ break;
+ case "stable":
+ symbol = "⏸️";
+ fill = "green";
+ break;
+ default:
+ symbol = "❔";
+ fill = "grey";
+ break;
+ }
+
+ // --- Status text ----------------------------------------------------------
+ const textParts = [
+ `${symbol} ${percentFull.toFixed(1)}%`,
+ `V=${currentVolume.toFixed(2)} / ${maxVolBeforeOverflow.toFixed(2)} m³`,
+ `net=${netFlowM3h.toFixed(1)} m³/h`,
+ `t≈${timeRemaining}`
+ ];
+
+ return {
+ fill,
+ shape: "dot",
+ text: textParts.join(" | ")
+ };
+ } catch (error) {
+ this.node.error("Error in updateNodeStatus: " + error.message);
+ return { fill: "red", shape: "ring", text: "Status Error" };
+ }
+ }
+
+
+ // any time based functions here
_startTickLoop() {
setTimeout(() => {
this._tickInterval = setInterval(() => this._tick(), 1000);
+
+ // Update node status on nodered screen every second ( this is not the best way to do this, but it works for now)
+ this._statusInterval = setInterval(() => {
+ const status = this._updateNodeStatus();
+ this.node.status(status);
+ }, 1000);
+
}, 1000);
}
@@ -156,7 +219,7 @@ class nodeClass {
_attachCloseHandler() {
this.node.on('close', (done) => {
clearInterval(this._tickInterval);
- //clearInterval(this._statusInterval);
+ clearInterval(this._statusInterval);
done();
});
}
diff --git a/src/specificClass.js b/src/specificClass.js
index 87c1339..84aa41e 100644
--- a/src/specificClass.js
+++ b/src/specificClass.js
@@ -1,5 +1,5 @@
const EventEmitter = require('events');
-const {logger,configUtils,configManager,childRegistrationUtils,MeasurementContainer,coolprop} = require('generalFunctions');
+const {logger,configUtils,configManager,childRegistrationUtils,MeasurementContainer,coolprop,interpolation} = require('generalFunctions');
class pumpingStation {
constructor(config={}) {
@@ -9,6 +9,7 @@ class pumpingStation {
this.defaultConfig = this.configManager.getConfig('pumpingStation');
this.configUtils = new configUtils(this.defaultConfig);
this.config = this.configUtils.initConfig(config);
+ this.interpolate = new interpolation();
// Init after config is set
this.logger = new logger(this.config.general.logging.enabled,this.config.general.logging.logLevel, this.config.general.name);
@@ -118,11 +119,10 @@ class pumpingStation {
const level = pressure_Pa / density * g;
this.measurements.type("level").variant("predicted").position(position).value(level);
- //updatePredictedLevel(); ??
+ //updatePredictedLevel(); ?? OLIFANT!
//calculate how muc flow went in or out based on pressure difference
this.logger.debug(`Using pressure: ${value} for calculations`);
-
}
@@ -134,7 +134,13 @@ class pumpingStation {
const level = this.measurements.type("level").variant("measured").position(position).getCurrentValue('m');
//calc vol in m3
const volume = this._calcVolumeFromLevel(level);
+ this.logger.debug(`basin minvol : ${this.basin.minVol}, cur volume : ${volume} / ${this.basin.maxVolOverflow}`);
+
+ const proc = this.interpolate.interpolate_lin_single_point(volume,this.basin.minVol,this.basin.maxVolOverflow,0,100);
+this.logger.debug(`PROC volume : ${proc}`);
this.measurements.type("volume").variant("measured").position("atEquipment").value(volume).unit('m3');
+ this.measurements.type("volume").variant("procent").position("atEquipment").value(proc)
+
//calc the most important values back to determine state and net up or downstream flow
this._calcNetFlow();
@@ -142,6 +148,7 @@ class pumpingStation {
}
+
_calcNetFlow() {
const { heightOverflow, heightOutlet, surfaceArea } = this.basin;
@@ -177,35 +184,17 @@ class pumpingStation {
}
_calcNetFlowFromMeasurements({ heightOverflow, heightOutlet, surfaceArea }) {
- const flowDiff = this.measurements
- .type("flow")
- .variant("measured")
- .difference({ from: "downstream", to: "upstream", unit: "m3/s" });
- const level = this.measurements
- .type("level")
- .variant("measured")
- .position("atEquipment")
- .getCurrentValue("m");
-
- const flowUpstream = this.measurements
- .type("flow")
- .variant("measured")
- .position("upstream")
- .getCurrentValue("m3/s");
-
- const flowDownstream = this.measurements
- .type("flow")
- .variant("measured")
- .position("downstream")
- .getCurrentValue("m3/s");
+ const flowDiff = this.measurements.type("flow").variant("measured").difference({ from: "downstream", to: "upstream", unit: "m3/s" });
+ const level = this.measurements.type("level").variant("measured").position("atEquipment").getCurrentValue("m");
+ const flowUpstream = this.measurements.type("flow").variant("measured").position("upstream").getCurrentValue("m3/s");
+ const flowDownstream = this.measurements.type("flow").variant("measured").position("downstream").getCurrentValue("m3/s");
if (flowDiff === null || level === null) {
this.logger.warn(`no flowdiff ${flowDiff} or level ${level} found escaping`);
return null;
}
-
const flowThreshold = 0.1; // m³/s
const state = { direction: "stable", seconds: 0, netUpstream: flowUpstream ?? 0, netDownstream: flowDownstream ?? 0 };
@@ -219,12 +208,7 @@ class pumpingStation {
state.seconds = remainingHeight * surfaceArea / Math.abs(flowDiff);
}
- this.measurements
- .type("netFlowRate")
- .variant("predicted")
- .position("atEquipment")
- .value(flowDiff)
- .unit("m3/s");
+ this.measurements.type("netFlowRate").variant("predicted").position("atEquipment").value(flowDiff).unit("m3/s");
this.logger.debug(
`Flow-based net flow | diff=${flowDiff.toFixed(3)} m3/s, level=${level.toFixed(3)} m`
@@ -288,7 +272,6 @@ class pumpingStation {
}
initBasinProperties() {
-
// Load and calc basic params
const volEmptyBasin = this.config.basin.volume;
@@ -301,8 +284,8 @@ class pumpingStation {
const surfaceArea = volEmptyBasin / heightBasin;
const maxVol = heightBasin * surfaceArea; // if Basin where to ever fill up completely this is the water volume
const maxVolOverflow = heightOverflow * surfaceArea ; // Max water volume before you start loosing water to overflow
- const minVol = heightInlet * surfaceArea;
- const minVolOut = heightOutlet * surfaceArea ; // this will indicate if its an open end or a closed end.
+ const minVol = heightOutlet * surfaceArea;
+ const minVolOut = heightInlet * surfaceArea ; // this will indicate if its an open end or a closed end.
this.basin.volEmptyBasin = volEmptyBasin ;
this.basin.heightBasin = heightBasin ;