Compare commits

...

5 Commits

Author SHA1 Message Date
Rene De Ren
4cf46f33c9 Expose output format selectors in editor 2026-03-12 16:39:25 +01:00
Rene De Ren
7b9fdd7342 fix: correct logging config path and child registration ID
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 <noreply@anthropic.com>
2026-03-12 09:33:28 +01:00
Rene De Ren
bb986c2dc8 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 <noreply@anthropic.com>
2026-03-11 15:35:28 +01:00
Rene De Ren
46dd2ca37a 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 <noreply@anthropic.com>
2026-03-11 14:59:35 +01:00
Rene De Ren
ccfa90394b Fix ESLint errors and bugs
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 13:39:57 +01:00
3 changed files with 113 additions and 140 deletions

View File

@@ -26,6 +26,8 @@
cooldown: { value: 0 }, cooldown: { value: 0 },
movementMode : { value: "staticspeed" }, // static or dynamic movementMode : { value: "staticspeed" }, // static or dynamic
machineCurve : { value: {}}, machineCurve : { value: {}},
processOutputFormat: { value: "process" },
dbaseOutputFormat: { value: "influxdb" },
//define asset properties //define asset properties
uuid: { value: "" }, uuid: { value: "" },
@@ -143,6 +145,24 @@
</select> </select>
</div> </div>
<h3>Output Formats</h3>
<div class="form-row">
<label for="node-input-processOutputFormat"><i class="fa fa-random"></i> Process Output</label>
<select id="node-input-processOutputFormat" style="width:60%;">
<option value="process">process</option>
<option value="json">json</option>
<option value="csv">csv</option>
</select>
</div>
<div class="form-row">
<label for="node-input-dbaseOutputFormat"><i class="fa fa-database"></i> Database Output</label>
<select id="node-input-dbaseOutputFormat" style="width:60%;">
<option value="influxdb">influxdb</option>
<option value="json">json</option>
<option value="csv">csv</option>
</select>
</div>
<!-- Asset fields injected here --> <!-- Asset fields injected here -->
<div id="asset-fields-placeholder"></div> <div id="asset-fields-placeholder"></div>
@@ -162,4 +182,4 @@
<li><b>Enable Log / Log Level:</b> toggle via Logger menu.</li> <li><b>Enable Log / Log Level:</b> toggle via Logger menu.</li>
<li><b>Position:</b> set Upstream / At Equipment / Downstream via Position menu.</li> <li><b>Position:</b> set Upstream / At Equipment / Downstream via Position menu.</li>
</ul> </ul>
</script> </script>

View File

