latest version
This commit is contained in:
@@ -58,7 +58,7 @@
|
|||||||
icon: "font-awesome/fa-tint",
|
icon: "font-awesome/fa-tint",
|
||||||
|
|
||||||
label: function () {
|
label: function () {
|
||||||
return this.positionIcon + " " + this.assetType || "pumpingStation";
|
return this.positionIcon + " PumpingStation";
|
||||||
},
|
},
|
||||||
|
|
||||||
oneditprepare: function() {
|
oneditprepare: function() {
|
||||||
|
|||||||
@@ -88,10 +88,7 @@ class nodeClass {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// init registration msg
|
||||||
* Register this node as a child upstream and downstream.
|
|
||||||
* Delayed to avoid Node-RED startup race conditions.
|
|
||||||
*/
|
|
||||||
_registerChild() {
|
_registerChild() {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.node.send([
|
this.node.send([
|
||||||
@@ -102,12 +99,78 @@ class nodeClass {
|
|||||||
}, 100);
|
}, 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
_updateNodeStatus() {
|
||||||
* Start the periodic tick loop to drive the Measurement class.
|
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() {
|
_startTickLoop() {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this._tickInterval = setInterval(() => this._tick(), 1000);
|
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);
|
}, 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -156,7 +219,7 @@ class nodeClass {
|
|||||||
_attachCloseHandler() {
|
_attachCloseHandler() {
|
||||||
this.node.on('close', (done) => {
|
this.node.on('close', (done) => {
|
||||||
clearInterval(this._tickInterval);
|
clearInterval(this._tickInterval);
|
||||||
//clearInterval(this._statusInterval);
|
clearInterval(this._statusInterval);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
const EventEmitter = require('events');
|
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 {
|
class pumpingStation {
|
||||||
constructor(config={}) {
|
constructor(config={}) {
|
||||||
@@ -9,6 +9,7 @@ class pumpingStation {
|
|||||||
this.defaultConfig = this.configManager.getConfig('pumpingStation');
|
this.defaultConfig = this.configManager.getConfig('pumpingStation');
|
||||||
this.configUtils = new configUtils(this.defaultConfig);
|
this.configUtils = new configUtils(this.defaultConfig);
|
||||||
this.config = this.configUtils.initConfig(config);
|
this.config = this.configUtils.initConfig(config);
|
||||||
|
this.interpolate = new interpolation();
|
||||||
|
|
||||||
// Init after config is set
|
// Init after config is set
|
||||||
this.logger = new logger(this.config.general.logging.enabled,this.config.general.logging.logLevel, this.config.general.name);
|
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;
|
const level = pressure_Pa / density * g;
|
||||||
|
|
||||||
this.measurements.type("level").variant("predicted").position(position).value(level);
|
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
|
//calculate how muc flow went in or out based on pressure difference
|
||||||
this.logger.debug(`Using pressure: ${value} for calculations`);
|
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');
|
const level = this.measurements.type("level").variant("measured").position(position).getCurrentValue('m');
|
||||||
//calc vol in m3
|
//calc vol in m3
|
||||||
const volume = this._calcVolumeFromLevel(level);
|
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("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
|
//calc the most important values back to determine state and net up or downstream flow
|
||||||
this._calcNetFlow();
|
this._calcNetFlow();
|
||||||
@@ -142,6 +148,7 @@ class pumpingStation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
_calcNetFlow() {
|
_calcNetFlow() {
|
||||||
const { heightOverflow, heightOutlet, surfaceArea } = this.basin;
|
const { heightOverflow, heightOutlet, surfaceArea } = this.basin;
|
||||||
|
|
||||||
@@ -177,35 +184,17 @@ class pumpingStation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_calcNetFlowFromMeasurements({ heightOverflow, heightOutlet, surfaceArea }) {
|
_calcNetFlowFromMeasurements({ heightOverflow, heightOutlet, surfaceArea }) {
|
||||||
const flowDiff = this.measurements
|
|
||||||
.type("flow")
|
|
||||||
.variant("measured")
|
|
||||||
.difference({ from: "downstream", to: "upstream", unit: "m3/s" });
|
|
||||||
|
|
||||||
const level = this.measurements
|
const flowDiff = this.measurements.type("flow").variant("measured").difference({ from: "downstream", to: "upstream", unit: "m3/s" });
|
||||||
.type("level")
|
const level = this.measurements.type("level").variant("measured").position("atEquipment").getCurrentValue("m");
|
||||||
.variant("measured")
|
const flowUpstream = this.measurements.type("flow").variant("measured").position("upstream").getCurrentValue("m3/s");
|
||||||
.position("atEquipment")
|
const flowDownstream = this.measurements.type("flow").variant("measured").position("downstream").getCurrentValue("m3/s");
|
||||||
.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) {
|
if (flowDiff === null || level === null) {
|
||||||
this.logger.warn(`no flowdiff ${flowDiff} or level ${level} found escaping`);
|
this.logger.warn(`no flowdiff ${flowDiff} or level ${level} found escaping`);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const flowThreshold = 0.1; // m³/s
|
const flowThreshold = 0.1; // m³/s
|
||||||
const state = { direction: "stable", seconds: 0, netUpstream: flowUpstream ?? 0, netDownstream: flowDownstream ?? 0 };
|
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);
|
state.seconds = remainingHeight * surfaceArea / Math.abs(flowDiff);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.measurements
|
this.measurements.type("netFlowRate").variant("predicted").position("atEquipment").value(flowDiff).unit("m3/s");
|
||||||
.type("netFlowRate")
|
|
||||||
.variant("predicted")
|
|
||||||
.position("atEquipment")
|
|
||||||
.value(flowDiff)
|
|
||||||
.unit("m3/s");
|
|
||||||
|
|
||||||
this.logger.debug(
|
this.logger.debug(
|
||||||
`Flow-based net flow | diff=${flowDiff.toFixed(3)} m3/s, level=${level.toFixed(3)} m`
|
`Flow-based net flow | diff=${flowDiff.toFixed(3)} m3/s, level=${level.toFixed(3)} m`
|
||||||
@@ -288,7 +272,6 @@ class pumpingStation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
initBasinProperties() {
|
initBasinProperties() {
|
||||||
|
|
||||||
|
|
||||||
// Load and calc basic params
|
// Load and calc basic params
|
||||||
const volEmptyBasin = this.config.basin.volume;
|
const volEmptyBasin = this.config.basin.volume;
|
||||||
@@ -301,8 +284,8 @@ class pumpingStation {
|
|||||||
const surfaceArea = volEmptyBasin / heightBasin;
|
const surfaceArea = volEmptyBasin / heightBasin;
|
||||||
const maxVol = heightBasin * surfaceArea; // if Basin where to ever fill up completely this is the water volume
|
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 maxVolOverflow = heightOverflow * surfaceArea ; // Max water volume before you start loosing water to overflow
|
||||||
const minVol = heightInlet * surfaceArea;
|
const minVol = heightOutlet * surfaceArea;
|
||||||
const minVolOut = heightOutlet * surfaceArea ; // this will indicate if its an open end or a closed end.
|
const minVolOut = heightInlet * surfaceArea ; // this will indicate if its an open end or a closed end.
|
||||||
|
|
||||||
this.basin.volEmptyBasin = volEmptyBasin ;
|
this.basin.volEmptyBasin = volEmptyBasin ;
|
||||||
this.basin.heightBasin = heightBasin ;
|
this.basin.heightBasin = heightBasin ;
|
||||||
|
|||||||
Reference in New Issue
Block a user