updated pumpingstation
This commit is contained in:
@@ -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%;">
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user