updated pumpingstation

This commit is contained in:
znetsixe
2025-11-20 12:15:46 +01:00
parent 0a6c7ee2e1
commit 5a575a29fe
3 changed files with 47 additions and 63 deletions

View File

@@ -24,6 +24,7 @@
heightInlet: { value: 0.8 }, // m, centre of inlet pipe above floor heightInlet: { value: 0.8 }, // m, centre of inlet pipe above floor
heightOutlet: { value: 0.2 }, // m, centre of outlet pipe above floor heightOutlet: { value: 0.2 }, // m, centre of outlet pipe above floor
heightOverflow: { value: 0.9 }, // m, overflow elevation heightOverflow: { value: 0.9 }, // m, overflow elevation
minHeightBasedOn: { value: "outlet" }, // basis for minimum height check: inlet or outlet
// Advanced reference information // Advanced reference information
refHeight: { value: "NAP" }, // reference height refHeight: { value: "NAP" }, // reference height
@@ -86,6 +87,10 @@
refHeightEl.value = this.refHeight || "NAP"; refHeightEl.value = this.refHeight || "NAP";
} }
const minHeightBasedOnEl = document.getElementById("node-input-minHeightBasedOn");
if (minHeightBasedOnEl) {
minHeightBasedOnEl.value = this.minHeightBasedOn;
}
//------------------- END OF CUSTOM config UI ELEMENTS ------------------- // //------------------- END OF CUSTOM config UI ELEMENTS ------------------- //
}, },
@@ -98,6 +103,7 @@
//node specific //node specific
node.refHeight = document.getElementById("node-input-refHeight").value || "NAP"; node.refHeight = document.getElementById("node-input-refHeight").value || "NAP";
node.minHeightBasedOn = document.getElementById("node-input-minHeightBasedOn").value || "outlet";
node.simulator = document.getElementById("node-input-simulator").checked; node.simulator = document.getElementById("node-input-simulator").checked;
["basinVolume","basinHeight","heightInlet","heightOutlet","heightOverflow","basinBottomRef"] ["basinVolume","basinHeight","heightInlet","heightOutlet","heightOverflow","basinBottomRef"]
@@ -151,6 +157,13 @@
<hr> <hr>
<!-- Reference data --> <!-- Reference data -->
<div class="form-row">
<label for="node-input-minHeightBasedOn"><i class="fa fa-arrows-v"></i> Minimum Height Based On</label>
<select id="node-input-minHeightBasedOn" style="width:60%;">
<option value="inlet">Inlet Elevation</option>
<option value="outlet">Outlet Elevation</option>
</select>
</div>
<div class="form-row"> <div class="form-row">
<label for="node-input-refHeight"><i class="fa fa-map-marker"></i> Reference height</label> <label for="node-input-refHeight"><i class="fa fa-map-marker"></i> Reference height</label>
<select id="node-input-refHeight" style="width:60%;"> <select id="node-input-refHeight" style="width:60%;">

View File

