forked from RnD/pumpingStation
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);
|
const childObj = this.RED.nodes.getNode(childId);
|
||||||
this.source.childRegistrationUtils.registerChild(childObj.source ,msg.positionVsParent);
|
this.source.childRegistrationUtils.registerChild(childObj.source ,msg.positionVsParent);
|
||||||
break;
|
break;
|
||||||
|
case 'calibratePredictedVolume':
|
||||||
|
const calibratedVolume = this.source.measurements
|
||||||
|
.type('volume')
|
||||||
|
.variant('measured')
|
||||||
|
.position('atequipment')
|
||||||
|
.getCurrentValue('m3');
|
||||||
|
this.source.calibratePredictedVolume(calibratedVolume);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -16,9 +16,9 @@ class PumpingStation {
|
|||||||
this.measurements.setPreferredUnit('level', 'm');
|
this.measurements.setPreferredUnit('level', 'm');
|
||||||
this.measurements.setPreferredUnit('volume', 'm3');
|
this.measurements.setPreferredUnit('volume', 'm3');
|
||||||
this.childRegistrationUtils = new childRegistrationUtils(this);
|
this.childRegistrationUtils = new childRegistrationUtils(this);
|
||||||
this.machines = {};
|
this.machines = {};
|
||||||
this.stations = {};
|
this.stations = {};
|
||||||
|
this.machineGroups = {};
|
||||||
|
|
||||||
//variants in determining what gets priority
|
//variants in determining what gets priority
|
||||||
this.flowVariants = ['measured', 'predicted'];
|
this.flowVariants = ['measured', 'predicted'];
|
||||||
@@ -51,15 +51,36 @@ class PumpingStation {
|
|||||||
return;
|
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') {
|
if (softwareType === 'machine' || softwareType === 'pumpingStation' || softwareType === 'machineGroupController') {
|
||||||
this._registerPredictedFlowChild(child);
|
this._registerPredictedFlowChild(child);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.logger.warn(`Unsupported child software type: ${softwareType}`);
|
this.logger.warn(`Unsupported child software type: ${softwareType}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
_safeGuardSystem(snapshot,remainingTime){
|
_safeGuardSystem(snapshot,remainingTime,direction){
|
||||||
let vol = null;
|
let vol = null;
|
||||||
|
|
||||||
for (const variant of this.volVariants){
|
for (const variant of this.volVariants){
|
||||||
@@ -67,17 +88,89 @@ class PumpingStation {
|
|||||||
//go through with variants until we find one that exists
|
//go through with variants until we find one that exists
|
||||||
if (!volsnap.samples.exists){ continue};
|
if (!volsnap.samples.exists){ continue};
|
||||||
|
|
||||||
const vol = volsnap.samples.current?.value ?? null;
|
vol = volsnap.samples.current?.value ?? null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(vol == 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');
|
||||||
/*
|
});
|
||||||
if(remainingTime < timeThreshhold || vol > maxVolume || vol < minVolume){}
|
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
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
tick() {
|
tick() {
|
||||||
@@ -89,7 +182,7 @@ class PumpingStation {
|
|||||||
const netFlow = this._selectBestNetFlow(snapshot);
|
const netFlow = this._selectBestNetFlow(snapshot);
|
||||||
const remaining = this._computeRemainingTime(snapshot, netFlow);
|
const remaining = this._computeRemainingTime(snapshot, netFlow);
|
||||||
|
|
||||||
this._safeGuardSystem(snapshot,remaining.seconds);
|
this._safeGuardSystem(snapshot,remaining.seconds,netFlow.direction);
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
direction: netFlow.direction,
|
direction: netFlow.direction,
|
||||||
@@ -151,9 +244,7 @@ class PumpingStation {
|
|||||||
const timestamp = eventData.timestamp ?? Date.now();
|
const timestamp = eventData.timestamp ?? Date.now();
|
||||||
const unit = eventData.unit ?? 'm3/s';
|
const unit = eventData.unit ?? 'm3/s';
|
||||||
|
|
||||||
this.logger.debug(
|
this.logger.debug(`Predicted flow update from ${childName} (${childId}, ${posKey}) -> ${value} ${unit}`);
|
||||||
`Predicted flow update from ${childName} (${childId}, ${posKey}) -> ${value} ${unit}`
|
|
||||||
);
|
|
||||||
|
|
||||||
this.predictedFlowChildren.get(childId)[posKey] = value;
|
this.predictedFlowChildren.get(childId)[posKey] = value;
|
||||||
this._refreshAggregatedPredictedFlow(posKey, timestamp, unit);
|
this._refreshAggregatedPredictedFlow(posKey, timestamp, unit);
|
||||||
@@ -201,6 +292,7 @@ class PumpingStation {
|
|||||||
_onLevelMeasurement(position, value, context = {}) {
|
_onLevelMeasurement(position, value, context = {}) {
|
||||||
const levelSeries = this.measurements.type('level').variant('measured').position(position);
|
const levelSeries = this.measurements.type('level').variant('measured').position(position);
|
||||||
const levelMeters = levelSeries.getCurrentValue('m');
|
const levelMeters = levelSeries.getCurrentValue('m');
|
||||||
|
|
||||||
if (levelMeters == null) return;
|
if (levelMeters == null) return;
|
||||||
|
|
||||||
const volume = this._calcVolumeFromLevel(levelMeters);
|
const volume = this._calcVolumeFromLevel(levelMeters);
|
||||||
@@ -598,7 +690,7 @@ module.exports = PumpingStation;
|
|||||||
/* ------------------------------------------------------------------------- */
|
/* ------------------------------------------------------------------------- */
|
||||||
/* Example usage */
|
/* Example usage */
|
||||||
/* ------------------------------------------------------------------------- */
|
/* ------------------------------------------------------------------------- */
|
||||||
/*
|
|
||||||
if (require.main === module) {
|
if (require.main === module) {
|
||||||
const Measurement = require('../../measurement/src/specificClass');
|
const Measurement = require('../../measurement/src/specificClass');
|
||||||
const RotatingMachine = require('../../rotatingMachine/src/specificClass');
|
const RotatingMachine = require('../../rotatingMachine/src/specificClass');
|
||||||
|
|||||||
Reference in New Issue
Block a user