@@ -41,31 +41,12 @@ class nodeClass {
* @param {object} uiConfig - Raw config from Node-RED UI. * @param {object} uiConfig - Raw config from Node-RED UI.
*/ */
_loadConfig(uiConfig,node) { _loadConfig(uiConfig,node) {
const cfgMgr = new configManager();
// Merge UI config over defaults // Build config: base sections + rotatingMachine-specific domain config
this.config = { this.config = cfgMgr.buildConfig(this.name, uiConfig, node.id, {
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
},
flowNumber: uiConfig.flowNumber flowNumber: uiConfig.flowNumber
}; });
// Utility for formatting outputs // Utility for formatting outputs
this._output = new outputUtils(); this._output = new outputUtils();
@@ -77,14 +58,12 @@ class nodeClass {
_setupSpecificClass(uiConfig) { _setupSpecificClass(uiConfig) {
const machineConfig = this.config; const machineConfig = this.config;
console.log(`----------------> Loaded movementMode in nodeClass: ${uiConfig.movementMode}`);
// need extra state for this // need extra state for this
const stateConfig = { const stateConfig = {
general: { general: {
logging: { logging: {
enabled: machineConfig.eneableLog, enabled: machineConfig.general.logging.enabled,
logLevel: machineConfig.logLevel logLevel: machineConfig.general.logging.logLevel
} }
}, },
movement: { movement: {
@@ -190,7 +169,7 @@ class nodeClass {
} }
return status; return status;
} catch (error) { } 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" }; return { fill: "red", shape: "ring", text: "Status Error" };
} }
} }
@@ -203,7 +182,7 @@ class nodeClass {
this.node.send([ this.node.send([
null, null,
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); }, 100);
} }
@@ -242,36 +221,40 @@ class nodeClass {
* Attach the node's input handler, routing control messages to the class. * Attach the node's input handler, routing control messages to the class.
*/ */
_attachInputHandler() { _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 */ /* Update to complete event based node by putting the tick function after an input event */
const m = this.source; const m = this.source;
switch(msg.topic) { switch(msg.topic) {
case 'registerChild': case 'registerChild': {
// Register this node as a child of the parent node // Register this node as a child of the parent node
const childId = msg.payload; 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); m.childRegistrationUtils.registerChild(childObj.source ,msg.positionVsParent);
break; break;
}
case 'setMode': case 'setMode':
m.setMode(msg.payload); m.setMode(msg.payload);
break; break;
case 'execSequence': case 'execSequence': {
const { source, action, parameter } = msg.payload; const { source, action, parameter } = msg.payload;
m.handleInput(source, action, parameter); m.handleInput(source, action, parameter);
break; break;
case 'execMovement': }
case 'execMovement': {
const { source: mvSource, action: mvAction, setpoint } = msg.payload; const { source: mvSource, action: mvAction, setpoint } = msg.payload;
m.handleInput(mvSource, mvAction, Number(setpoint)); m.handleInput(mvSource, mvAction, Number(setpoint));
break; break;
case 'flowMovement': }
case 'flowMovement': {
const { source: fmSource, action: fmAction, setpoint: fmSetpoint } = msg.payload; const { source: fmSource, action: fmAction, setpoint: fmSetpoint } = msg.payload;
m.handleInput(fmSource, fmAction, Number(fmSetpoint)); m.handleInput(fmSource, fmAction, Number(fmSetpoint));
break; break;
case 'emergencystop': }
case 'emergencystop': {
const { source: esSource, action: esAction } = msg.payload; const { source: esSource, action: esAction } = msg.payload;
m.handleInput(esSource, esAction); m.handleInput(esSource, esAction);
break; break;
}
case 'showWorkingCurves': case 'showWorkingCurves':
m.showWorkingCurves(); m.showWorkingCurves();
send({ topic : "Showing curve" , payload: m.showWorkingCurves() }); send({ topic : "Showing curve" , payload: m.showWorkingCurves() });

View File

@@ -1,5 +1,5 @@
const EventEmitter = require('events'); 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 { class Machine {
@@ -97,21 +97,23 @@ class Machine {
_init(){ _init(){
//assume standard temperature is 20degrees //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 //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 //populate min and max
const flowunit = this.config.general.unit; if (this.predictFlow) {
this.measurements.type('flow').variant('predicted').position('max').value(this.predictFlow.currentFxyYMax, Date.now() , flowunit) const flowunit = this.config.general.unit;
this.measurements.type('flow').variant('predicted').position('min').value(this.predictFlow.currentFxyYMin).unit(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(){ _updateState(){
const isOperational = this._isOperationalState(); const isOperational = this._isOperationalState();
if(!isOperational){ if(!isOperational){
//overrule the last prediction this should be 0 now //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(POSITIONS.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.AT_EQUIPMENT).value(0,Date.now(),this.config.general.unit);
} }
} }
@@ -138,53 +140,10 @@ class Machine {
_connectMeasurement(measurementChild) { _connectMeasurement(measurementChild) {
const position = measurementChild.config.functionality.positionVsParent; const position = measurementChild.config.functionality.positionVsParent;
const distance = measurementChild.config.functionality.distanceVsParent || 0;
const measurementType = measurementChild.config.asset.type; const measurementType = measurementChild.config.asset.type;
//rebuild to measurementype.variant no position and then switch based on values not strings or names. //rebuild to measurementype.variant no position and then switch based on values not strings or names.
const eventName = `${measurementType}.measured.${position}`; 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}`); this.logger.debug(`Setting up listener for ${eventName} from child ${measurementChild.config.general.name}`);
// Register event listener for measurement updates // Register event listener for measurement updates
measurementChild.measurements.emitter.on(eventName, (eventData) => { measurementChild.measurements.emitter.on(eventName, (eventData) => {
@@ -195,26 +154,36 @@ _callMeasurementHandler(measurementType, value, position, context) {
.type(measurementType) .type(measurementType)
.variant("measured") .variant("measured")
.position(position) .position(position)
.value(eventData.value, eventData.timestamp, eventData.unit); .value(eventData.value, eventData.timestamp, eventData.unit);
// Call the appropriate handler // Call the appropriate handler
switch (measurementType) { this._callMeasurementHandler(measurementType, eventData.value, position, eventData);
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();
}
}); });
} }
// 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) { _connectReactor(reactorChild) {
this.downstreamSink = reactorChild; // downstream from the pumps perpective this.downstreamSink = reactorChild; // downstream from the pumps perpective
} }
@@ -224,8 +193,8 @@ _callMeasurementHandler(measurementType, value, position, context) {
// Method to assess drift using errorMetrics // Method to assess drift using errorMetrics
assessDrift(measurement, processMin, processMax) { assessDrift(measurement, processMin, processMax) {
this.logger.debug(`Assessing drift for measurement: ${measurement} processMin: ${processMin} processMax: ${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 predictedMeasurement = this.measurements.type(measurement).variant("predicted").position(POSITIONS.DOWNSTREAM).getAllValues().values;
const measuredMeasurement = this.measurements.type(measurement).variant("measured").position("downstream").getAllValues().values; const measuredMeasurement = this.measurements.type(measurement).variant("measured").position(POSITIONS.DOWNSTREAM).getAllValues().values;
if (!predictedMeasurement || !measuredMeasurement) return null; if (!predictedMeasurement || !measuredMeasurement) return null;
@@ -304,11 +273,12 @@ _callMeasurementHandler(measurementType, value, position, context) {
case "exitmaintenance": case "exitmaintenance":
return await this.executeSequence(parameter); return await this.executeSequence(parameter);
case "flowmovement": case "flowmovement": {
// Calculate the control value for a desired flow // Calculate the control value for a desired flow
const pos = this.calcCtrl(parameter); const pos = this.calcCtrl(parameter);
// Move to the desired setpoint // Move to the desired setpoint
return await this.setpoint(pos); return await this.setpoint(pos);
}
case "emergencystop": case "emergencystop":
this.logger.warn(`Emergency stop activated by '${source}'.`); this.logger.warn(`Emergency stop activated by '${source}'.`);
@@ -401,23 +371,23 @@ _callMeasurementHandler(measurementType, value, position, context) {
calcFlow(x) { calcFlow(x) {
if(this.hasCurve) { if(this.hasCurve) {
if (!this._isOperationalState()) { 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(POSITIONS.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.AT_EQUIPMENT).value(0,Date.now(),this.config.general.unit);
this.logger.debug(`Machine is not operational. Setting predicted flow to 0.`); this.logger.debug(`Machine is not operational. Setting predicted flow to 0.`);
return 0; return 0;
} }
const cFlow = this.predictFlow.y(x); 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(POSITIONS.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.AT_EQUIPMENT).value(cFlow,Date.now(),this.config.general.unit);
//this.logger.debug(`Calculated flow: ${cFlow} for pressure: ${this.getMeasuredPressure()} and position: ${x}`); //this.logger.debug(`Calculated flow: ${cFlow} for pressure: ${this.getMeasuredPressure()} and position: ${x}`);
return cFlow; return cFlow;
} }
// If no curve data is available, log a warning and return 0 // 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.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(POSITIONS.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.AT_EQUIPMENT).value(0, Date.now(),this.config.general.unit);
return 0; return 0;
} }
@@ -426,20 +396,20 @@ _callMeasurementHandler(measurementType, value, position, context) {
calcPower(x) { calcPower(x) {
if(this.hasCurve) { if(this.hasCurve) {
if (!this._isOperationalState()) { 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.`); this.logger.debug(`Machine is not operational. Setting predicted power to 0.`);
return 0; return 0;
} }
//this.predictPower.currentX = x; Decrepated //this.predictPower.currentX = x; Decrepated
const cPower = this.predictPower.y(x); 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}`); //this.logger.debug(`Calculated power: ${cPower} for pressure: ${this.getMeasuredPressure()} and position: ${x}`);
return cPower; return cPower;
} }
// If no curve data is available, log a warning and return 0 // 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.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; return 0;
} }
@@ -457,7 +427,7 @@ _callMeasurementHandler(measurementType, value, position, context) {
// If no curve data is available, log a warning and return 0 // 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.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; return 0;
} }
@@ -467,14 +437,14 @@ _callMeasurementHandler(measurementType, value, position, context) {
if(this.hasCurve) { if(this.hasCurve) {
this.predictCtrl.currentX = x; this.predictCtrl.currentX = x;
const cCtrl = this.predictCtrl.y(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}`); //this.logger.debug(`Calculated ctrl: ${cCtrl} for pressure: ${this.getMeasuredPressure()} and position: ${x}`);
return cCtrl; return cCtrl;
} }
// If no curve data is available, log a warning and return 0 // 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.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; return 0;
} }
@@ -506,7 +476,7 @@ _callMeasurementHandler(measurementType, value, position, context) {
} }
// get downstream // 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 // Only downstream => use it, warn that it's partial
if (downstreamPressure != null) { if (downstreamPressure != null) {
@@ -560,7 +530,7 @@ _callMeasurementHandler(measurementType, value, position, context) {
} }
// get // 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 // Only upstream => might still accept it, but warn
if (upstreamFlow != null) { if (upstreamFlow != null) {
@@ -569,7 +539,7 @@ _callMeasurementHandler(measurementType, value, position, context) {
} }
// get // 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 // Only downstream => might still accept it, but warn
if (downstreamFlow != null) { if (downstreamFlow != null) {
@@ -583,7 +553,7 @@ _callMeasurementHandler(measurementType, value, position, context) {
} }
handleMeasuredPower() { 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 your system calls it "upstream" or just a single "value", adjust accordingly
if (power != null) { if (power != null) {
@@ -629,8 +599,8 @@ _callMeasurementHandler(measurementType, value, position, context) {
// Update predicted flow if you have prediction capability // Update predicted flow if you have prediction capability
if (this.predictFlow) { if (this.predictFlow) {
this.measurements.type("flow").variant("predicted").position("downstream").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("atEquipment").value(this.predictFlow.outputY || 0); this.measurements.type("flow").variant("predicted").position(POSITIONS.AT_EQUIPMENT).value(this.predictFlow.outputY || 0);
} }
} }
@@ -761,35 +731,35 @@ _callMeasurementHandler(measurementType, value, position, context) {
const pressureDiff = this.measurements.type('pressure').variant('measured').difference('Pa'); const pressureDiff = this.measurements.type('pressure').variant('measured').difference('Pa');
const g = gravity.getStandardGravity(); const g = gravity.getStandardGravity();
const temp = this.measurements.type('temperature').variant('measured').position('atEquipment').getCurrentValue('K'); const temp = this.measurements.type('temperature').variant('measured').position(POSITIONS.AT_EQUIPMENT).getCurrentValue('K');
const atmPressure = this.measurements.type('atmPressure').variant('measured').position('atEquipment').getCurrentValue('Pa'); const atmPressure = this.measurements.type('atmPressure').variant('measured').position(POSITIONS.AT_EQUIPMENT).getCurrentValue('Pa');
console.log(`--------------------calc efficiency : Pressure diff:${pressureDiff},${temp}, ${g} `); console.log(`--------------------calc efficiency : Pressure diff:${pressureDiff},${temp}, ${g} `);
const rho = coolprop.PropsSI('D', 'T', temp, 'P', atmPressure, 'WasteWater'); const rho = coolprop.PropsSI('D', 'T', temp, 'P', atmPressure, 'WasteWater');
this.logger.debug(`temp: ${temp} atmPressure : ${atmPressure} rho : ${rho} pressureDiff: ${pressureDiff?.value || 0}`); 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 flowM3s = this.measurements.type('flow').variant('predicted').position(POSITIONS.AT_EQUIPMENT).getCurrentValue('m3/s');
const powerWatt = this.measurements.type('power').variant('predicted').position('atEquipment').getCurrentValue('W'); const powerWatt = this.measurements.type('power').variant('predicted').position(POSITIONS.AT_EQUIPMENT).getCurrentValue('W');
this.logger.debug(`Flow : ${flowM3s} power: ${powerWatt}`); this.logger.debug(`Flow : ${flowM3s} power: ${powerWatt}`);
if (power != 0 && flow != 0) { if (power != 0 && flow != 0) {
const specificFlow = flow / power; const specificFlow = flow / power;
const specificEnergyConsumption = power / flow; const specificEnergyConsumption = power / flow;
this.measurements.type("efficiency").variant(variant).position('atEquipment').value(specificFlow); this.measurements.type("efficiency").variant(variant).position(POSITIONS.AT_EQUIPMENT).value(specificFlow);
this.measurements.type("specificEnergyConsumption").variant(variant).position('atEquipment').value(specificEnergyConsumption); this.measurements.type("specificEnergyConsumption").variant(variant).position(POSITIONS.AT_EQUIPMENT).value(specificEnergyConsumption);
if(pressureDiff?.value != null && flowM3s != null && powerWatt != null){ if(pressureDiff?.value != null && flowM3s != null && powerWatt != null){
const meterPerBar = pressureDiff.value / rho * g; const meterPerBar = pressureDiff.value / rho * g;
const nHydraulicEfficiency = rho * g * flowM3s * (pressureDiff.value * meterPerBar ) / powerWatt; 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 ? //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();
} }
@@ -889,7 +859,7 @@ const PT1 = new Child(config={
}, },
functionality:{ functionality:{
softwareType:"measurement", softwareType:"measurement",
positionVsParent:"upstream", positionVsParent: POSITIONS.UPSTREAM,
}, },
asset:{ asset:{
supplier:"Vega", supplier:"Vega",
@@ -911,7 +881,7 @@ const PT2 = new Child(config={
}, },
functionality:{ functionality:{
softwareType:"measurement", softwareType:"measurement",
positionVsParent:"upstream", positionVsParent: POSITIONS.UPSTREAM,
}, },
asset:{ asset:{
supplier:"Vega", supplier:"Vega",
@@ -965,8 +935,8 @@ const machine = new Machine(machineConfig, stateConfig);
//machine.logger.info(JSON.stringify(curve["machineCurves"]["Hydrostal"]["H05K-S03R+HGM1X-X280KO"])); //machine.logger.info(JSON.stringify(curve["machineCurves"]["Hydrostal"]["H05K-S03R+HGM1X-X280KO"]));
machine.logger.info(`Registering child...`); machine.logger.info(`Registering child...`);
machine.childRegistrationUtils.registerChild(PT1, "upstream"); machine.childRegistrationUtils.registerChild(PT1, POSITIONS.UPSTREAM);
machine.childRegistrationUtils.registerChild(PT2, "downstream"); machine.childRegistrationUtils.registerChild(PT2, POSITIONS.DOWNSTREAM);
//feed curve to the machine class //feed curve to the machine class
//machine.updateCurve(curve["machineCurves"]["Hydrostal"]["H05K-S03R+HGM1X-X280KO"]); //machine.updateCurve(curve["machineCurves"]["Hydrostal"]["H05K-S03R+HGM1X-X280KO"]);
@@ -979,8 +949,8 @@ machine.getOutput();
//manual test //manual test
//machine.handleInput("parent", "execSequence", "startup"); //machine.handleInput("parent", "execSequence", "startup");
machine.measurements.type("pressure").variant("measured").position('upstream').value(-200); machine.measurements.type("pressure").variant("measured").position(POSITIONS.UPSTREAM).value(-200);
machine.measurements.type("pressure").variant("measured").position('downstream').value(1000); machine.measurements.type("pressure").variant("measured").position(POSITIONS.DOWNSTREAM).value(1000);
testingSequences(); testingSequences();