@@ -63,6 +63,7 @@ class nodeClass {
}, },
hydraulics:{ hydraulics:{
refHeight: uiConfig.refHeight, refHeight: uiConfig.refHeight,
minHeightBasedOn: uiConfig.minHeightBasedOn,
basinBottomRef: uiConfig.basinBottomRef, basinBottomRef: uiConfig.basinBottomRef,
} }
}; };
@@ -191,13 +192,9 @@ class nodeClass {
this.node.on('input', (msg, send, done) => { this.node.on('input', (msg, send, done) => {
switch (msg.topic) { switch (msg.topic) {
//example //example
/*case 'simulator': case 'changemode':
this.source.toggleSimulation(); this.source.changeMode(msg.payload);
break; break;
default:
this.source.handleInput(msg);
break;
*/
case 'registerChild': case 'registerChild':
// Register this node as a child of the parent node // Register this node as a child of the parent node
const childId = msg.payload; const childId = msg.payload;

View File

@@ -106,7 +106,7 @@ class PumpingStation {
//get threshholds from config //get threshholds from config
const timeThreshhold = this.config.safety.timeleftToFullOrEmptyThresholdSeconds; //seconds const timeThreshhold = this.config.safety.timeleftToFullOrEmptyThresholdSeconds; //seconds
const triggerHighVol = this.basin.maxVolOverflow * ( this.config.safety.overfillThresholdPercent/100 ); const triggerHighVol = this.basin.maxVolOverflow * ( this.config.safety.overfillThresholdPercent/100 );
const triggerLowVol = this.basin.minVolOut * ( this.config.safety.dryRunThresholdPercent/100 ); const triggerLowVol = this.basin.minVol * ( this.config.safety.dryRunThresholdPercent/100 );
// trigger conditions for draining // trigger conditions for draining
if(direction == "draining"){ if(direction == "draining"){
@@ -165,10 +165,10 @@ class PumpingStation {
} }
changeMode(newMode){ changeMode(newMode){
if ( this.config.control.allowedModes.has(newMode) ){ if ( this.config.control.allowedModes.has(newMode) ){
const currentMode = this.mode;
this.logger.info(`Control mode changing from ${currentMode} to ${newMode}`);
this.mode = newMode; this.mode = newMode;
this.logger.info(`Control mode changed from ${currentMode} to ${newMode}`);
} }
else{ else{
this.logger.warn(`Attempted to change to unsupported control mode: ${newMode}`); this.logger.warn(`Attempted to change to unsupported control mode: ${newMode}`);
@@ -176,48 +176,6 @@ class PumpingStation {
} }
/* old levelcontrol
async _controlLevelBased(snapshot, remainingTime) {
// current volume as a percentage of usable capacity
const vol = this._resolveVolume(snapshot);
if (vol == null) {
this.logger.warn('No valid volume found for level-based control');
return;
}
const { thresholds, timeThresholdSeconds } = this.config.control.levelbased;
const percentFull = (vol / this.basin.maxVolOverflow) * 100;
// pick thresholds that are now crossed but were not crossed before
const newlyCrossed = thresholds.filter(t => percentFull >= t && !this._levelState.crossed.has(t));
this.logger.debug(`Level-based control: vol=${vol.toFixed(2)} m³ (${percentFull.toFixed(1)}%), newly crossed thresholds: [${newlyCrossed.join(', ')}]`);
if (!newlyCrossed.length) return;
const now = Date.now();
if (!this._levelState.dwellUntil) {
this._levelState.dwellUntil = now + timeThresholdSeconds * 1000;
this.logger.debug(`Level-based control: waiting ${timeThresholdSeconds}s before acting`);
return;
}
this.logger.debug(`Level-based control: dwelling for another ${Math.round((this._levelState.dwellUntil - now) / 1000)} seconds`);
if (now < this._levelState.dwellUntil) return; // still waiting
this._levelState.dwellUntil = null; // dwell satisfied, let pumps start
for (const threshold of newlyCrossed) {
const nextMachine = this._nextIdleMachine();
if (!nextMachine) break;
this._levelState.crossed.add(threshold);
this.logger.info(
`level-based control: threshold ${threshold}% reached, starting "${nextMachine.config.general.name}" (vol=${vol.toFixed(2)} m³)`
);
await nextMachine.handleInput('parent', 'execSequence', 'startup');
}
}
*/
async _controlLevelBased(snapshot, remainingTime) { async _controlLevelBased(snapshot, remainingTime) {
@@ -337,12 +295,19 @@ class PumpingStation {
case "flowbased": case "flowbased":
this._controlFlowBased(); this._controlFlowBased();
break; break;
case "manual":
this._manualControl();
break;
default: default:
this.logger.warn(`Unsupported control mode: ${mode}`); this.logger.warn(`Unsupported control mode: ${mode}`);
} }
} }
_manualControl() {
// Nothing to do - manual mode
}
//calibrate the predicted volume to a known value //calibrate the predicted volume to a known value
calibratePredictedVolume(calibratedVol, timestamp = Date.now()){ calibratePredictedVolume(calibratedVol, timestamp = Date.now()){
@@ -835,17 +800,22 @@ class PumpingStation {
initBasinProperties() { initBasinProperties() {
const volEmptyBasin = this.config.basin.volume; //is min height based on inlet or outlet elevation?
const heightBasin = this.config.basin.height; const minHeightBasedOn = this.config.hydraulics.minHeightBasedOn;
const heightInlet = this.config.basin.heightInlet;
const heightOutlet = this.config.basin.heightOutlet;
const heightOverflow = this.config.basin.heightOverflow;
const surfaceArea = volEmptyBasin / heightBasin; const volEmptyBasin = this.config.basin.volume; //volume when basin is empty
const maxVol = heightBasin * surfaceArea; const heightBasin = this.config.basin.height; //total height of basin
const maxVolOverflow = heightOverflow * surfaceArea; const heightInlet = this.config.basin.heightInlet; //height at which inlet is located
const minVol = heightOutlet * surfaceArea; const heightOutlet = this.config.basin.heightOutlet; //height at which outlet is located
const minVolOut = heightInlet * surfaceArea; const heightOverflow = this.config.basin.heightOverflow; //height at which overflow occurs
const surfaceArea = volEmptyBasin / heightBasin; //assume uniform cross section for now
const maxVol = heightBasin * surfaceArea; //maximum volume when basin is full
const maxVolOverflow = heightOverflow * surfaceArea; //maximum volume before overflow occurs
const minVolOut = heightOutlet * surfaceArea; //minimum volume to have outlet just above basin bottom
const minVolIn = heightInlet * surfaceArea; //minimum volume to have inlet just above waterline
const minVol = (minHeightBasedOn === "inlet") ? minVolIn : minVolOut;
this.logger.debug(`Basin min volume based on ${minHeightBasedOn} : ${minVol.toFixed(2)} m3`);
this.basin = { this.basin = {
volEmptyBasin, volEmptyBasin,
@@ -856,11 +826,13 @@ class PumpingStation {
surfaceArea, surfaceArea,
maxVol, maxVol,
maxVolOverflow, maxVolOverflow,
minVolIn,
minVolOut,
minVol, minVol,
minVolOut minHeightBasedOn
}; };
this.measurements.type('volume').variant('predicted').position('atEquipment').value(heightOutlet).unit('m3'); this.measurements.type('volume').variant('predicted').position('atEquipment').value(minVol).unit('m3');
this.logger.debug( this.logger.debug(
`Basin initialized | area=${surfaceArea.toFixed(2)} m2, max=${maxVol.toFixed(2)} m3, overflow=${maxVolOverflow.toFixed(2)} m3` `Basin initialized | area=${surfaceArea.toFixed(2)} m2, max=${maxVol.toFixed(2)} m3, overflow=${maxVolOverflow.toFixed(2)} m3`
@@ -899,6 +871,8 @@ class PumpingStation {
output.minVol = this.basin.minVol; output.minVol = this.basin.minVol;
output.maxVolOverflow = this.basin.maxVolOverflow; output.maxVolOverflow = this.basin.maxVolOverflow;
output.minVolOut = this.basin.minVolOut; output.minVolOut = this.basin.minVolOut;
output.minVolIn = this.basin.minVolIn;
output.minHeightBasedOn = this.basin.minHeightBasedOn;
return output; return output;
} }
} }