forked from RnD/pumpingStation
updated retrieval mechanism
This commit is contained in:
@@ -178,8 +178,9 @@ class nodeClass {
|
|||||||
* Execute a single tick: update measurement, format and send outputs.
|
* Execute a single tick: update measurement, format and send outputs.
|
||||||
*/
|
*/
|
||||||
_tick() {
|
_tick() {
|
||||||
//this.source.tick();
|
|
||||||
|
|
||||||
|
//pumping station needs time based ticks to recalc level when predicted
|
||||||
|
this.source.tick();
|
||||||
const raw = this.source.getOutput();
|
const raw = this.source.getOutput();
|
||||||
const processMsg = this._output.formatMsg(raw, this.config, 'process');
|
const processMsg = this._output.formatMsg(raw, this.config, 'process');
|
||||||
const influxMsg = this._output.formatMsg(raw, this.config, 'influxdb');
|
const influxMsg = this._output.formatMsg(raw, this.config, 'influxdb');
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ class pumpingStation {
|
|||||||
|
|
||||||
// Initialize basin-specific properties and calculate used parameters
|
// Initialize basin-specific properties and calculate used parameters
|
||||||
this.initBasinProperties();
|
this.initBasinProperties();
|
||||||
|
this.parent = {}; // object to hold parent information for when we follow flow directions.
|
||||||
this.child = {}; // object to hold child information so we know on what to subscribe
|
this.child = {}; // object to hold child information so we know on what to subscribe
|
||||||
this.machines = {}; // object to hold child machine information
|
this.machines = {}; // object to hold child machine information
|
||||||
this.childRegistrationUtils = new childRegistrationUtils(this); // Child registration utility
|
this.childRegistrationUtils = new childRegistrationUtils(this); // Child registration utility
|
||||||
@@ -66,40 +66,88 @@ class pumpingStation {
|
|||||||
this.machines[child.config.general.id] === undefined ? this.machines[child.config.general.id] = child : this.logger.warn(`Machine ${child.config.general.id} is already registered.`);
|
this.machines[child.config.general.id] === undefined ? this.machines[child.config.general.id] = child : this.logger.warn(`Machine ${child.config.general.id} is already registered.`);
|
||||||
|
|
||||||
//listen for machine pressure changes
|
//listen for machine pressure changes
|
||||||
this.logger.debug(`Listening for pressure changes from machine ${child.config.general.id}`);
|
this.logger.debug(`Listening for flow changes from machine ${child.config.general.id}`);
|
||||||
|
|
||||||
//for now lets focus on handling downstream predicted flow
|
//for now lets focus on handling downstream predicted flow
|
||||||
child.measurements.emitter.on("flow.predicted.downstream", (eventData) => {
|
child.measurements.emitter.on("flow.predicted.downstream", (eventData) => {
|
||||||
this.logger.debug(`Flow prediction update from ${child.config.general.id}: ${eventData.value} ${eventData.unit}`);
|
this.logger.debug(`Flow prediction update from ${child.config.general.id}: ${eventData.value} ${eventData.unit}`);
|
||||||
this.updateMachineFlowPrediction();
|
this.measurements.type('flow').variant('predicted').position('atEquipment').value(eventData.value,eventData.timestamp,eventData.unit);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
// add one for group later
|
||||||
|
if( softwareType == "machineGroup" ){
|
||||||
//how to handle when there are machines connected and there is an updated predicted flow variable
|
|
||||||
updateMachineFlowPrediction(){
|
|
||||||
|
|
||||||
//check if container exists
|
|
||||||
const hasMeasuredFlow = measurements.type("flow").variant("measured").exists();
|
|
||||||
|
|
||||||
//if there is no down / upstream flow being measured we can take the machines flow to calculate the flow and update predicted level
|
|
||||||
if( ! hasMeasuredFlow ) {
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//update prediction in outgoing downstream flow
|
||||||
|
_updateDownstreamFlowPrediction(){
|
||||||
|
|
||||||
|
//get downflow
|
||||||
|
const downFlowExists = this.measurements.type("flow").variant("predicted").position("atEquipment").exists();
|
||||||
|
if(!downFlowExists){return};
|
||||||
|
|
||||||
|
const downFlow = this.measurements.type("flow").variant("predicted").position("atEquipment");
|
||||||
|
const currDownFlow = downFlow.getLaggedValue(0, "m3/s"); // { value, timestamp, unit }
|
||||||
|
const prevDownFlow = downFlow.getLaggedValue(1, "m3/s"); // { value, timestamp, unit }
|
||||||
|
|
||||||
|
if (!currDownFlow || !prevDownFlow) return;
|
||||||
|
|
||||||
|
this.logger.debug(`currDownflow = ${currDownFlow.value} , prevDownFlow = ${prevDownFlow.value}`);
|
||||||
|
|
||||||
|
// calc difference in time
|
||||||
|
const deltaT = currDownFlow.timestamp - prevDownFlow.timestamp;
|
||||||
|
const deltaSeconds = deltaT / 1000;
|
||||||
|
|
||||||
|
if (deltaSeconds <= 0) {
|
||||||
|
this.logger.warn(`Flow integration aborted; invalid Δt=${deltaSeconds}s.`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const avgFlow = (currDownFlow.value + prevDownFlow.value) / 2;
|
||||||
|
const volumeSubstracted = avgFlow * deltaSeconds;
|
||||||
|
|
||||||
|
//substract seeing as this is downstream and is being pulled away from the pumpingstaion and keep track of status
|
||||||
|
const currVolume = this.measurements.type('volume').variant('predicted').position('atEquipment').getCurrentValue('m3');
|
||||||
|
const newVol = currVolume - volumeSubstracted;
|
||||||
|
|
||||||
|
this.measurements.type('volume').variant('predicted').position('atEquipment').value(newVol).unit('m3');
|
||||||
|
//convert to a predicted level
|
||||||
|
const newLevel = this._calcLevelFromVolume(newVol);
|
||||||
|
|
||||||
|
this.measurements.type('level').variant('predicted').position('atEquipment').value(newLevel).unit('m');
|
||||||
|
|
||||||
|
this.logger.debug(`new predicted volume : ${newVol} new predicted level: ${newLevel} `);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//update prediction in incomming upstream flow
|
||||||
|
_updateUpstreamFlowPrediction(){
|
||||||
|
|
||||||
|
}
|
||||||
|
//trigger shutdown when level is too low and trigger no start flag for childs ?
|
||||||
|
safetyVolCheck(){
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//update measured temperature to adjust density of liquid
|
||||||
updateMeasuredTemperature(){
|
updateMeasuredTemperature(){
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//update measured flow and recalc
|
||||||
updateMeasuredFlow(){
|
updateMeasuredFlow(){
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//keep updating the volume / level when the flow is still active from a machine or machinegroup or incoming from another source
|
||||||
|
tick(){
|
||||||
|
//go through all the functions that require time based checks or updates
|
||||||
|
this._updateDownstreamFlowPrediction();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
_callMeasurementHandler(measurementType, value, position, context) {
|
_callMeasurementHandler(measurementType, value, position, context) {
|
||||||
@@ -204,7 +252,7 @@ class pumpingStation {
|
|||||||
|
|
||||||
if (flowBased && levelBased) {
|
if (flowBased && levelBased) {
|
||||||
this.logger.debug(
|
this.logger.debug(
|
||||||
`Flow vs Level comparison | flow=${flowBased.netFlowRate.toFixed(3)} ` +
|
`Flow vs Level comparison | flow=${flowBased.netFlowRate.value.toFixed(3)} ` +
|
||||||
`m3/s, level=${levelBased.netFlowRate.toFixed(3)} m3/s`
|
`m3/s, level=${levelBased.netFlowRate.toFixed(3)} m3/s`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -249,7 +297,7 @@ class pumpingStation {
|
|||||||
this.measurements.type("netFlowRate").variant("predicted").position("atEquipment").value(flowDiff).unit("m3/s");
|
this.measurements.type("netFlowRate").variant("predicted").position("atEquipment").value(flowDiff).unit("m3/s");
|
||||||
|
|
||||||
this.logger.debug(
|
this.logger.debug(
|
||||||
`Flow-based net flow | diff=${flowDiff.toFixed(3)} m3/s, level=${level.toFixed(3)} m`
|
`Flow-based net flow | diff=${flowDiff.value.toFixed(3)} m3/s, level=${level.toFixed(3)} m`
|
||||||
);
|
);
|
||||||
|
|
||||||
return { source: "flow", netFlowRate: flowDiff, state };
|
return { source: "flow", netFlowRate: flowDiff, state };
|
||||||
@@ -344,6 +392,11 @@ _calcVolumeFromLevel(level) {
|
|||||||
return Math.max(level, 0) * surfaceArea;
|
return Math.max(level, 0) * surfaceArea;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_calcLevelFromVolume(vol){
|
||||||
|
const surfaceArea = this.basin.surfaceArea;
|
||||||
|
return Math.max(vol, 0) / surfaceArea;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
getOutput() {
|
getOutput() {
|
||||||
return {
|
return {
|
||||||
@@ -355,10 +408,192 @@ _calcVolumeFromLevel(level) {
|
|||||||
|
|
||||||
module.exports = pumpingStation;
|
module.exports = pumpingStation;
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------------- */
|
||||||
|
/* Example: pumping station + rotating machine + measurements (stand-alone) */
|
||||||
|
/* ------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
const PumpingStation = require("./specificClass");
|
||||||
|
const RotatingMachine = require("../../rotatingMachine/src/specificClass");
|
||||||
|
const Measurement = require("../../measurement/src/specificClass");
|
||||||
|
|
||||||
|
/** Helpers ******************************************************************/
|
||||||
|
function createPumpingStationConfig(name) {
|
||||||
|
return {
|
||||||
|
general: {
|
||||||
|
logging: { enabled: true, logLevel: "debug" },
|
||||||
|
name,
|
||||||
|
id: `${name}-${Date.now()}`,
|
||||||
|
unit: "m3/h"
|
||||||
|
},
|
||||||
|
functionality: {
|
||||||
|
softwareType: "pumpingStation",
|
||||||
|
role: "stationcontroller"
|
||||||
|
},
|
||||||
|
basin: {
|
||||||
|
volume: 43.75,
|
||||||
|
height: 3.5,
|
||||||
|
heightInlet: 0.3,
|
||||||
|
heightOutlet: 0.2,
|
||||||
|
heightOverflow: 3.0
|
||||||
|
},
|
||||||
|
hydraulics: {
|
||||||
|
refHeight: "NAP",
|
||||||
|
basinBottomRef: 0
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function createLevelMeasurementConfig(name) {
|
||||||
|
return {
|
||||||
|
general: {
|
||||||
|
logging: { enabled: true, logLevel: "debug" },
|
||||||
|
name,
|
||||||
|
id: `${name}-${Date.now()}`,
|
||||||
|
unit: "m"
|
||||||
|
},
|
||||||
|
functionality: {
|
||||||
|
softwareType: "measurement",
|
||||||
|
role: "sensor",
|
||||||
|
positionVsParent: "atEquipment"
|
||||||
|
},
|
||||||
|
asset: {
|
||||||
|
category: "sensor",
|
||||||
|
type: "level",
|
||||||
|
model: "demo-level",
|
||||||
|
supplier: "demoCo",
|
||||||
|
unit: "m"
|
||||||
|
},
|
||||||
|
scaling: { enabled: false },
|
||||||
|
smoothing: { smoothWindow: 5, smoothMethod: "none" }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function createFlowMeasurementConfig(name, position) {
|
||||||
|
return {
|
||||||
|
general: {
|
||||||
|
logging: { enabled: true, logLevel: "debug" },
|
||||||
|
name,
|
||||||
|
id: `${name}-${Date.now()}`,
|
||||||
|
unit: "m3/s"
|
||||||
|
},
|
||||||
|
functionality: {
|
||||||
|
softwareType: "measurement",
|
||||||
|
role: "sensor",
|
||||||
|
positionVsParent: position
|
||||||
|
},
|
||||||
|
asset: {
|
||||||
|
category: "sensor",
|
||||||
|
type: "flow",
|
||||||
|
model: "demo-flow",
|
||||||
|
supplier: "demoCo",
|
||||||
|
unit: "m3/s"
|
||||||
|
},
|
||||||
|
scaling: { enabled: false },
|
||||||
|
smoothing: { smoothWindow: 5, smoothMethod: "none" }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function createMachineConfig(name) {
|
||||||
|
|
||||||
|
curve = require('C:/Users/zn375/.node-red/public/fallbackData.json');
|
||||||
|
return {
|
||||||
|
|
||||||
|
general: {
|
||||||
|
name: name,
|
||||||
|
logging: {
|
||||||
|
enabled: true,
|
||||||
|
logLevel: "warn",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
asset: {
|
||||||
|
supplier: "Hydrostal",
|
||||||
|
type: "pump",
|
||||||
|
category: "centrifugal",
|
||||||
|
model: "hidrostal-H05K-S03R", // Ensure this field is present.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function createMachineStateConfig() {
|
||||||
|
return {
|
||||||
|
general: {
|
||||||
|
logging: {
|
||||||
|
enabled: true,
|
||||||
|
logLevel: "debug",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// Your custom config here (or leave empty for defaults)
|
||||||
|
movement: {
|
||||||
|
speed: 1,
|
||||||
|
},
|
||||||
|
time: {
|
||||||
|
starting: 2,
|
||||||
|
warmingup: 3,
|
||||||
|
stopping: 2,
|
||||||
|
coolingdown: 3,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// convenience for seeding measurements
|
||||||
|
function pushSample(measurement, type, value, unit) {
|
||||||
|
const pos = measurement.config.functionality.positionVsParent;
|
||||||
|
measurement.measurements
|
||||||
|
.type(type)
|
||||||
|
.variant("measured")
|
||||||
|
.position(pos)
|
||||||
|
.value(value, Date.now(), unit);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Demo *********************************************************************/
|
||||||
|
(async function demoStationWithPump() {
|
||||||
|
const station = new PumpingStation(createPumpingStationConfig("PumpingStationDemo"));
|
||||||
|
const pump = new RotatingMachine(createMachineConfig("Pump1"), createMachineStateConfig());
|
||||||
|
|
||||||
|
const levelSensor = new Measurement(createLevelMeasurementConfig("WetWellLevel"));
|
||||||
|
const upstreamFlow = new Measurement(createFlowMeasurementConfig("InfluentFlow", "upstream"));
|
||||||
|
const downstreamFlow = new Measurement(createFlowMeasurementConfig("PumpDischargeFlow", "downstream"));
|
||||||
|
|
||||||
|
|
||||||
|
// station uses the sensors
|
||||||
|
/*
|
||||||
|
station.childRegistrationUtils.registerChild(levelSensor, levelSensor.config.functionality.softwareType);
|
||||||
|
station.childRegistrationUtils.registerChild(upstreamFlow, upstreamFlow.config.functionality.softwareType);
|
||||||
|
station.childRegistrationUtils.registerChild(downstreamFlow, downstreamFlow.config.functionality.softwareType);
|
||||||
|
*/
|
||||||
|
|
||||||
|
// pump owns the downstream flow sensor
|
||||||
|
pump.childRegistrationUtils.registerChild(downstreamFlow, downstreamFlow.config.functionality.positionVsParent);
|
||||||
|
station.childRegistrationUtils.registerChild(pump,"downstream");
|
||||||
|
|
||||||
|
setInterval(() => station.tick(), 1000);
|
||||||
|
|
||||||
|
// seed a starting level & flow
|
||||||
|
/*
|
||||||
|
pushSample(levelSensor, "level", 1.8, "m");
|
||||||
|
pushSample(upstreamFlow, "flow", 0.35, "m3/s");
|
||||||
|
pushSample(downstreamFlow, "flow", 0.20, "m3/s");
|
||||||
|
*/
|
||||||
|
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 20));
|
||||||
|
|
||||||
|
// pump increases discharge flow
|
||||||
|
/*
|
||||||
|
pushSample(downstreamFlow, "flow", 0.28, "m3/s");
|
||||||
|
pushSample(upstreamFlow, "flow", 0.40, "m3/s");
|
||||||
|
pushSample(levelSensor, "level", 1.85, "m");
|
||||||
|
*/
|
||||||
|
|
||||||
|
await pump.handleInput("parent", "execSequence", "startup");
|
||||||
|
await pump.handleInput("parent", "execMovement", 50);
|
||||||
|
console.log("Station state:", station.state);
|
||||||
|
console.log("Station output:", station.getOutput());
|
||||||
|
console.log("Pump state:", pump.state.getCurrentState());
|
||||||
|
})();
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
||||||
//
|
|
||||||
|
|
||||||
//coolprop example
|
//coolprop example
|
||||||
(async () => {
|
(async () => {
|
||||||
const PropsSI = await coolprop.getPropsSI();
|
const PropsSI = await coolprop.getPropsSI();
|
||||||
|
|||||||
Reference in New Issue
Block a user