diff --git a/src/nodeClass.js b/src/nodeClass.js index 9b96042..7706508 100644 --- a/src/nodeClass.js +++ b/src/nodeClass.js @@ -67,8 +67,8 @@ class nodeClass { const totalFlow = mg.measurements ?.type("flow") ?.variant("predicted") - ?.position("downstream") - ?.getCurrentValue() || 0; + ?.position("atequipment") + ?.getCurrentValue('m3/h') || 0; const totalPower = mg.measurements ?.type("power") @@ -199,16 +199,16 @@ class nodeClass { const RED = this.RED; switch (msg.topic) { case "registerChild": - console.log(`Registering child in mgc: ${msg.payload}`); + //console.log(`Registering child in mgc: ${msg.payload}`); const childId = msg.payload; const childObj = RED.nodes.getNode(childId); // Debug: Check what we're getting - console.log(`Child object:`, childObj ? 'found' : 'NOT FOUND'); - console.log(`Child source:`, childObj?.source ? 'exists' : 'MISSING'); + //console.log(`Child object:`, childObj ? 'found' : 'NOT FOUND'); + //console.log(`Child source:`, childObj?.source ? 'exists' : 'MISSING'); if (childObj?.source) { - console.log(`Child source type:`, childObj.source.constructor.name); - console.log(`Child has state:`, !!childObj.source.state); + //console.log(`Child source type:`, childObj.source.constructor.name); + //console.log(`Child has state:`, !!childObj.source.state); } mg.childRegistrationUtils.registerChild( @@ -217,7 +217,7 @@ class nodeClass { ); // Debug: Check machines after registration - console.log(`Total machines after registration:`, Object.keys(mg.machines || {}).length); + //console.log(`Total machines after registration:`, Object.keys(mg.machines || {}).length); break; case "setMode": diff --git a/src/specificClass.js b/src/specificClass.js index 82f4d08..478b7b4 100644 --- a/src/specificClass.js +++ b/src/specificClass.js @@ -15,7 +15,17 @@ class MachineGroup { this.logger = new logger(this.config.general.logging.enabled,this.config.general.logging.logLevel, this.config.general.name); // Initialize measurements - this.measurements = new MeasurementContainer(); + this.measurements = new MeasurementContainer({ + autoConvert: true, + windowSize: 50, + defaultUnits: { + pressure: 'mbar', + flow: 'l/s', + power: 'kW', + temperature: 'C' + } + }); + this.interpolation = new interpolation(); // Machines and child data @@ -39,6 +49,8 @@ class MachineGroup { registerChild(child,softwareType) { this.logger.debug('Setting up childs specific for this class'); + + const position = child.config.general.positionVsParent; if(softwareType == "machine"){ // Check if the machine is already registered @@ -133,15 +145,23 @@ class MachineGroup { this.logger.debug(`\n --------- Calculating dynamic totals for ${Object.keys(this.machines).length} machines. @ current pressure settings : ----------`); Object.values(this.machines).forEach(machine => { + //skip machines without valid curve + if(!machine.hasCurve){ + this.logger.error(`Machine ${machine.config.general.id} does not have a valid curve. Skipping in dynamic totals calculation.`); + return; + } + this.logger.debug(`Processing machine with id: ${machine.config.general.id}`); this.logger.debug(`Current pressure settings: ${JSON.stringify(machine.predictFlow.currentF)}`); + //fetch min flow ever seen over all machines const minFlow = machine.predictFlow.currentFxyYMin; const maxFlow = machine.predictFlow.currentFxyYMax; const minPower = machine.predictPower.currentFxyYMin; const maxPower = machine.predictPower.currentFxyYMax; - const actFlow = machine.measurements.type("flow").variant("predicted").position("downstream").getCurrentValue(); - const actPower = machine.measurements.type("power").variant("predicted").position("atEquipment").getCurrentValue(); + + const actFlow = machine.measurements.type("flow").variant("predicted").position("atequipment").getCurrentValue(); + const actPower = machine.measurements.type("power").variant("predicted").position("atequipment").getCurrentValue(); this.logger.debug(`Machine ${machine.config.general.id} - Min Flow: ${minFlow}, Max Flow: ${maxFlow}, Min Power: ${minPower}, Max Power: ${maxPower}, NCog: ${machine.NCog}`); @@ -192,14 +212,14 @@ class MachineGroup { handlePressureChange() { this.logger.info("---------------------->>>>>>>>>>>>>>>>>>>>>>>>>>>Pressure change detected."); // Recalculate totals - const { flow, power } = this.calcDynamicTotals(); + const { flow, power } = this.calcDynamicTotals(); this.logger.debug(`Dynamic Totals after pressure change - Flow: Min ${flow.min}, Max ${flow.max}, Act ${flow.act} | Power: Min ${power.min}, Max ${power.max}, Act ${power.act}`); - this.measurements.type("flow").variant("predicted").position("downstream").value(flow.act); - this.measurements.type("power").variant("predicted").position("atEquipment").value(power.act); + this.measurements.type("flow").variant("predicted").position("atequipment").value(flow.act); + this.measurements.type("power").variant("predicted").position("atequipment").value(power.act); const { maxEfficiency, lowestEfficiency } = this.calcGroupEfficiency(this.machines); - const efficiency = this.measurements.type("efficiency").variant("predicted").position("atEquipment").getCurrentValue(); + const efficiency = this.measurements.type("efficiency").variant("predicted").position("atequipment").getCurrentValue(); this.calcDistanceBEP(efficiency,maxEfficiency,lowestEfficiency); } @@ -238,8 +258,8 @@ class MachineGroup { if(machine.measurements.type("flow").variant("measured").position("downstream").getCurrentValue()){ flow = machine.measurements.type("flow").variant("measured").position("downstream").getCurrentValue(); } - else if(machine.measurements.type("flow").variant("predicted").position("downstream").getCurrentValue()){ - flow = machine.measurements.type("flow").variant("predicted").position("downstream").getCurrentValue(); + else if(machine.measurements.type("flow").variant("predicted").position("atequipment").getCurrentValue()){ + flow = machine.measurements.type("flow").variant("predicted").position("atequipment").getCurrentValue(); } else{ this.logger.error("Dont perform calculation at all seeing that there is a machine working but we dont know the flow its producing"); @@ -264,7 +284,7 @@ class MachineGroup { Object.keys(machines).forEach(machineId => { const state = machines[machineId].state.getCurrentState(); - const validActionForMode = machines[machineId].isValidActionForMode("execSequence", "auto"); + const validActionForMode = machines[machineId].isValidActionForMode("execsequence", "auto"); // Reasons why a machine is not valid for the combination @@ -451,10 +471,10 @@ class MachineGroup { this.logger.debug(`Moving to demand: ${Qd.toFixed(2)} -> Pumps: [${debugInfo}] => Total Power: ${bestResult.bestPower.toFixed(2)}`); //store the total delivered power - this.measurements.type("power").variant("predicted").position("atEquipment").value(bestResult.bestPower); - this.measurements.type("flow").variant("predicted").position("downstream").value(bestResult.bestFlow); - this.measurements.type("efficiency").variant("predicted").position("atEquipment").value(bestResult.bestFlow / bestResult.bestPower); - this.measurements.type("Ncog").variant("predicted").position("atEquipment").value(bestResult.bestCog); + this.measurements.type("power").variant("predicted").position("atequipment").value(bestResult.bestPower); + this.measurements.type("flow").variant("predicted").position("atequipment").value(bestResult.bestFlow); + this.measurements.type("efficiency").variant("predicted").position("atequipment").value(bestResult.bestFlow / bestResult.bestPower); + this.measurements.type("Ncog").variant("predicted").position("atequipment").value(bestResult.bestCog); await Promise.all(Object.entries(this.machines).map(async ([machineId, machine]) => { // Find the flow for this machine in the best combination @@ -469,16 +489,16 @@ class MachineGroup { } if( (flow <= 0 ) && ( machineStates[machineId] === "operational" || machineStates[machineId] === "accelerating" || machineStates[machineId] === "decelerating" ) ){ - await machine.handleInput("parent", "execSequence", "shutdown"); + await machine.handleInput("parent", "execsequence", "shutdown"); } if(machineStates[machineId] === "idle" && flow > 0){ - await machine.handleInput("parent", "execSequence", "startup"); - await machine.handleInput("parent", "flowMovement", flow); + await machine.handleInput("parent", "execsequence", "startup"); + await machine.handleInput("parent", "flowmovement", flow); } if(machineStates[machineId] === "operational" && flow > 0 ){ - await machine.handleInput("parent", "flowMovement", flow); + await machine.handleInput("parent", "flowmovement", flow); } })); } @@ -551,7 +571,7 @@ class MachineGroup { filterOutUnavailableMachines(list) { const newList = list.filter(({ id, machine }) => { const state = machine.state.getCurrentState(); - const validActionForMode = machine.isValidActionForMode("execSequence", "auto"); + const validActionForMode = machine.isValidActionForMode("execsequence", "auto"); return !(state === "off" || state === "coolingdown" || state === "stopping" || state === "emergencystop" || !validActionForMode); }); @@ -681,10 +701,10 @@ class MachineGroup { this.logger.debug(`Priority control for demand: ${totalFlow.toFixed(2)} -> Active pumps: [${debugInfo}] => Total Power: ${totalPower.toFixed(2)}`); // Store measurements - this.measurements.type("power").variant("predicted").position("atEquipment").value(totalPower); - this.measurements.type("flow").variant("predicted").position("downstream").value(totalFlow); - this.measurements.type("efficiency").variant("predicted").position("atEquipment").value(totalFlow / totalPower); - this.measurements.type("Ncog").variant("predicted").position("atEquipment").value(totalCog); + this.measurements.type("power").variant("predicted").position("atequipment").value(totalPower); + this.measurements.type("flow").variant("predicted").position("atequipment").value(totalFlow); + this.measurements.type("efficiency").variant("predicted").position("atequipment").value(totalFlow / totalPower); + this.measurements.type("Ncog").variant("predicted").position("atequipment").value(totalCog); this.logger.debug(`Flow distribution: ${JSON.stringify(flowDistribution)}`); // Apply the flow distribution to machines @@ -694,13 +714,13 @@ class MachineGroup { const currentState = this.machines[machineId].state.getCurrentState(); if (flow <= 0 && (currentState === "operational" || currentState === "accelerating" || currentState === "decelerating")) { - await machine.handleInput("parent", "execSequence", "shutdown"); + await machine.handleInput("parent", "execsequence", "shutdown"); } else if (currentState === "idle" && flow > 0) { - await machine.handleInput("parent", "execSequence", "startup"); + await machine.handleInput("parent", "execsequence", "startup"); } else if (currentState === "operational" && flow > 0) { - await machine.handleInput("parent", "flowMovement", flow); + await machine.handleInput("parent", "flowmovement", flow); } })); } @@ -716,7 +736,7 @@ class MachineGroup { if(input < 0 ){ //turn all machines off await Promise.all(Object.entries(this.machines).map(async ([machineId, machine]) => { - if (this.isMachineActive(machineId)) { await machine.handleInput("parent", "execSequence", "shutdown"); } + if (this.isMachineActive(machineId)) { await machine.handleInput("parent", "execsequence", "shutdown"); } })); return; } @@ -780,13 +800,13 @@ class MachineGroup { const currentState = this.machines[machineId].state.getCurrentState(); if (ctrl < 0 && (currentState === "operational" || currentState === "accelerating" || currentState === "decelerating")) { - await machine.handleInput("parent", "execSequence", "shutdown"); + await machine.handleInput("parent", "execsequence", "shutdown"); } else if (currentState === "idle" && ctrl >= 0) { - await machine.handleInput("parent", "execSequence", "startup"); + await machine.handleInput("parent", "execsequence", "startup"); } else if (currentState === "operational" && ctrl > 0) { - await machine.handleInput("parent", "execMovement", ctrl); + await machine.handleInput("parent", "execmovement", ctrl); } })); @@ -796,8 +816,8 @@ class MachineGroup { // fetch and store measurements Object.entries(this.machines).forEach(([machineId, machine]) => { - const powerValue = machine.measurements.type("power").variant("predicted").position("atEquipment").getCurrentValue(); - const flowValue = machine.measurements.type("flow").variant("predicted").position("downstream").getCurrentValue(); + const powerValue = machine.measurements.type("power").variant("predicted").position("atequipment").getCurrentValue(); + const flowValue = machine.measurements.type("flow").variant("predicted").position("atequipment").getCurrentValue(); if (powerValue !== null) { totalPower.push(powerValue); @@ -807,11 +827,11 @@ class MachineGroup { } }); - this.measurements.type("power").variant("predicted").position("atEquipment").value(totalPower.reduce((a, b) => a + b, 0)); - this.measurements.type("flow").variant("predicted").position("downstream").value(totalFlow.reduce((a, b) => a + b, 0)); + this.measurements.type("power").variant("predicted").position("atequipment").value(totalPower.reduce((a, b) => a + b, 0)); + this.measurements.type("flow").variant("predicted").position("atequipment").value(totalFlow.reduce((a, b) => a + b, 0)); if(totalPower.reduce((a, b) => a + b, 0) > 0){ - this.measurements.type("efficiency").variant("predicted").position("atEquipment").value(totalFlow.reduce((a, b) => a + b, 0) / totalPower.reduce((a, b) => a + b, 0)); + this.measurements.type("efficiency").variant("predicted").position("atequipment").value(totalFlow.reduce((a, b) => a + b, 0) / totalPower.reduce((a, b) => a + b, 0)); } } @@ -821,6 +841,13 @@ class MachineGroup { } async handleInput(source, demand, powerCap = Infinity, priorityList = null) { + + const demandQ = parseFloat(demand); + + if(!Number.isFinite(demandQ)){ + this.logger.error(`Invalid flow demand input: ${demand}. Must be a finite number.`); + return; + } //abort current movements await this.abortActiveMovements("new demand received"); @@ -828,7 +855,6 @@ class MachineGroup { const scaling = this.scaling; const mode = this.mode; const dynamicTotals = this.calcDynamicTotals(); - const demandQ = parseFloat(demand); let demandQout = 0; // keep output Q by default 0 for safety this.logger.debug(`Handling input from ${source}: Demand = ${demand}, Power Cap = ${powerCap}, Priority List = ${priorityList}`); @@ -857,7 +883,6 @@ class MachineGroup { break; case "normalized": - this.logger.debug(`Normalizing flow demand: ${demandQ} with min: ${dynamicTotals.flow.min} and max: ${dynamicTotals.flow.max}`); if(demand < 0){ this.logger.debug(`Turning machines off`); @@ -875,7 +900,6 @@ class MachineGroup { } - // Execute control based on mode switch(mode) { case "prioritycontrol": @@ -911,7 +935,7 @@ class MachineGroup { async turnOffAllMachines(){ await Promise.all(Object.entries(this.machines).map(async ([machineId, machine]) => { - if (this.isMachineActive(machineId)) { await machine.handleInput("parent", "execSequence", "shutdown"); } + if (this.isMachineActive(machineId)) { await machine.handleInput("parent", "execsequence", "shutdown"); } })); } @@ -929,7 +953,7 @@ class MachineGroup { this.measurements.getVariants(type).forEach(variant => { const downstreamVal = this.measurements.type(type).variant(variant).position("downstream").getCurrentValue(); - const atEquipmentVal = this.measurements.type(type).variant(variant).position("atEquipment").getCurrentValue(); + const atEquipmentVal = this.measurements.type(type).variant(variant).position("atequipment").getCurrentValue(); const upstreamVal = this.measurements.type(type).variant(variant).position("upstream").getCurrentValue(); if (downstreamVal != null) { @@ -939,7 +963,7 @@ class MachineGroup { output[`upstream_${variant}_${type}`] = upstreamVal; } if (atEquipmentVal != null) { - output[`atEquipment_${variant}_${type}`] = atEquipmentVal; + output[`atequipment${variant}_${type}`] = atEquipmentVal; } if (downstreamVal != null && upstreamVal != null) { const diffVal = this.measurements.type(type).variant(variant).difference().value; @@ -964,8 +988,8 @@ class MachineGroup { } module.exports = MachineGroup; -/* +/* const Machine = require('../../rotatingMachine/src/specificClass'); const Measurement = require('../../measurement/src/specificClass'); const specs = require('../../generalFunctions/datasets/assetData/curves/hidrostal-H05K-S03R.json'); @@ -993,9 +1017,9 @@ function createBaseMachineConfig(machineNum, name,specs) { mode: { current: "auto", allowedActions: { - auto: ["execSequence", "execMovement", "statusCheck"], - virtualControl: ["execMovement", "statusCheck"], - fysicalControl: ["statusCheck"] + auto: ["execsequence", "execmovement", "statuscheck"], + virtualControl: ["execmovement", "statuscheck"], + fysicalControl: ["statuscheck"] }, allowedSources: { auto: ["parent", "GUI"], @@ -1103,7 +1127,7 @@ async function makeMachines(){ const percMax = 100; try{ - /* + for(let demand = mg.dynamicTotals.flow.min ; demand <= mg.dynamicTotals.flow.max ; demand += 2){ //set pressure