Added simpel case for level control
This commit is contained in:
@@ -20,6 +20,10 @@ class PumpingStation {
|
||||
this.stations = {};
|
||||
this.machineGroups = {};
|
||||
|
||||
//fetch control mode from config by default
|
||||
this.mode = this.config.control.mode;
|
||||
this._levelState = { crossed: new Set(), dwellUntil: null };
|
||||
|
||||
//variants in determining what gets priority
|
||||
this.flowVariants = ['measured', 'predicted'];
|
||||
this.levelVariants = ['measured', 'predicted'];
|
||||
@@ -80,16 +84,11 @@ class PumpingStation {
|
||||
this.logger.warn(`Unsupported child software type: ${softwareType}`);
|
||||
}
|
||||
|
||||
_safeGuardSystem(snapshot,remainingTime,direction){
|
||||
let vol = null;
|
||||
_safetyController(snapshot,remainingTime,direction){
|
||||
|
||||
for (const variant of this.volVariants){
|
||||
const volsnap = snapshot.vols[variant];
|
||||
//go through with variants until we find one that exists
|
||||
if (!volsnap.samples.exists){ continue};
|
||||
this.safetyControllerActive = false;
|
||||
|
||||
vol = volsnap.samples.current?.value ?? null;
|
||||
}
|
||||
const vol = this._resolveVolume(snapshot);
|
||||
|
||||
if(vol == null){
|
||||
//if we cant get a volume we cant control blind turn all pumps off.
|
||||
@@ -97,13 +96,14 @@ class PumpingStation {
|
||||
machine.handleInput('parent', 'execSequence', 'shutdown');
|
||||
});
|
||||
this.logger.warn('No volume data available to safe guard system; shutting down all machines.');
|
||||
this.safetyControllerActive = true;
|
||||
return;
|
||||
}
|
||||
|
||||
//get threshholds from config
|
||||
const timeThreshhold = this.config.control.timeThreshholdSeconds; //seconds
|
||||
const triggerHighVol = this.basin.maxVolOverflow * ( this.config.control.thresholdHighVolume/100 );
|
||||
const triggerLowVol = this.basin.minVolOut * ( this.config.control.thresholdLowVolume/100);
|
||||
const timeThreshhold = this.config.safety.timeleftToFullOrEmptyThresholdSeconds; //seconds
|
||||
const triggerHighVol = this.basin.maxVolOverflow * ( this.config.safety.overfillThresholdPercent/100 );
|
||||
const triggerLowVol = this.basin.minVolOut * ( this.config.safety.dryRunThresholdPercent/100 );
|
||||
|
||||
// trigger conditions for draining
|
||||
if(direction == "draining"){
|
||||
@@ -127,13 +127,90 @@ class PumpingStation {
|
||||
group.handleInput('parent', 'execSequence', 'shutdown');
|
||||
this.logger.warn(`Safe guard triggered: vol=${vol.toFixed(2)} m3, remainingTime=${remainingTime ? remainingTime.toFixed(1) : 'N/A'} s; shutting down machine group "${groupId}"`);
|
||||
});
|
||||
this.safetyControllerActive = true;
|
||||
}
|
||||
}
|
||||
|
||||
// trigger conditions for filling
|
||||
if(direction == "filling"){
|
||||
}
|
||||
|
||||
changeMode(newMode){
|
||||
|
||||
if ( this.config.control.allowedModes.has(newMode) ){
|
||||
this.mode = newMode;
|
||||
this.logger.info(`Control mode changed from ${currentMode} to ${newMode}`);
|
||||
}
|
||||
else{
|
||||
this.logger.warn(`Attempted to change to unsupported control mode: ${newMode}`);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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');
|
||||
}
|
||||
}
|
||||
|
||||
_resolveVolume(snapshot) {
|
||||
for (const variant of this.volVariants) {
|
||||
const volsnap = snapshot.vols[variant];
|
||||
if (volsnap?.samples?.exists) return volsnap.samples.current?.value ?? null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
_nextIdleMachine() {
|
||||
return Object.values(this.machines).find(m => !m._isOperationalState());
|
||||
}
|
||||
|
||||
//control logic
|
||||
_controlLogic(snapshot, remainingTime){
|
||||
const mode = this.mode;
|
||||
|
||||
switch(mode){
|
||||
case "levelbased":
|
||||
this.logger.debug(`Executing level-based control logic`);
|
||||
this._controlLevelBased(snapshot, remainingTime);
|
||||
break;
|
||||
case "flowbased":
|
||||
this._controlFlowBased();
|
||||
break;
|
||||
default:
|
||||
this.logger.warn(`Unsupported control mode: ${mode}`);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//calibrate the predicted volume to a known value
|
||||
@@ -178,11 +255,15 @@ class PumpingStation {
|
||||
|
||||
this._updatePredictedVolume(snapshot);
|
||||
|
||||
|
||||
const netFlow = this._selectBestNetFlow(snapshot);
|
||||
const remaining = this._computeRemainingTime(snapshot, netFlow);
|
||||
|
||||
this._safeGuardSystem(snapshot,remaining.seconds,netFlow.direction);
|
||||
//check safety conditions
|
||||
this._safetyController(snapshot,remaining.seconds,netFlow.direction);
|
||||
if(this.safetyControllerActive) return;
|
||||
|
||||
//if safety not active proceed with normal control
|
||||
this._controlLogic(snapshot,remaining.seconds);
|
||||
|
||||
this.state = {
|
||||
direction: netFlow.direction,
|
||||
|
||||
Reference in New Issue
Block a user