before functional changes by Codex

This commit is contained in:
znetsixe
2026-02-19 17:36:44 +01:00
parent 405be33626
commit b5137ba9c2
10 changed files with 1118 additions and 220 deletions

View File

@@ -87,10 +87,63 @@ class Machine {
this.child = {}; // object to hold child information so we know on what to subscribe
this.childRegistrationUtils = new childRegistrationUtils(this); // Child registration utility
this.virtualPressureChildIds = {
upstream: "dashboard-sim-upstream",
downstream: "dashboard-sim-downstream",
};
this.virtualPressureChildren = {};
this.realPressureChildIds = {
upstream: new Set(),
downstream: new Set(),
};
this._initVirtualPressureChildren();
}
_initVirtualPressureChildren() {
const createVirtualChild = (position) => {
const id = this.virtualPressureChildIds[position];
const name = `dashboard-sim-${position}`;
const measurements = new MeasurementContainer({
autoConvert: true,
defaultUnits: {
pressure: "mbar",
flow: this.config.general.unit,
power: "kW",
temperature: "C",
},
});
measurements.setChildId(id);
measurements.setChildName(name);
measurements.setParentRef(this);
return {
config: {
general: { id, name },
functionality: {
softwareType: "measurement",
positionVsParent: position,
},
asset: {
type: "pressure",
unit: "mbar",
},
},
measurements,
};
};
const upstreamChild = createVirtualChild("upstream");
const downstreamChild = createVirtualChild("downstream");
this.virtualPressureChildren.upstream = upstreamChild;
this.virtualPressureChildren.downstream = downstreamChild;
this.registerChild(upstreamChild, "measurement");
this.registerChild(downstreamChild, "measurement");
}
_init(){
//assume standard temperature is 20degrees
this.measurements.type('temperature').variant('measured').position('atEquipment').value(15).unit('C');
@@ -118,13 +171,19 @@ class Machine {
/*------------------- Register child events -------------------*/
registerChild(child, softwareType) {
this.logger.debug('Setting up child event for softwaretype ' + softwareType);
const resolvedSoftwareType = softwareType || child?.config?.functionality?.softwareType || "measurement";
this.logger.debug('Setting up child event for softwaretype ' + resolvedSoftwareType);
if(softwareType === "measurement"){
const position = child.config.functionality.positionVsParent;
const distance = child.config.functionality.distanceVsParent || 0;
if(resolvedSoftwareType === "measurement"){
const position = String(child.config.functionality.positionVsParent || "atEquipment").toLowerCase();
const measurementType = child.config.asset.type;
const key = `${measurementType}_${position}`;
const childId = child.config?.general?.id || `${measurementType}-${position}-unknown`;
const isVirtualPressureChild = Object.values(this.virtualPressureChildIds).includes(childId);
if (measurementType === "pressure" && !isVirtualPressureChild) {
this.realPressureChildIds[position]?.add(childId);
}
//rebuild to measurementype.variant no position and then switch based on values not strings or names.
const eventName = `${measurementType}.measured.${position}`;
@@ -140,6 +199,7 @@ class Machine {
.type(measurementType)
.variant("measured")
.position(position)
.child(childId)
.value(eventData.value, eventData.timestamp, eventData.unit);
// Call the appropriate handler
@@ -439,14 +499,16 @@ _callMeasurementHandler(measurementType, value, position, context) {
return 0;
}
const pressureDiff = this.measurements.type('pressure').variant('measured').difference();
const upstreamPressure = this._getPreferredPressureValue("upstream");
const downstreamPressure = this._getPreferredPressureValue("downstream");
// Both upstream & downstream => differential
if (pressureDiff) {
this.logger.debug(`Pressure differential: ${pressureDiff.value}`);
this.predictFlow.fDimension = pressureDiff.value;
this.predictPower.fDimension = pressureDiff.value;
this.predictCtrl.fDimension = pressureDiff.value;
if (upstreamPressure != null && downstreamPressure != null) {
const pressureDiffValue = downstreamPressure - upstreamPressure;
this.logger.debug(`Pressure differential: ${pressureDiffValue}`);
this.predictFlow.fDimension = pressureDiffValue;
this.predictPower.fDimension = pressureDiffValue;
this.predictCtrl.fDimension = pressureDiffValue;
//update the cog
const { cog, minEfficiency } = this.calcCog();
// calc efficiency
@@ -454,12 +516,9 @@ _callMeasurementHandler(measurementType, value, position, context) {
//update the distance from peak
this.calcDistanceBEP(efficiency,cog,minEfficiency);
return pressureDiff.value;
return pressureDiffValue;
}
// get downstream
const downstreamPressure = this.measurements.type('pressure').variant('measured').position('downstream').getCurrentValue();
// Only downstream => use it, warn that it's partial
if (downstreamPressure != null) {
this.logger.warn(`Using downstream pressure only for prediction: ${downstreamPressure} This is less acurate!!`);
@@ -475,6 +534,21 @@ _callMeasurementHandler(measurementType, value, position, context) {
return downstreamPressure;
}
// Only upstream => use it, warn that it's partial
if (upstreamPressure != null) {
this.logger.warn(`Using upstream pressure only for prediction: ${upstreamPressure} This is less acurate!!`);
this.predictFlow.fDimension = upstreamPressure;
this.predictPower.fDimension = upstreamPressure;
this.predictCtrl.fDimension = upstreamPressure;
//update the cog
const { cog, minEfficiency } = this.calcCog();
// calc efficiency
const efficiency = this.calcEfficiency(this.predictPower.outputY, this.predictFlow.outputY, "predicted");
//update the distance from peak
this.calcDistanceBEP(efficiency,cog,minEfficiency);
return upstreamPressure;
}
this.logger.error(`No valid pressure measurements available to calculate prediction using last known pressure`);
//set default at 0 => lowest pressure possible
@@ -493,6 +567,80 @@ _callMeasurementHandler(measurementType, value, position, context) {
return 0;
}
_getPreferredPressureValue(position) {
const realIds = Array.from(this.realPressureChildIds[position] || []);
for (const childId of realIds) {
const value = this.measurements
.type("pressure")
.variant("measured")
.position(position)
.child(childId)
.getCurrentValue();
if (value != null) return value;
}
const virtualId = this.virtualPressureChildIds[position];
if (virtualId) {
const simulatedValue = this.measurements
.type("pressure")
.variant("measured")
.position(position)
.child(virtualId)
.getCurrentValue();
if (simulatedValue != null) return simulatedValue;
}
return this.measurements
.type("pressure")
.variant("measured")
.position(position)
.getCurrentValue();
}
getPressureInitializationStatus() {
const upstreamPressure = this._getPreferredPressureValue("upstream");
const downstreamPressure = this._getPreferredPressureValue("downstream");
const hasUpstream = upstreamPressure != null;
const hasDownstream = downstreamPressure != null;
const hasDifferential = hasUpstream && hasDownstream;
return {
hasUpstream,
hasDownstream,
hasDifferential,
initialized: hasUpstream || hasDownstream || hasDifferential,
source: hasDifferential ? 'differential' : hasDownstream ? 'downstream' : hasUpstream ? 'upstream' : null,
};
}
updateSimulatedMeasurement(type, position, value, context = {}) {
const normalizedType = String(type || "").toLowerCase();
const normalizedPosition = String(position || "atEquipment").toLowerCase();
if (normalizedType !== "pressure") {
this._callMeasurementHandler(normalizedType, value, normalizedPosition, context);
return;
}
if (!this.virtualPressureChildIds[normalizedPosition]) {
this.logger.warn(`Unsupported simulated pressure position '${normalizedPosition}'`);
return;
}
const child = this.virtualPressureChildren[normalizedPosition];
if (!child?.measurements) {
this.logger.error(`Virtual pressure child '${normalizedPosition}' is missing`);
return;
}
child.measurements
.type("pressure")
.variant("measured")
.position(normalizedPosition)
.value(value, context.timestamp || Date.now(), context.unit || "mbar");
}
handleMeasuredFlow() {
const flowDiff = this.measurements.type('flow').variant('measured').difference();
@@ -588,8 +736,9 @@ _callMeasurementHandler(measurementType, value, position, context) {
// Helper method for operational state check
_isOperationalState() {
const state = this.state.getCurrentState();
this.logger.debug(`Checking operational state ${this.state.getCurrentState()} ? ${["operational", "accelerating", "decelerating"].includes(state)}`);
return ["operational", "accelerating", "decelerating"].includes(state);
const activeStates = ["operational", "warmingup", "accelerating", "decelerating"];
this.logger.debug(`Checking operational state ${this.state.getCurrentState()} ? ${activeStates.includes(state)}`);
return activeStates.includes(state);
}
//what is the internal functions that need updating when something changes that has influence on this.
@@ -822,150 +971,3 @@ _callMeasurementHandler(measurementType, value, position, context) {
module.exports = Machine;
/*------------------- Testing -------------------*/
/*
curve = require('C:/Users/zn375/.node-red/public/fallbackData.json');
//import a child
const Child = require('../../measurement/src/specificClass');
console.log(`Creating child...`);
const PT1 = new Child(config={
general:{
name:"PT1",
logging:{
enabled:true,
logLevel:"debug",
},
},
functionality:{
softwareType:"measurement",
positionVsParent:"upstream",
},
asset:{
supplier:"Vega",
category:"sensor",
type:"pressure",
model:"Vegabar 82",
unit: "mbar"
},
});
const PT2 = new Child(config={
general:{
name:"PT2",
logging:{
enabled:true,
logLevel:"debug",
},
},
functionality:{
softwareType:"measurement",
positionVsParent:"upstream",
},
asset:{
supplier:"Vega",
category:"sensor",
type:"pressure",
model:"Vegabar 82",
unit: "mbar"
},
});
//create a machine
console.log(`Creating machine...`);
const machineConfig = {
general: {
name: "Hydrostal",
logging: {
enabled: true,
logLevel: "debug",
}
},
asset: {
supplier: "Hydrostal",
type: "pump",
category: "centrifugal",
model: "H05K-S03R+HGM1X-X280KO", // Ensure this field is present.
machineCurve: curve["machineCurves"]["Hydrostal"]["H05K-S03R+HGM1X-X280KO"],
}
}
const stateConfig = {
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,
},
};
const machine = new Machine(machineConfig, stateConfig);
//machine.logger.info(JSON.stringify(curve["machineCurves"]["Hydrostal"]["H05K-S03R+HGM1X-X280KO"]));
machine.logger.info(`Registering child...`);
machine.childRegistrationUtils.registerChild(PT1, "upstream");
machine.childRegistrationUtils.registerChild(PT2, "downstream");
//feed curve to the machine class
//machine.updateCurve(curve["machineCurves"]["Hydrostal"]["H05K-S03R+HGM1X-X280KO"]);
PT1.logger.info(`Enable sim...`);
PT1.toggleSimulation();
PT2.logger.info(`Enable sim...`);
PT2.toggleSimulation();
machine.getOutput();
//manual test
//machine.handleInput("parent", "execSequence", "startup");
machine.measurements.type("pressure").variant("measured").position('upstream').value(-200);
machine.measurements.type("pressure").variant("measured").position('downstream').value(1000);
testingSequences();
const tickLoop = setInterval(changeInput,1000);
function changeInput(){
PT1.logger.info(`tick...`);
PT1.tick();
PT2.tick();
}
async function testingSequences(){
try{
console.log(` ********** Testing sequence startup... **********`);
await machine.handleInput("parent", "execSequence", "startup");
console.log(` ********** Testing movement to 15... **********`);
await machine.handleInput("parent", "execMovement", 15);
machine.getOutput();
console.log(` ********** Testing sequence shutdown... **********`);
await machine.handleInput("parent", "execSequence", "shutdown");
console.log(`********** Testing moving to setpoint 10... while in idle **********`);
await machine.handleInput("parent", "execMovement", 10);
console.log(` ********** Testing sequence emergencyStop... **********`);
await machine.handleInput("parent", "execSequence", "emergencystop");
console.log(`********** Testing sequence boot... **********`);
await machine.handleInput("parent", "execSequence", "boot");
}catch(error){
console.error(`Error: ${error}`);
}
}
//*/