From c081acae4e18c0d714c015be36c84278a0eac684 Mon Sep 17 00:00:00 2001 From: "p.vanderwilt" Date: Fri, 10 Oct 2025 13:27:31 +0200 Subject: [PATCH 01/13] Remove non-implemented temperature handling function --- src/specificClass.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/specificClass.js b/src/specificClass.js index 823f60c..679f3b9 100644 --- a/src/specificClass.js +++ b/src/specificClass.js @@ -161,10 +161,6 @@ _callMeasurementHandler(measurementType, value, position, context) { this.updateMeasuredFlow(value, position, context); break; - case 'temperature': - this.updateMeasuredTemperature(value, position, context); - break; - default: this.logger.warn(`No handler for measurement type: ${measurementType}`); // Generic handler - just update position From 37e6523c5527e3bfd9d9dbb4f021e018f45be3cf Mon Sep 17 00:00:00 2001 From: HorriblePerson555 <47578455+HorriblePerson555@users.noreply.github.com> Date: Thu, 16 Oct 2025 16:32:20 +0200 Subject: [PATCH 02/13] Refactor child registration to use dedicated connection methods for measurement and reactor types --- src/specificClass.js | 90 ++++++++++++++++++++++++-------------------- 1 file changed, 50 insertions(+), 40 deletions(-) diff --git a/src/specificClass.js b/src/specificClass.js index 245946b..a0f3853 100644 --- a/src/specificClass.js +++ b/src/specificClass.js @@ -77,52 +77,62 @@ class Machine { registerChild(child, softwareType) { this.logger.debug('Setting up child event for softwaretype ' + softwareType); - if(softwareType === "measurement"){ - const position = child.config.functionality.positionVsParent; - const distance = child.config.functionality.distanceVsParent || 0; - const measurementType = child.config.asset.type; - const key = `${measurementType}_${position}`; - //rebuild to measurementype.variant no position and then switch based on values not strings or names. - const eventName = `${measurementType}.measured.${position}`; - - this.logger.debug(`Setting up listener for ${eventName} from child ${child.config.general.name}`); - // Register event listener for measurement updates - child.measurements.emitter.on(eventName, (eventData) => { - this.logger.debug(`🔄 ${position} ${measurementType} from ${eventData.childName}: ${eventData.value} ${eventData.unit}`); + switch (softwareType) { + case "measurement": + this._connectMeasurement(child); + break; + case "reactor": + this._connectReactor(child); + break; - - console.log(` Emitting... ${eventName} with data:`); - // Store directly in parent's measurement container - this.measurements - .type(measurementType) - .variant("measured") - .position(position) - .value(eventData.value, eventData.timestamp, eventData.unit); - - // Call the appropriate handler - this._callMeasurementHandler(measurementType, eventData.value, position, eventData); - }); + default: + this.logger.error(`Unrecognized softwareType: ${softwareType}`); } } -// Centralized handler dispatcher -_callMeasurementHandler(measurementType, value, position, context) { - switch (measurementType) { - case 'pressure': - this.updateMeasuredPressure(value, position, context); - break; + _connectMeasurement(measurementChild) { + const position = measurementChild.config.functionality.positionVsParent; + const distance = measurementChild.config.functionality.distanceVsParent || 0; + const measurementType = measurementChild.config.asset.type; + const key = `${measurementType}_${position}`; + //rebuild to measurementype.variant no position and then switch based on values not strings or names. + const eventName = `${measurementType}.measured.${position}`; + + this.logger.debug(`Setting up listener for ${eventName} from child ${measurementChild.config.general.name}`); + // Register event listener for measurement updates + measurementChild.measurements.emitter.on(eventName, (eventData) => { + this.logger.debug(`🔄 ${position} ${measurementType} from ${eventData.childName}: ${eventData.value} ${eventData.unit}`); + + + console.log(` Emitting... ${eventName} with data:`); + // Store directly in parent's measurement container + this.measurements + .type(measurementType) + .variant("measured") + .position(position) + .value(eventData.value, eventData.timestamp, eventData.unit); - case 'flow': - this.updateMeasuredFlow(value, position, context); - break; - - default: - this.logger.warn(`No handler for measurement type: ${measurementType}`); - // Generic handler - just update position - this.updatePosition(); - break; + // Call the appropriate handler + switch (measurementType) { + case 'pressure': + this.updateMeasuredPressure(eventData.value, position, eventData); + break; + + case 'flow': + this.updateMeasuredFlow(eventData.value, position, eventData); + break; + + default: + this.logger.warn(`No handler for measurement type: ${measurementType}`); + // Generic handler - just update position + this.updatePosition(); + } + }); + } + + _connectReactor(reactorChild) { + this.logger.error("Reactor child not implemented yet."); } -} //---------------- END child stuff -------------// From d7cc6a4a8b1f35a12dd2a69fddd25b81767fac23 Mon Sep 17 00:00:00 2001 From: HorriblePerson555 <47578455+HorriblePerson555@users.noreply.github.com> Date: Fri, 17 Oct 2025 13:38:05 +0200 Subject: [PATCH 03/13] Enhance child registration logging and add validation for measurement child --- src/specificClass.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/specificClass.js b/src/specificClass.js index a0f3853..3ad3fa5 100644 --- a/src/specificClass.js +++ b/src/specificClass.js @@ -75,13 +75,13 @@ class Machine { /*------------------- Register child events -------------------*/ registerChild(child, softwareType) { - this.logger.debug('Setting up child event for softwaretype ' + softwareType); - switch (softwareType) { case "measurement": + this.logger.debug(`Registering measurement child...`); this._connectMeasurement(child); break; case "reactor": + this.logger.debug(`Registering reactor child...`); this._connectReactor(child); break; @@ -91,10 +91,14 @@ class Machine { } _connectMeasurement(measurementChild) { + if (!measurementChild) { + this.logger.warn("Invalid measurement provided."); + return; + } + const position = measurementChild.config.functionality.positionVsParent; const distance = measurementChild.config.functionality.distanceVsParent || 0; const measurementType = measurementChild.config.asset.type; - const key = `${measurementType}_${position}`; //rebuild to measurementype.variant no position and then switch based on values not strings or names. const eventName = `${measurementType}.measured.${position}`; From a8fb56bfb8ceb5d11ab7748844d5b5b986eb2289 Mon Sep 17 00:00:00 2001 From: "p.vanderwilt" Date: Wed, 22 Oct 2025 14:41:35 +0200 Subject: [PATCH 04/13] Add upstream and downstream reactor handling; improve error logging --- src/specificClass.js | 56 ++++++++++++++++++++++++++++---------------- 1 file changed, 36 insertions(+), 20 deletions(-) diff --git a/src/specificClass.js b/src/specificClass.js index 3ad3fa5..aba5570 100644 --- a/src/specificClass.js +++ b/src/specificClass.js @@ -68,6 +68,10 @@ class Machine { this.updatePosition(); }); + // used for holding the source and sink unit operations or other object with setInfluent / getEffluent method for e.g. recirculation. + this.upstreamReactor = null; + this.downstreamReactor = null; + this.child = {}; // object to hold child information so we know on what to subscribe this.childRegistrationUtils = new childRegistrationUtils(this); // Child registration utility @@ -92,7 +96,7 @@ class Machine { _connectMeasurement(measurementChild) { if (!measurementChild) { - this.logger.warn("Invalid measurement provided."); + this.logger.error("Invalid measurement provided."); return; } @@ -107,14 +111,12 @@ class Machine { measurementChild.measurements.emitter.on(eventName, (eventData) => { this.logger.debug(`🔄 ${position} ${measurementType} from ${eventData.childName}: ${eventData.value} ${eventData.unit}`); - - console.log(` Emitting... ${eventName} with data:`); // Store directly in parent's measurement container this.measurements .type(measurementType) .variant("measured") .position(position) - .value(eventData.value, eventData.timestamp, eventData.unit); + .value(eventData.value, eventData.timestamp, eventData.unit); // Call the appropriate handler switch (measurementType) { @@ -135,26 +137,31 @@ class Machine { } _connectReactor(reactorChild) { - this.logger.error("Reactor child not implemented yet."); + if (!reactorChild) { + this.logger.error("Invalid measurement provided."); + return; + } + + this.downstreamReactor = reactorChild; // downstream from the pumps perpective } -//---------------- END child stuff -------------// + //---------------- END child stuff -------------// - // Method to assess drift using errorMetrics - assessDrift(measurement, processMin, processMax) { - this.logger.debug(`Assessing drift for measurement: ${measurement} processMin: ${processMin} processMax: ${processMax}`); - const predictedMeasurement = this.measurements.type(measurement).variant("predicted").position("downstream").getAllValues().values; - const measuredMeasurement = this.measurements.type(measurement).variant("measured").position("downstream").getAllValues().values; + // Method to assess drift using errorMetrics + assessDrift(measurement, processMin, processMax) { + this.logger.debug(`Assessing drift for measurement: ${measurement} processMin: ${processMin} processMax: ${processMax}`); + const predictedMeasurement = this.measurements.type(measurement).variant("predicted").position("downstream").getAllValues().values; + const measuredMeasurement = this.measurements.type(measurement).variant("measured").position("downstream").getAllValues().values; - if (!predictedMeasurement || !measuredMeasurement) return null; - - return this.errorMetrics.assessDrift( - predictedMeasurement, - measuredMeasurement, - processMin, - processMax - ); - } + if (!predictedMeasurement || !measuredMeasurement) return null; + + return this.errorMetrics.assessDrift( + predictedMeasurement, + measuredMeasurement, + processMin, + processMax + ); + } reverseCurve(curve) { const reversedCurve = {}; @@ -499,12 +506,17 @@ class Machine { // NEW: Flow handler updateMeasuredFlow(value, position, context = {}) { + if (!this._isOperationalState()) { this.logger.warn(`Machine not operational, skipping flow update from ${context.childName || 'unknown'}`); return; } this.logger.debug(`Flow update: ${value} at ${position} from ${context.childName || 'child'}`); + + if (this.upstreamReactor && this.downstreamReactor){ + this._updateConnectedReactor(); + } // Store in parent's measurement container this.measurements.type("flow").variant("measured").position(position).value(value, context.timestamp, context.unit); @@ -515,6 +527,10 @@ class Machine { } } + _updateConnectedReactor() { + this.downstreamReactor.setInfluent = this.upstreamReactor.getEffluent[1]; + } + // Helper method for operational state check _isOperationalState() { const state = this.state.getCurrentState(); From ac40a93ef1728a777eb1d7855695c851e7e1e8bb Mon Sep 17 00:00:00 2001 From: "p.vanderwilt" Date: Fri, 31 Oct 2025 13:07:52 +0100 Subject: [PATCH 05/13] Simplify child registration error handling --- src/specificClass.js | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/specificClass.js b/src/specificClass.js index aba5570..f1d8123 100644 --- a/src/specificClass.js +++ b/src/specificClass.js @@ -79,6 +79,11 @@ class Machine { /*------------------- Register child events -------------------*/ registerChild(child, softwareType) { + if(!child) { + this.logger.error(`Invalid ${softwareType} child provided.`); + return; + } + switch (softwareType) { case "measurement": this.logger.debug(`Registering measurement child...`); @@ -95,11 +100,6 @@ class Machine { } _connectMeasurement(measurementChild) { - if (!measurementChild) { - this.logger.error("Invalid measurement provided."); - return; - } - const position = measurementChild.config.functionality.positionVsParent; const distance = measurementChild.config.functionality.distanceVsParent || 0; const measurementType = measurementChild.config.asset.type; @@ -137,11 +137,6 @@ class Machine { } _connectReactor(reactorChild) { - if (!reactorChild) { - this.logger.error("Invalid measurement provided."); - return; - } - this.downstreamReactor = reactorChild; // downstream from the pumps perpective } From 303dfc477d7f25461c59f1cde6b5db1d8f8a1d1c Mon Sep 17 00:00:00 2001 From: "p.vanderwilt" Date: Fri, 31 Oct 2025 14:16:00 +0100 Subject: [PATCH 06/13] Add flow number configuration and UI input for rotating machine --- rotatingMachine.html | 5 +++++ src/specificClass.js | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/rotatingMachine.html b/rotatingMachine.html index 6d54b66..e2a0a69 100644 --- a/rotatingMachine.html +++ b/rotatingMachine.html @@ -25,6 +25,7 @@ shutdown: { value: 0 }, cooldown: { value: 0 }, machineCurve : { value: {}}, + flowNumber : { value: 1, required: true }, //define asset properties uuid: { value: "" }, @@ -127,6 +128,10 @@ +
+ + +
diff --git a/src/specificClass.js b/src/specificClass.js index f1d8123..1579611 100644 --- a/src/specificClass.js +++ b/src/specificClass.js @@ -523,7 +523,8 @@ class Machine { } _updateConnectedReactor() { - this.downstreamReactor.setInfluent = this.upstreamReactor.getEffluent[1]; + // Handles flow according to the configured "flow number" + this.downstreamReactor.setInfluent = this.upstreamReactor.getEffluent[this.config.flowNumber]; } // Helper method for operational state check From b6d268659a9d2d5b9e42dd922c24eaf63f80321a Mon Sep 17 00:00:00 2001 From: "p.vanderwilt" Date: Thu, 6 Nov 2025 14:50:40 +0100 Subject: [PATCH 07/13] Refactor flow handling: rename reactor references to source and sink and fix config minor bug --- rotatingMachine.html | 4 ++-- src/nodeClass.js | 3 ++- src/specificClass.js | 16 ++++++++-------- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/rotatingMachine.html b/rotatingMachine.html index e2a0a69..93473b4 100644 --- a/rotatingMachine.html +++ b/rotatingMachine.html @@ -24,8 +24,8 @@ warmup: { value: 0 }, shutdown: { value: 0 }, cooldown: { value: 0 }, - machineCurve : { value: {}}, - flowNumber : { value: 1, required: true }, + machineCurve: { value: {}}, + flowNumber: { value: 1, required: true }, //define asset properties uuid: { value: "" }, diff --git a/src/nodeClass.js b/src/nodeClass.js index 6d20326..7bb504e 100644 --- a/src/nodeClass.js +++ b/src/nodeClass.js @@ -63,7 +63,8 @@ class nodeClass { }, functionality: { positionVsParent: uiConfig.positionVsParent - } + }, + flowNumber: uiConfig.flowNumber }; // Utility for formatting outputs diff --git a/src/specificClass.js b/src/specificClass.js index 1579611..55a4b32 100644 --- a/src/specificClass.js +++ b/src/specificClass.js @@ -69,8 +69,8 @@ class Machine { }); // used for holding the source and sink unit operations or other object with setInfluent / getEffluent method for e.g. recirculation. - this.upstreamReactor = null; - this.downstreamReactor = null; + this.upstreamSource = null; + this.downstreamSink = null; this.child = {}; // object to hold child information so we know on what to subscribe this.childRegistrationUtils = new childRegistrationUtils(this); // Child registration utility @@ -93,7 +93,6 @@ class Machine { this.logger.debug(`Registering reactor child...`); this._connectReactor(child); break; - default: this.logger.error(`Unrecognized softwareType: ${softwareType}`); } @@ -137,7 +136,7 @@ class Machine { } _connectReactor(reactorChild) { - this.downstreamReactor = reactorChild; // downstream from the pumps perpective + this.downstreamSink = reactorChild; // downstream from the pumps perpective } //---------------- END child stuff -------------// @@ -509,8 +508,8 @@ class Machine { this.logger.debug(`Flow update: ${value} at ${position} from ${context.childName || 'child'}`); - if (this.upstreamReactor && this.downstreamReactor){ - this._updateConnectedReactor(); + if (this.upstreamSource && this.downstreamSink) { + this._updateSourceSink(); } // Store in parent's measurement container @@ -522,9 +521,10 @@ class Machine { } } - _updateConnectedReactor() { + _updateSourceSink() { // Handles flow according to the configured "flow number" - this.downstreamReactor.setInfluent = this.upstreamReactor.getEffluent[this.config.flowNumber]; + this.logger.debug(`Updating source-sink pair: ${this.upstreamSource.config.functionality.softwareType} - ${this.downstreamSink.config.functionality.softwareType}`); + this.downstreamSink.setInfluent = this.upstreamSource.getEffluent[this.config.flowNumber]; } // Helper method for operational state check From 99b45c87e41db09b9a97f971abbf578f5e776084 Mon Sep 17 00:00:00 2001 From: "p.vanderwilt" Date: Fri, 14 Nov 2025 12:55:11 +0100 Subject: [PATCH 08/13] Rename _updateSourceSink to updateSourceSink for outside access --- src/specificClass.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/specificClass.js b/src/specificClass.js index 55a4b32..7652922 100644 --- a/src/specificClass.js +++ b/src/specificClass.js @@ -509,7 +509,7 @@ class Machine { this.logger.debug(`Flow update: ${value} at ${position} from ${context.childName || 'child'}`); if (this.upstreamSource && this.downstreamSink) { - this._updateSourceSink(); + this.updateSourceSink(); } // Store in parent's measurement container @@ -521,7 +521,7 @@ class Machine { } } - _updateSourceSink() { + updateSourceSink() { // Handles flow according to the configured "flow number" this.logger.debug(`Updating source-sink pair: ${this.upstreamSource.config.functionality.softwareType} - ${this.downstreamSink.config.functionality.softwareType}`); this.downstreamSink.setInfluent = this.upstreamSource.getEffluent[this.config.flowNumber]; From ccfa90394bf00ee6f7ca071d5389a7fa2c576bb6 Mon Sep 17 00:00:00 2001 From: Rene De Ren Date: Wed, 11 Mar 2026 13:39:57 +0100 Subject: [PATCH 09/13] Fix ESLint errors and bugs Co-Authored-By: Claude Opus 4.6 --- src/nodeClass.js | 20 ++++++---- src/specificClass.js | 95 +++++++++++++++----------------------------- 2 files changed, 45 insertions(+), 70 deletions(-) diff --git a/src/nodeClass.js b/src/nodeClass.js index 6c73b48..842c3a6 100644 --- a/src/nodeClass.js +++ b/src/nodeClass.js @@ -190,7 +190,7 @@ class nodeClass { } return status; } catch (error) { - node.error("Error in updateNodeStatus: " + error.message); + this.node.error("Error in updateNodeStatus: " + error.message); return { fill: "red", shape: "ring", text: "Status Error" }; } } @@ -246,32 +246,36 @@ class nodeClass { /* Update to complete event based node by putting the tick function after an input event */ const m = this.source; switch(msg.topic) { - case 'registerChild': + case 'registerChild': { // Register this node as a child of the parent node const childId = msg.payload; - const childObj = this.RED.nodes.getNode(childId); + const childObj = this.RED.nodes.getNode(childId); m.childRegistrationUtils.registerChild(childObj.source ,msg.positionVsParent); break; + } case 'setMode': m.setMode(msg.payload); break; - case 'execSequence': + case 'execSequence': { const { source, action, parameter } = msg.payload; m.handleInput(source, action, parameter); break; - case 'execMovement': + } + case 'execMovement': { const { source: mvSource, action: mvAction, setpoint } = msg.payload; m.handleInput(mvSource, mvAction, Number(setpoint)); break; - case 'flowMovement': + } + case 'flowMovement': { const { source: fmSource, action: fmAction, setpoint: fmSetpoint } = msg.payload; m.handleInput(fmSource, fmAction, Number(fmSetpoint)); - break; - case 'emergencystop': + } + case 'emergencystop': { const { source: esSource, action: esAction } = msg.payload; m.handleInput(esSource, esAction); break; + } case 'showWorkingCurves': m.showWorkingCurves(); send({ topic : "Showing curve" , payload: m.showWorkingCurves() }); diff --git a/src/specificClass.js b/src/specificClass.js index b80f66b..2d7ce86 100644 --- a/src/specificClass.js +++ b/src/specificClass.js @@ -101,9 +101,11 @@ class Machine { //assume standard atm pressure is at sea level this.measurements.type('atmPressure').variant('measured').position('atEquipment').value(101325).unit('Pa'); //populate min and max - const flowunit = this.config.general.unit; - this.measurements.type('flow').variant('predicted').position('max').value(this.predictFlow.currentFxyYMax, Date.now() , flowunit) - this.measurements.type('flow').variant('predicted').position('min').value(this.predictFlow.currentFxyYMin).unit(this.config.general.unit); + if (this.predictFlow) { + const flowunit = this.config.general.unit; + this.measurements.type('flow').variant('predicted').position('max').value(this.predictFlow.currentFxyYMax, Date.now() , flowunit); + this.measurements.type('flow').variant('predicted').position('min').value(this.predictFlow.currentFxyYMin).unit(this.config.general.unit); + } } _updateState(){ @@ -143,48 +145,6 @@ class Machine { //rebuild to measurementype.variant no position and then switch based on values not strings or names. const eventName = `${measurementType}.measured.${position}`; - this.logger.debug(`Setting up listener for ${eventName} from child ${child.config.general.name}`); - // Register event listener for measurement updates - child.measurements.emitter.on(eventName, (eventData) => { - this.logger.debug(`🔄 ${position} ${measurementType} from ${eventData.childName}: ${eventData.value} ${eventData.unit}`); - - - this.logger.debug(` Emitting... ${eventName} with data:`); - // Store directly in parent's measurement container - this.measurements - .type(measurementType) - .variant("measured") - .position(position) - .value(eventData.value, eventData.timestamp, eventData.unit); - - // Call the appropriate handler - this._callMeasurementHandler(measurementType, eventData.value, position, eventData); - }); - } - } - -// Centralized handler dispatcher -_callMeasurementHandler(measurementType, value, position, context) { - switch (measurementType) { - case 'pressure': - this.updateMeasuredPressure(value, position, context); - break; - - case 'flow': - this.updateMeasuredFlow(value, position, context); - break; - - case 'temperature': - this.updateMeasuredTemperature(value, position, context); - break; - - default: - this.logger.warn(`No handler for measurement type: ${measurementType}`); - // Generic handler - just update position - this.updatePosition(); - break; - } -} this.logger.debug(`Setting up listener for ${eventName} from child ${measurementChild.config.general.name}`); // Register event listener for measurement updates measurementChild.measurements.emitter.on(eventName, (eventData) => { @@ -195,26 +155,36 @@ _callMeasurementHandler(measurementType, value, position, context) { .type(measurementType) .variant("measured") .position(position) - .value(eventData.value, eventData.timestamp, eventData.unit); - + .value(eventData.value, eventData.timestamp, eventData.unit); + // Call the appropriate handler - switch (measurementType) { - case 'pressure': - this.updateMeasuredPressure(eventData.value, position, eventData); - break; - - case 'flow': - this.updateMeasuredFlow(eventData.value, position, eventData); - break; - - default: - this.logger.warn(`No handler for measurement type: ${measurementType}`); - // Generic handler - just update position - this.updatePosition(); - } + this._callMeasurementHandler(measurementType, eventData.value, position, eventData); }); } + // Centralized handler dispatcher + _callMeasurementHandler(measurementType, value, position, context) { + switch (measurementType) { + case 'pressure': + this.updateMeasuredPressure(value, position, context); + break; + + case 'flow': + this.updateMeasuredFlow(value, position, context); + break; + + case 'temperature': + this.updateMeasuredTemperature(value, position, context); + break; + + default: + this.logger.warn(`No handler for measurement type: ${measurementType}`); + // Generic handler - just update position + this.updatePosition(); + break; + } + } + _connectReactor(reactorChild) { this.downstreamSink = reactorChild; // downstream from the pumps perpective } @@ -304,11 +274,12 @@ _callMeasurementHandler(measurementType, value, position, context) { case "exitmaintenance": return await this.executeSequence(parameter); - case "flowmovement": + case "flowmovement": { // Calculate the control value for a desired flow const pos = this.calcCtrl(parameter); // Move to the desired setpoint return await this.setpoint(pos); + } case "emergencystop": this.logger.warn(`Emergency stop activated by '${source}'.`); From 46dd2ca37a3a089e4cf0442bf65dc0053fd06723 Mon Sep 17 00:00:00 2001 From: Rene De Ren Date: Wed, 11 Mar 2026 14:59:35 +0100 Subject: [PATCH 10/13] Migrate _loadConfig to use ConfigManager.buildConfig() Replaces manual base config construction with shared buildConfig() method. Node now only specifies domain-specific config sections. Part of #1: Extract base config schema Co-Authored-By: Claude Opus 4.6 --- src/nodeClass.js | 27 ++++----------------------- 1 file changed, 4 insertions(+), 23 deletions(-) diff --git a/src/nodeClass.js b/src/nodeClass.js index 842c3a6..53f8dc0 100644 --- a/src/nodeClass.js +++ b/src/nodeClass.js @@ -41,31 +41,12 @@ class nodeClass { * @param {object} uiConfig - Raw config from Node-RED UI. */ _loadConfig(uiConfig,node) { + const cfgMgr = new configManager(); - // Merge UI config over defaults - this.config = { - general: { - id: node.id, // node.id is for the child registration process - unit: uiConfig.unit, // add converter options later to convert to default units (need like a model that defines this which units we are going to use and then conver to those standards) - logging: { - enabled: uiConfig.enableLog, - logLevel: uiConfig.logLevel - } - }, - asset: { - uuid: uiConfig.assetUuid, //need to add this later to the asset model - tagCode: uiConfig.assetTagCode, //need to add this later to the asset model - supplier: uiConfig.supplier, - category: uiConfig.category, //add later to define as the software type - type: uiConfig.assetType, - model: uiConfig.model, - unit: uiConfig.unit - }, - functionality: { - positionVsParent: uiConfig.positionVsParent - }, + // Build config: base sections + rotatingMachine-specific domain config + this.config = cfgMgr.buildConfig(this.name, uiConfig, node.id, { flowNumber: uiConfig.flowNumber - }; + }); // Utility for formatting outputs this._output = new outputUtils(); From bb986c2dc8c1f12540460099524482fbeb399463 Mon Sep 17 00:00:00 2001 From: Rene De Ren Date: Wed, 11 Mar 2026 15:35:28 +0100 Subject: [PATCH 11/13] refactor: adopt POSITIONS constants and fix ESLint warnings Replace hardcoded position strings with POSITIONS.* constants. Prefix unused variables with _ to resolve no-unused-vars warnings. Fix no-prototype-builtins where applicable. Co-Authored-By: Claude Opus 4.6 --- src/nodeClass.js | 2 +- src/specificClass.js | 79 ++++++++++++++++++++++---------------------- 2 files changed, 40 insertions(+), 41 deletions(-) diff --git a/src/nodeClass.js b/src/nodeClass.js index 53f8dc0..6bd143a 100644 --- a/src/nodeClass.js +++ b/src/nodeClass.js @@ -223,7 +223,7 @@ class nodeClass { * Attach the node's input handler, routing control messages to the class. */ _attachInputHandler() { - this.node.on('input', (msg, send, done) => { + this.node.on('input', (msg, send, _done) => { /* Update to complete event based node by putting the tick function after an input event */ const m = this.source; switch(msg.topic) { diff --git a/src/specificClass.js b/src/specificClass.js index 2d7ce86..50a2e82 100644 --- a/src/specificClass.js +++ b/src/specificClass.js @@ -1,5 +1,5 @@ const EventEmitter = require('events'); -const {loadCurve,gravity,logger,configUtils,configManager,state, nrmse, MeasurementContainer, predict, interpolation , childRegistrationUtils,coolprop} = require('generalFunctions'); +const {loadCurve,gravity,logger,configUtils,configManager,state, nrmse, MeasurementContainer, predict, interpolation , childRegistrationUtils,coolprop, POSITIONS} = require('generalFunctions'); class Machine { @@ -97,9 +97,9 @@ class Machine { _init(){ //assume standard temperature is 20degrees - this.measurements.type('temperature').variant('measured').position('atEquipment').value(15).unit('C'); + this.measurements.type('temperature').variant('measured').position(POSITIONS.AT_EQUIPMENT).value(15).unit('C'); //assume standard atm pressure is at sea level - this.measurements.type('atmPressure').variant('measured').position('atEquipment').value(101325).unit('Pa'); + this.measurements.type('atmPressure').variant('measured').position(POSITIONS.AT_EQUIPMENT).value(101325).unit('Pa'); //populate min and max if (this.predictFlow) { const flowunit = this.config.general.unit; @@ -112,8 +112,8 @@ class Machine { const isOperational = this._isOperationalState(); if(!isOperational){ //overrule the last prediction this should be 0 now - this.measurements.type("flow").variant("predicted").position("downstream").value(0,Date.now(),this.config.general.unit); - this.measurements.type("flow").variant("predicted").position("atEquipment").value(0,Date.now(),this.config.general.unit); + this.measurements.type("flow").variant("predicted").position(POSITIONS.DOWNSTREAM).value(0,Date.now(),this.config.general.unit); + this.measurements.type("flow").variant("predicted").position(POSITIONS.AT_EQUIPMENT).value(0,Date.now(),this.config.general.unit); } } @@ -140,7 +140,6 @@ class Machine { _connectMeasurement(measurementChild) { const position = measurementChild.config.functionality.positionVsParent; - const distance = measurementChild.config.functionality.distanceVsParent || 0; const measurementType = measurementChild.config.asset.type; //rebuild to measurementype.variant no position and then switch based on values not strings or names. const eventName = `${measurementType}.measured.${position}`; @@ -194,8 +193,8 @@ class Machine { // Method to assess drift using errorMetrics assessDrift(measurement, processMin, processMax) { this.logger.debug(`Assessing drift for measurement: ${measurement} processMin: ${processMin} processMax: ${processMax}`); - const predictedMeasurement = this.measurements.type(measurement).variant("predicted").position("downstream").getAllValues().values; - const measuredMeasurement = this.measurements.type(measurement).variant("measured").position("downstream").getAllValues().values; + const predictedMeasurement = this.measurements.type(measurement).variant("predicted").position(POSITIONS.DOWNSTREAM).getAllValues().values; + const measuredMeasurement = this.measurements.type(measurement).variant("measured").position(POSITIONS.DOWNSTREAM).getAllValues().values; if (!predictedMeasurement || !measuredMeasurement) return null; @@ -372,23 +371,23 @@ class Machine { calcFlow(x) { if(this.hasCurve) { if (!this._isOperationalState()) { - this.measurements.type("flow").variant("predicted").position("downstream").value(0,Date.now(),this.config.general.unit); - this.measurements.type("flow").variant("predicted").position("atEquipment").value(0,Date.now(),this.config.general.unit); + this.measurements.type("flow").variant("predicted").position(POSITIONS.DOWNSTREAM).value(0,Date.now(),this.config.general.unit); + this.measurements.type("flow").variant("predicted").position(POSITIONS.AT_EQUIPMENT).value(0,Date.now(),this.config.general.unit); this.logger.debug(`Machine is not operational. Setting predicted flow to 0.`); return 0; } const cFlow = this.predictFlow.y(x); - this.measurements.type("flow").variant("predicted").position("downstream").value(cFlow,Date.now(),this.config.general.unit); - this.measurements.type("flow").variant("predicted").position("atEquipment").value(cFlow,Date.now(),this.config.general.unit); + this.measurements.type("flow").variant("predicted").position(POSITIONS.DOWNSTREAM).value(cFlow,Date.now(),this.config.general.unit); + this.measurements.type("flow").variant("predicted").position(POSITIONS.AT_EQUIPMENT).value(cFlow,Date.now(),this.config.general.unit); //this.logger.debug(`Calculated flow: ${cFlow} for pressure: ${this.getMeasuredPressure()} and position: ${x}`); return cFlow; } // If no curve data is available, log a warning and return 0 this.logger.warn(`No curve data available for flow calculation. Returning 0.`); - this.measurements.type("flow").variant("predicted").position("downstream").value(0, Date.now(),this.config.general.unit); - this.measurements.type("flow").variant("predicted").position("atEquipment").value(0, Date.now(),this.config.general.unit); + this.measurements.type("flow").variant("predicted").position(POSITIONS.DOWNSTREAM).value(0, Date.now(),this.config.general.unit); + this.measurements.type("flow").variant("predicted").position(POSITIONS.AT_EQUIPMENT).value(0, Date.now(),this.config.general.unit); return 0; } @@ -397,20 +396,20 @@ class Machine { calcPower(x) { if(this.hasCurve) { if (!this._isOperationalState()) { - this.measurements.type("power").variant("predicted").position('atEquipment').value(0); + this.measurements.type("power").variant("predicted").position(POSITIONS.AT_EQUIPMENT).value(0); this.logger.debug(`Machine is not operational. Setting predicted power to 0.`); return 0; } //this.predictPower.currentX = x; Decrepated const cPower = this.predictPower.y(x); - this.measurements.type("power").variant("predicted").position('atEquipment').value(cPower); + this.measurements.type("power").variant("predicted").position(POSITIONS.AT_EQUIPMENT).value(cPower); //this.logger.debug(`Calculated power: ${cPower} for pressure: ${this.getMeasuredPressure()} and position: ${x}`); return cPower; } // If no curve data is available, log a warning and return 0 this.logger.warn(`No curve data available for power calculation. Returning 0.`); - this.measurements.type("power").variant("predicted").position('atEquipment').value(0); + this.measurements.type("power").variant("predicted").position(POSITIONS.AT_EQUIPMENT).value(0); return 0; } @@ -428,7 +427,7 @@ class Machine { // If no curve data is available, log a warning and return 0 this.logger.warn(`No curve data available for power calculation. Returning 0.`); - this.measurements.type("power").variant("predicted").position('atEquipment').value(0); + this.measurements.type("power").variant("predicted").position(POSITIONS.AT_EQUIPMENT).value(0); return 0; } @@ -438,14 +437,14 @@ class Machine { if(this.hasCurve) { this.predictCtrl.currentX = x; const cCtrl = this.predictCtrl.y(x); - this.measurements.type("ctrl").variant("predicted").position('atEquipment').value(cCtrl); + this.measurements.type("ctrl").variant("predicted").position(POSITIONS.AT_EQUIPMENT).value(cCtrl); //this.logger.debug(`Calculated ctrl: ${cCtrl} for pressure: ${this.getMeasuredPressure()} and position: ${x}`); return cCtrl; } // If no curve data is available, log a warning and return 0 this.logger.warn(`No curve data available for control calculation. Returning 0.`); - this.measurements.type("ctrl").variant("predicted").position('atEquipment').value(0); + this.measurements.type("ctrl").variant("predicted").position(POSITIONS.AT_EQUIPMENT).value(0); return 0; } @@ -477,7 +476,7 @@ class Machine { } // get downstream - const downstreamPressure = this.measurements.type('pressure').variant('measured').position('downstream').getCurrentValue(); + const downstreamPressure = this.measurements.type('pressure').variant('measured').position(POSITIONS.DOWNSTREAM).getCurrentValue(); // Only downstream => use it, warn that it's partial if (downstreamPressure != null) { @@ -531,7 +530,7 @@ class Machine { } // get - const upstreamFlow = this.measurements.type('flow').variant('measured').position('upstream').getCurrentValue(); + const upstreamFlow = this.measurements.type('flow').variant('measured').position(POSITIONS.UPSTREAM).getCurrentValue(); // Only upstream => might still accept it, but warn if (upstreamFlow != null) { @@ -540,7 +539,7 @@ class Machine { } // get - const downstreamFlow = this.measurements.type('flow').variant('measured').position('downstream').getCurrentValue(); + const downstreamFlow = this.measurements.type('flow').variant('measured').position(POSITIONS.DOWNSTREAM).getCurrentValue(); // Only downstream => might still accept it, but warn if (downstreamFlow != null) { @@ -554,7 +553,7 @@ class Machine { } handleMeasuredPower() { - const power = this.measurements.type("power").variant("measured").position("atEquipment").getCurrentValue(); + const power = this.measurements.type("power").variant("measured").position(POSITIONS.AT_EQUIPMENT).getCurrentValue(); // If your system calls it "upstream" or just a single "value", adjust accordingly if (power != null) { @@ -600,8 +599,8 @@ class Machine { // Update predicted flow if you have prediction capability if (this.predictFlow) { - this.measurements.type("flow").variant("predicted").position("downstream").value(this.predictFlow.outputY || 0); - this.measurements.type("flow").variant("predicted").position("atEquipment").value(this.predictFlow.outputY || 0); + this.measurements.type("flow").variant("predicted").position(POSITIONS.DOWNSTREAM).value(this.predictFlow.outputY || 0); + this.measurements.type("flow").variant("predicted").position(POSITIONS.AT_EQUIPMENT).value(this.predictFlow.outputY || 0); } } @@ -732,35 +731,35 @@ class Machine { const pressureDiff = this.measurements.type('pressure').variant('measured').difference('Pa'); const g = gravity.getStandardGravity(); - const temp = this.measurements.type('temperature').variant('measured').position('atEquipment').getCurrentValue('K'); - const atmPressure = this.measurements.type('atmPressure').variant('measured').position('atEquipment').getCurrentValue('Pa'); + const temp = this.measurements.type('temperature').variant('measured').position(POSITIONS.AT_EQUIPMENT).getCurrentValue('K'); + const atmPressure = this.measurements.type('atmPressure').variant('measured').position(POSITIONS.AT_EQUIPMENT).getCurrentValue('Pa'); console.log(`--------------------calc efficiency : Pressure diff:${pressureDiff},${temp}, ${g} `); const rho = coolprop.PropsSI('D', 'T', temp, 'P', atmPressure, 'WasteWater'); this.logger.debug(`temp: ${temp} atmPressure : ${atmPressure} rho : ${rho} pressureDiff: ${pressureDiff?.value || 0}`); - const flowM3s = this.measurements.type('flow').variant('predicted').position('atEquipment').getCurrentValue('m3/s'); - const powerWatt = this.measurements.type('power').variant('predicted').position('atEquipment').getCurrentValue('W'); + const flowM3s = this.measurements.type('flow').variant('predicted').position(POSITIONS.AT_EQUIPMENT).getCurrentValue('m3/s'); + const powerWatt = this.measurements.type('power').variant('predicted').position(POSITIONS.AT_EQUIPMENT).getCurrentValue('W'); this.logger.debug(`Flow : ${flowM3s} power: ${powerWatt}`); if (power != 0 && flow != 0) { const specificFlow = flow / power; const specificEnergyConsumption = power / flow; - this.measurements.type("efficiency").variant(variant).position('atEquipment').value(specificFlow); - this.measurements.type("specificEnergyConsumption").variant(variant).position('atEquipment').value(specificEnergyConsumption); + this.measurements.type("efficiency").variant(variant).position(POSITIONS.AT_EQUIPMENT).value(specificFlow); + this.measurements.type("specificEnergyConsumption").variant(variant).position(POSITIONS.AT_EQUIPMENT).value(specificEnergyConsumption); if(pressureDiff?.value != null && flowM3s != null && powerWatt != null){ const meterPerBar = pressureDiff.value / rho * g; const nHydraulicEfficiency = rho * g * flowM3s * (pressureDiff.value * meterPerBar ) / powerWatt; - this.measurements.type("nHydraulicEfficiency").variant(variant).position('atEquipment').value(nHydraulicEfficiency); + this.measurements.type("nHydraulicEfficiency").variant(variant).position(POSITIONS.AT_EQUIPMENT).value(nHydraulicEfficiency); } } //change this to nhydrefficiency ? - return this.measurements.type("efficiency").variant(variant).position('atEquipment').getCurrentValue(); + return this.measurements.type("efficiency").variant(variant).position(POSITIONS.AT_EQUIPMENT).getCurrentValue(); } @@ -860,7 +859,7 @@ const PT1 = new Child(config={ }, functionality:{ softwareType:"measurement", - positionVsParent:"upstream", + positionVsParent: POSITIONS.UPSTREAM, }, asset:{ supplier:"Vega", @@ -882,7 +881,7 @@ const PT2 = new Child(config={ }, functionality:{ softwareType:"measurement", - positionVsParent:"upstream", + positionVsParent: POSITIONS.UPSTREAM, }, asset:{ supplier:"Vega", @@ -936,8 +935,8 @@ 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"); +machine.childRegistrationUtils.registerChild(PT1, POSITIONS.UPSTREAM); +machine.childRegistrationUtils.registerChild(PT2, POSITIONS.DOWNSTREAM); //feed curve to the machine class //machine.updateCurve(curve["machineCurves"]["Hydrostal"]["H05K-S03R+HGM1X-X280KO"]); @@ -950,8 +949,8 @@ 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); +machine.measurements.type("pressure").variant("measured").position(POSITIONS.UPSTREAM).value(-200); +machine.measurements.type("pressure").variant("measured").position(POSITIONS.DOWNSTREAM).value(1000); testingSequences(); From 7b9fdd7342a2f7407b5b3bc03310a1693750035c Mon Sep 17 00:00:00 2001 From: Rene De Ren Date: Thu, 12 Mar 2026 09:33:28 +0100 Subject: [PATCH 12/13] fix: correct logging config path and child registration ID MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixed eneableLog typo accessing wrong config path — now uses machineConfig.general.logging.enabled/logLevel. Changed _registerChild to use this.node.id consistent with all other nodes. Removed debug console.log. Co-Authored-By: Claude Opus 4.6 --- src/nodeClass.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/nodeClass.js b/src/nodeClass.js index 6bd143a..775f452 100644 --- a/src/nodeClass.js +++ b/src/nodeClass.js @@ -58,14 +58,12 @@ class nodeClass { _setupSpecificClass(uiConfig) { const machineConfig = this.config; - console.log(`----------------> Loaded movementMode in nodeClass: ${uiConfig.movementMode}`); - // need extra state for this const stateConfig = { general: { logging: { - enabled: machineConfig.eneableLog, - logLevel: machineConfig.logLevel + enabled: machineConfig.general.logging.enabled, + logLevel: machineConfig.general.logging.logLevel } }, movement: { @@ -184,7 +182,7 @@ class nodeClass { this.node.send([ null, null, - { topic: 'registerChild', payload: this.config.general.id, positionVsParent: this.config?.functionality?.positionVsParent || 'atEquipment' }, + { topic: 'registerChild', payload: this.node.id, positionVsParent: this.config?.functionality?.positionVsParent || 'atEquipment' }, ]); }, 100); } From 4cf46f33c94af22a3107b6f288068ab5ce66e199 Mon Sep 17 00:00:00 2001 From: Rene De Ren Date: Thu, 12 Mar 2026 16:39:25 +0100 Subject: [PATCH 13/13] Expose output format selectors in editor --- rotatingMachine.html | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/rotatingMachine.html b/rotatingMachine.html index 28e210c..caf67ec 100644 --- a/rotatingMachine.html +++ b/rotatingMachine.html @@ -26,6 +26,8 @@ cooldown: { value: 0 }, movementMode : { value: "staticspeed" }, // static or dynamic machineCurve : { value: {}}, + processOutputFormat: { value: "process" }, + dbaseOutputFormat: { value: "influxdb" }, //define asset properties uuid: { value: "" }, @@ -143,6 +145,24 @@ +

Output Formats

+
+ + +
+
+ + +
+
@@ -162,4 +182,4 @@
  • Enable Log / Log Level: toggle via Logger menu.
  • Position: set Upstream / At Equipment / Downstream via Position menu.
  • - \ No newline at end of file +