added safeguarding when vol gets too low for machines,
This commit is contained in:
@@ -204,6 +204,14 @@ class nodeClass {
|
||||
const childObj = this.RED.nodes.getNode(childId);
|
||||
this.source.childRegistrationUtils.registerChild(childObj.source ,msg.positionVsParent);
|
||||
break;
|
||||
case 'calibratePredictedVolume':
|
||||
const calibratedVolume = this.source.measurements
|
||||
.type('volume')
|
||||
.variant('measured')
|
||||
.position('atequipment')
|
||||
.getCurrentValue('m3');
|
||||
this.source.calibratePredictedVolume(calibratedVolume);
|
||||
break;
|
||||
}
|
||||
done();
|
||||
});
|
||||
|
||||
@@ -18,7 +18,7 @@ class PumpingStation {
|
||||
this.childRegistrationUtils = new childRegistrationUtils(this);
|
||||
this.machines = {};
|
||||
this.stations = {};
|
||||
|
||||
this.machineGroups = {};
|
||||
|
||||
//variants in determining what gets priority
|
||||
this.flowVariants = ['measured', 'predicted'];
|
||||
@@ -51,15 +51,36 @@ class PumpingStation {
|
||||
return;
|
||||
}
|
||||
|
||||
//for machines register them for control
|
||||
if(softwareType === 'machine'){
|
||||
const childId = child.config.general.id;
|
||||
this.machines[childId] = child;
|
||||
this.logger.debug(`Registered machine child "${child.config.general.name}" with id "${childId}"`);
|
||||
}
|
||||
|
||||
// for pumping stations register them for control
|
||||
if(softwareType === 'pumpingStation'){
|
||||
const childId = child.config.general.id;
|
||||
this.stations[childId] = child;
|
||||
this.logger.debug(`Registered pumping station child "${child.config.general.name}" with id "${childId}"`);
|
||||
}
|
||||
|
||||
// for machine group controllers register them for control
|
||||
if(softwareType === 'machineGroupController'){
|
||||
const childId = child.config.general.id;
|
||||
this.machineGroups[childId] = child;
|
||||
this.logger.debug(`Registered machine group controller child "${child.config.general.name}" with id "${childId}"`);
|
||||
}
|
||||
|
||||
//for all childs that can provide predicted flow data
|
||||
if (softwareType === 'machine' || softwareType === 'pumpingStation' || softwareType === 'machineGroupController') {
|
||||
this._registerPredictedFlowChild(child);
|
||||
return;
|
||||
}
|
||||
|
||||
this.logger.warn(`Unsupported child software type: ${softwareType}`);
|
||||
}
|
||||
|
||||
_safeGuardSystem(snapshot,remainingTime){
|
||||
_safeGuardSystem(snapshot,remainingTime,direction){
|
||||
let vol = null;
|
||||
|
||||
for (const variant of this.volVariants){
|
||||
@@ -67,17 +88,89 @@ class PumpingStation {
|
||||
//go through with variants until we find one that exists
|
||||
if (!volsnap.samples.exists){ continue};
|
||||
|
||||
const vol = volsnap.samples.current?.value ?? null;
|
||||
vol = volsnap.samples.current?.value ?? null;
|
||||
}
|
||||
|
||||
if(vol == null){
|
||||
//if we cant get a volume, we must force whole system off.
|
||||
//if we cant get a volume we cant control blind turn all pumps off.
|
||||
Object.entries(this.machines).forEach(([machineId, machine]) => {
|
||||
machine.handleInput('parent', 'execSequence', 'shutdown');
|
||||
});
|
||||
this.logger.warn('No volume data available to safe guard system; shutting down all machines.');
|
||||
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);
|
||||
|
||||
// trigger conditions for draining
|
||||
if(direction == "draining"){
|
||||
this.logger.debug(
|
||||
`Safe-guard (draining): vol=${vol != null ? vol.toFixed(2) + ' m3' : 'N/A'}; ` +
|
||||
`remainingTime=${Number.isFinite(remainingTime) ? remainingTime.toFixed(1) + ' s' : 'N/A'}; ` +
|
||||
`direction=${String(direction)}; triggerLowVol=${Number.isFinite(triggerLowVol) ? triggerLowVol.toFixed(2) + ' m3' : 'N/A'}`
|
||||
);
|
||||
|
||||
if( (remainingTime < timeThreshhold && remainingTime !== null) || vol < triggerLowVol || vol == null){
|
||||
//shut down all downstream or atequipment machines,pumping stations and machine groups
|
||||
Object.entries(this.machines).forEach(([machineId, machine]) => {
|
||||
machine.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 "${machineId}"`);
|
||||
});
|
||||
Object.entries(this.stations).forEach(([stationId, station]) => {
|
||||
station.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 station "${stationId}"`);
|
||||
});
|
||||
Object.entries(this.machineGroups).forEach(([groupId, group]) => {
|
||||
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}"`);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// trigger conditions for filling
|
||||
if(direction == "filling"){
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
//calibrate the predicted volume to a known value
|
||||
calibratePredictedVolume(calibratedVol, timestamp = Date.now()){
|
||||
|
||||
const volumeChain = this.measurements
|
||||
.type('volume')
|
||||
.variant('predicted')
|
||||
.position('atequipment');
|
||||
|
||||
//if we have existing values clear them out
|
||||
const volumeMeasurement = volumeChain.exists() ? volumeChain.get() : null;
|
||||
if (volumeMeasurement) {
|
||||
volumeMeasurement.values = [];
|
||||
volumeMeasurement.timestamps = [];
|
||||
}
|
||||
|
||||
volumeChain.value(calibratedVol, timestamp, 'm3').unit('m3');
|
||||
|
||||
const levelChain = this.measurements
|
||||
.type('level')
|
||||
.variant('predicted')
|
||||
.position('atequipment');
|
||||
|
||||
const levelMeasurement = levelChain.exists() ? levelChain.get() : null;
|
||||
if (levelMeasurement) {
|
||||
levelMeasurement.values = [];
|
||||
levelMeasurement.timestamps = [];
|
||||
}
|
||||
|
||||
levelChain.value(this._calcLevelFromVolume(calibratedVol), timestamp, 'm');
|
||||
|
||||
this._predictedFlowState = {
|
||||
inflow: 0,
|
||||
outflow: 0,
|
||||
lastTimestamp: timestamp
|
||||
};
|
||||
/*
|
||||
if(remainingTime < timeThreshhold || vol > maxVolume || vol < minVolume){}
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
tick() {
|
||||
@@ -89,7 +182,7 @@ class PumpingStation {
|
||||
const netFlow = this._selectBestNetFlow(snapshot);
|
||||
const remaining = this._computeRemainingTime(snapshot, netFlow);
|
||||
|
||||
this._safeGuardSystem(snapshot,remaining.seconds);
|
||||
this._safeGuardSystem(snapshot,remaining.seconds,netFlow.direction);
|
||||
|
||||
this.state = {
|
||||
direction: netFlow.direction,
|
||||
@@ -151,9 +244,7 @@ class PumpingStation {
|
||||
const timestamp = eventData.timestamp ?? Date.now();
|
||||
const unit = eventData.unit ?? 'm3/s';
|
||||
|
||||
this.logger.debug(
|
||||
`Predicted flow update from ${childName} (${childId}, ${posKey}) -> ${value} ${unit}`
|
||||
);
|
||||
this.logger.debug(`Predicted flow update from ${childName} (${childId}, ${posKey}) -> ${value} ${unit}`);
|
||||
|
||||
this.predictedFlowChildren.get(childId)[posKey] = value;
|
||||
this._refreshAggregatedPredictedFlow(posKey, timestamp, unit);
|
||||
@@ -201,6 +292,7 @@ class PumpingStation {
|
||||
_onLevelMeasurement(position, value, context = {}) {
|
||||
const levelSeries = this.measurements.type('level').variant('measured').position(position);
|
||||
const levelMeters = levelSeries.getCurrentValue('m');
|
||||
|
||||
if (levelMeters == null) return;
|
||||
|
||||
const volume = this._calcVolumeFromLevel(levelMeters);
|
||||
@@ -598,7 +690,7 @@ module.exports = PumpingStation;
|
||||
/* ------------------------------------------------------------------------- */
|
||||
/* Example usage */
|
||||
/* ------------------------------------------------------------------------- */
|
||||
/*
|
||||
|
||||
if (require.main === module) {
|
||||
const Measurement = require('../../measurement/src/specificClass');
|
||||
const RotatingMachine = require('../../rotatingMachine/src/specificClass');
|
||||
|
||||
Reference in New Issue
Block a user