Compare commits
19 Commits
99b45c87e4
...
4cf46f33c9
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4cf46f33c9 | ||
|
|
7b9fdd7342 | ||
|
|
bb986c2dc8 | ||
|
|
46dd2ca37a | ||
|
|
ccfa90394b | ||
|
|
e236cccfd6 | ||
|
|
108d2e23ca | ||
|
|
446ef81f24 | ||
|
|
966ba06faa | ||
|
|
e8c96c4b1e | ||
|
|
f083e7596a | ||
|
|
6ca6e536a5 | ||
|
|
fb75fb8a11 | ||
|
|
6528c966d8 | ||
|
|
994cf641a3 | ||
|
|
6ae622b6bf | ||
|
|
4b5ec33c1d | ||
|
|
51f966cfb9 | ||
|
|
4ae6beba37 |
@@ -24,8 +24,10 @@
|
|||||||
warmup: { value: 0 },
|
warmup: { value: 0 },
|
||||||
shutdown: { value: 0 },
|
shutdown: { value: 0 },
|
||||||
cooldown: { value: 0 },
|
cooldown: { value: 0 },
|
||||||
machineCurve: { value: {}},
|
movementMode : { value: "staticspeed" }, // static or dynamic
|
||||||
flowNumber: { value: 1, required: true },
|
machineCurve : { value: {}},
|
||||||
|
processOutputFormat: { value: "process" },
|
||||||
|
dbaseOutputFormat: { value: "influxdb" },
|
||||||
|
|
||||||
//define asset properties
|
//define asset properties
|
||||||
uuid: { value: "" },
|
uuid: { value: "" },
|
||||||
@@ -55,7 +57,7 @@
|
|||||||
icon: "font-awesome/fa-cog",
|
icon: "font-awesome/fa-cog",
|
||||||
|
|
||||||
label: function () {
|
label: function () {
|
||||||
return this.positionIcon + " " + this.category.slice(0, -1) || "Machine";
|
return this.positionIcon + " " + this.category || "Machine";
|
||||||
},
|
},
|
||||||
|
|
||||||
oneditprepare: function() {
|
oneditprepare: function() {
|
||||||
@@ -75,6 +77,10 @@
|
|||||||
document.getElementById("node-input-warmup");
|
document.getElementById("node-input-warmup");
|
||||||
document.getElementById("node-input-shutdown");
|
document.getElementById("node-input-shutdown");
|
||||||
document.getElementById("node-input-cooldown");
|
document.getElementById("node-input-cooldown");
|
||||||
|
const movementMode = document.getElementById("node-input-movementMode");
|
||||||
|
if (movementMode) {
|
||||||
|
movementMode.value = this.movementMode || "staticspeed";
|
||||||
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
oneditsave: function() {
|
oneditsave: function() {
|
||||||
@@ -100,6 +106,9 @@
|
|||||||
node[field] = value;
|
node[field] = value;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
node.movementMode = document.getElementById("node-input-movementMode").value;
|
||||||
|
console.log(`----------------> Saving movementMode: ${node.movementMode}`);
|
||||||
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
@@ -129,8 +138,29 @@
|
|||||||
<input type="number" id="node-input-cooldown" style="width:60%;" />
|
<input type="number" id="node-input-cooldown" style="width:60%;" />
|
||||||
</div>
|
</div>
|
||||||
<div class="form-row">
|
<div class="form-row">
|
||||||
<label for="node-input-flowNumber"><i class="fa fa-clock-o"></i> Flow Number</label>
|
<label for="node-input-movementMode"><i class="fa fa-exchange"></i> Movement Mode</label>
|
||||||
<input type="number" id="node-input-flowNumber" style="width:60%;" />
|
<select id="node-input-movementMode" style="width:60%;">
|
||||||
|
<option value="staticspeed">Static</option>
|
||||||
|
<option value="dynspeed">Dynamic</option>
|
||||||
|
</select>
|
||||||
|
</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>
|
</div>
|
||||||
|
|
||||||
<!-- Asset fields injected here -->
|
<!-- Asset fields injected here -->
|
||||||
@@ -152,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>
|
||||||
|
|||||||
@@ -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();
|
||||||
@@ -81,12 +62,13 @@ class nodeClass {
|
|||||||
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: {
|
||||||
speed: Number(uiConfig.speed)
|
speed: Number(uiConfig.speed),
|
||||||
|
mode: uiConfig.movementMode
|
||||||
},
|
},
|
||||||
time: {
|
time: {
|
||||||
starting: Number(uiConfig.startup),
|
starting: Number(uiConfig.startup),
|
||||||
@@ -115,8 +97,8 @@ class nodeClass {
|
|||||||
try {
|
try {
|
||||||
const mode = m.currentMode;
|
const mode = m.currentMode;
|
||||||
const state = m.state.getCurrentState();
|
const state = m.state.getCurrentState();
|
||||||
const flow = Math.round(m.measurements.type("flow").variant("predicted").position('downstream').getCurrentValue());
|
const flow = Math.round(m.measurements.type("flow").variant("predicted").position('downstream').getCurrentValue('m3/h'));
|
||||||
const power = Math.round(m.measurements.type("power").variant("predicted").position('upstream').getCurrentValue());
|
const power = Math.round(m.measurements.type("power").variant("predicted").position('atequipment').getCurrentValue('kW'));
|
||||||
let symbolState;
|
let symbolState;
|
||||||
switch(state){
|
switch(state){
|
||||||
case "off":
|
case "off":
|
||||||
@@ -146,6 +128,9 @@ class nodeClass {
|
|||||||
case "decelerating":
|
case "decelerating":
|
||||||
symbolState = "⏪";
|
symbolState = "⏪";
|
||||||
break;
|
break;
|
||||||
|
case "maintenance":
|
||||||
|
symbolState = "🔧";
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
const position = m.state.getCurrentPosition();
|
const position = m.state.getCurrentPosition();
|
||||||
const roundedPosition = Math.round(position * 100) / 100;
|
const roundedPosition = Math.round(position * 100) / 100;
|
||||||
@@ -184,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" };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -197,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);
|
||||||
}
|
}
|
||||||
@@ -225,8 +210,8 @@ class nodeClass {
|
|||||||
//this.source.tick();
|
//this.source.tick();
|
||||||
|
|
||||||
const raw = this.source.getOutput();
|
const raw = this.source.getOutput();
|
||||||
const processMsg = this._output.formatMsg(raw, this.config, 'process');
|
const processMsg = this._output.formatMsg(raw, this.source.config, 'process');
|
||||||
const influxMsg = this._output.formatMsg(raw, this.config, 'influxdb');
|
const influxMsg = this._output.formatMsg(raw, this.source.config, 'influxdb');
|
||||||
|
|
||||||
// Send only updated outputs on ports 0 & 1
|
// Send only updated outputs on ports 0 & 1
|
||||||
this.node.send([processMsg, influxMsg]);
|
this.node.send([processMsg, influxMsg]);
|
||||||
@@ -236,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() });
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
const EventEmitter = require('events');
|
const EventEmitter = require('events');
|
||||||
const {loadCurve,logger,configUtils,configManager,state, nrmse, MeasurementContainer, predict, interpolation , childRegistrationUtils} = require('generalFunctions');
|
const {loadCurve,gravity,logger,configUtils,configManager,state, nrmse, MeasurementContainer, predict, interpolation , childRegistrationUtils,coolprop, POSITIONS} = require('generalFunctions');
|
||||||
const { name } = require('../../generalFunctions/src/convert/lodash/lodash._shimkeys');
|
|
||||||
|
|
||||||
class Machine {
|
class Machine {
|
||||||
|
|
||||||
@@ -17,7 +16,7 @@ class Machine {
|
|||||||
|
|
||||||
// Load a specific curve
|
// Load a specific curve
|
||||||
this.model = machineConfig.asset.model; // Get the model from the machineConfig
|
this.model = machineConfig.asset.model; // Get the model from the machineConfig
|
||||||
this.curve = this.model ? loadCurve(this.model) : null;
|
this.curve = this.model ? loadCurve(this.model) : null; // we need to convert the curve and add units to the curve information
|
||||||
|
|
||||||
//Init config and check if it is valid
|
//Init config and check if it is valid
|
||||||
this.config = this.configUtils.initConfig(machineConfig);
|
this.config = this.configUtils.initConfig(machineConfig);
|
||||||
@@ -35,10 +34,8 @@ class Machine {
|
|||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
this.hasCurve = true;
|
this.hasCurve = true;
|
||||||
this.config = this.configUtils.updateConfig(this.config, {
|
this.config = this.configUtils.updateConfig(this.config, { asset: { ...this.config.asset, machineCurve: this.curve } });
|
||||||
asset: { ...this.config.asset, machineCurve: this.curve }
|
//machineConfig = { ...machineConfig, asset: { ...machineConfig.asset, machineCurve: this.curve } }; // Merge curve into machineConfig
|
||||||
});
|
|
||||||
machineConfig = { ...machineConfig, asset: { ...machineConfig.asset, machineCurve: this.curve } }; // Merge curve into machineConfig
|
|
||||||
this.predictFlow = new predict({ curve: this.config.asset.machineCurve.nq }); // load nq (x : ctrl , y : flow relationship)
|
this.predictFlow = new predict({ curve: this.config.asset.machineCurve.nq }); // load nq (x : ctrl , y : flow relationship)
|
||||||
this.predictPower = new predict({ curve: this.config.asset.machineCurve.np }); // load np (x : ctrl , y : power relationship)
|
this.predictPower = new predict({ curve: this.config.asset.machineCurve.np }); // load np (x : ctrl , y : power relationship)
|
||||||
this.predictCtrl = new predict({ curve: this.reverseCurve(this.config.asset.machineCurve.nq) }); // load reversed nq (x: flow, y: ctrl relationship)
|
this.predictCtrl = new predict({ curve: this.reverseCurve(this.config.asset.machineCurve.nq) }); // load reversed nq (x: flow, y: ctrl relationship)
|
||||||
@@ -48,7 +45,17 @@ class Machine {
|
|||||||
this.errorMetrics = new nrmse(errorMetricsConfig, this.logger);
|
this.errorMetrics = new nrmse(errorMetricsConfig, this.logger);
|
||||||
|
|
||||||
// Initialize measurements
|
// Initialize measurements
|
||||||
this.measurements = new MeasurementContainer();
|
this.measurements = new MeasurementContainer({
|
||||||
|
autoConvert: true,
|
||||||
|
windowSize: 50,
|
||||||
|
defaultUnits: {
|
||||||
|
pressure: 'mbar',
|
||||||
|
flow: this.config.general.unit,
|
||||||
|
power: 'kW',
|
||||||
|
temperature: 'C'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
this.interpolation = new interpolation();
|
this.interpolation = new interpolation();
|
||||||
|
|
||||||
this.flowDrift = null;
|
this.flowDrift = null;
|
||||||
@@ -68,6 +75,16 @@ class Machine {
|
|||||||
this.updatePosition();
|
this.updatePosition();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
//When state changes look if we need to do other updates
|
||||||
|
this.state.emitter.on("stateChange", (newState) => {
|
||||||
|
this.logger.debug(`State change detected: ${newState}`);
|
||||||
|
this._updateState();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
//perform init for certain values
|
||||||
|
this._init();
|
||||||
|
|
||||||
// used for holding the source and sink unit operations or other object with setInfluent / getEffluent method for e.g. recirculation.
|
// used for holding the source and sink unit operations or other object with setInfluent / getEffluent method for e.g. recirculation.
|
||||||
this.upstreamSource = null;
|
this.upstreamSource = null;
|
||||||
this.downstreamSink = null;
|
this.downstreamSink = null;
|
||||||
@@ -75,6 +92,29 @@ class Machine {
|
|||||||
this.child = {}; // object to hold child information so we know on what to subscribe
|
this.child = {}; // object to hold child information so we know on what to subscribe
|
||||||
this.childRegistrationUtils = new childRegistrationUtils(this); // Child registration utility
|
this.childRegistrationUtils = new childRegistrationUtils(this); // Child registration utility
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
_init(){
|
||||||
|
//assume standard temperature is 20degrees
|
||||||
|
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(POSITIONS.AT_EQUIPMENT).value(101325).unit('Pa');
|
||||||
|
//populate min and max
|
||||||
|
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(){
|
||||||
|
const isOperational = this._isOperationalState();
|
||||||
|
if(!isOperational){
|
||||||
|
//overrule the last prediction this should be 0 now
|
||||||
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*------------------- Register child events -------------------*/
|
/*------------------- Register child events -------------------*/
|
||||||
@@ -100,7 +140,6 @@ 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}`;
|
||||||
@@ -115,26 +154,36 @@ class Machine {
|
|||||||
.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
|
||||||
}
|
}
|
||||||
@@ -144,8 +193,8 @@ class Machine {
|
|||||||
// 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;
|
||||||
|
|
||||||
@@ -176,43 +225,66 @@ class Machine {
|
|||||||
// -------- Mode and Input Management -------- //
|
// -------- Mode and Input Management -------- //
|
||||||
isValidSourceForMode(source, mode) {
|
isValidSourceForMode(source, mode) {
|
||||||
const allowedSourcesSet = this.config.mode.allowedSources[mode] || [];
|
const allowedSourcesSet = this.config.mode.allowedSources[mode] || [];
|
||||||
return allowedSourcesSet.has(source);
|
const allowed = allowedSourcesSet.has(source);
|
||||||
|
allowed?
|
||||||
|
this.logger.debug(`source is allowed proceeding with ${source} for mode ${mode}`) :
|
||||||
|
this.logger.warn(`${source} is not allowed in mode ${mode}`);
|
||||||
|
|
||||||
|
return allowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
isValidActionForMode(action, mode) {
|
isValidActionForMode(action, mode) {
|
||||||
const allowedActionsSet = this.config.mode.allowedActions[mode] || [];
|
const allowedActionsSet = this.config.mode.allowedActions[mode] || [];
|
||||||
return allowedActionsSet.has(action);
|
const allowed = allowedActionsSet.has(action);
|
||||||
|
allowed ?
|
||||||
|
this.logger.debug(`Action is allowed proceeding with ${action} for mode ${mode}`) :
|
||||||
|
this.logger.warn(`${action} is not allowed in mode ${mode}`);
|
||||||
|
|
||||||
|
return allowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
async handleInput(source, action, parameter) {
|
async handleInput(source, action, parameter) {
|
||||||
|
|
||||||
if (!this.isValidSourceForMode(source, this.currentMode)) {
|
//sanitize input
|
||||||
let warningTxt = `Source '${source}' is not valid for mode '${this.currentMode}'.`;
|
if( typeof action !== 'string'){this.logger.error(`Action must be string`); return;}
|
||||||
this.logger.warn(warningTxt);
|
//convert to lower case to avoid to many mistakes in commands
|
||||||
return {status : false , feedback: warningTxt};
|
action = action.toLowerCase();
|
||||||
}
|
|
||||||
|
// check for validity of the request
|
||||||
|
if(!this.isValidActionForMode(action,this.currentMode)){return ;}
|
||||||
|
if (!this.isValidSourceForMode(source, this.currentMode)) {return ;}
|
||||||
|
|
||||||
this.logger.info(`Handling input from source '${source}' with action '${action}' in mode '${this.currentMode}'.`);
|
this.logger.info(`Handling input from source '${source}' with action '${action}' in mode '${this.currentMode}'.`);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case "execSequence":
|
|
||||||
|
case "execsequence":
|
||||||
return await this.executeSequence(parameter);
|
return await this.executeSequence(parameter);
|
||||||
|
|
||||||
case "execMovement":
|
case "execmovement":
|
||||||
return await this.setpoint(parameter);
|
return await this.setpoint(parameter);
|
||||||
|
|
||||||
case "flowMovement":
|
case "entermaintenance":
|
||||||
|
|
||||||
|
return await this.executeSequence(parameter);
|
||||||
|
|
||||||
|
|
||||||
|
case "exitmaintenance":
|
||||||
|
return await this.executeSequence(parameter);
|
||||||
|
|
||||||
|
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}'.`);
|
||||||
return await this.executeSequence("emergencyStop");
|
return await this.executeSequence("emergencyStop");
|
||||||
|
|
||||||
case "statusCheck":
|
case "statuscheck":
|
||||||
this.logger.info(`Status Check: Mode = '${this.currentMode}', Source = '${source}'.`);
|
this.logger.info(`Status Check: Mode = '${this.currentMode}', Source = '${source}'.`);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -299,21 +371,23 @@ class Machine {
|
|||||||
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);
|
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.`);
|
this.logger.debug(`Machine is not operational. Setting predicted flow to 0.`);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
//this.predictFlow.currentX = x; Decrepated
|
|
||||||
const cFlow = this.predictFlow.y(x);
|
const cFlow = this.predictFlow.y(x);
|
||||||
this.measurements.type("flow").variant("predicted").position("downstream").value(cFlow);
|
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}`);
|
//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);
|
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;
|
return 0;
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -322,20 +396,20 @@ class Machine {
|
|||||||
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;
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -353,7 +427,7 @@ class Machine {
|
|||||||
|
|
||||||
// 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;
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -363,14 +437,14 @@ class Machine {
|
|||||||
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;
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -378,6 +452,11 @@ class Machine {
|
|||||||
// returns the best available pressure measurement to use in the prediction calculation
|
// returns the best available pressure measurement to use in the prediction calculation
|
||||||
// this will be either the differential pressure, downstream or upstream pressure
|
// this will be either the differential pressure, downstream or upstream pressure
|
||||||
getMeasuredPressure() {
|
getMeasuredPressure() {
|
||||||
|
if(this.hasCurve === false){
|
||||||
|
this.logger.error(`No valid curve available to calculate prediction using last known pressure`);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
const pressureDiff = this.measurements.type('pressure').variant('measured').difference();
|
const pressureDiff = this.measurements.type('pressure').variant('measured').difference();
|
||||||
|
|
||||||
// Both upstream & downstream => differential
|
// Both upstream & downstream => differential
|
||||||
@@ -397,7 +476,7 @@ class Machine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 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) {
|
||||||
@@ -426,6 +505,9 @@ class Machine {
|
|||||||
const efficiency = this.calcEfficiency(this.predictPower.outputY, this.predictFlow.outputY, "predicted");
|
const efficiency = this.calcEfficiency(this.predictPower.outputY, this.predictFlow.outputY, "predicted");
|
||||||
//update the distance from peak
|
//update the distance from peak
|
||||||
this.calcDistanceBEP(efficiency,cog,minEfficiency);
|
this.calcDistanceBEP(efficiency,cog,minEfficiency);
|
||||||
|
//place min and max flow capabilities in containerthis.predictFlow.currentFxyYMax - this.predictFlow.currentFxyYMin
|
||||||
|
this.measurements.type('flow').variant('predicted').position('max').value(this.predictFlow.currentFxyYMax).unit(this.config.general.unit);
|
||||||
|
this.measurements.type('flow').variant('predicted').position('min').value(this.predictFlow.currentFxyYMin).unit(this.config.general.unit);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -448,7 +530,7 @@ class Machine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 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) {
|
||||||
@@ -457,7 +539,7 @@ class Machine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 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) {
|
||||||
@@ -471,7 +553,7 @@ class Machine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
||||||
@@ -517,7 +599,8 @@ class Machine {
|
|||||||
|
|
||||||
// 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("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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -530,6 +613,7 @@ class Machine {
|
|||||||
// Helper method for operational state check
|
// Helper method for operational state check
|
||||||
_isOperationalState() {
|
_isOperationalState() {
|
||||||
const state = this.state.getCurrentState();
|
const state = this.state.getCurrentState();
|
||||||
|
this.logger.debug(`Checking operational state ${this.state.getCurrentState()} ? ${["operational", "accelerating", "decelerating"].includes(state)}`);
|
||||||
return ["operational", "accelerating", "decelerating"].includes(state);
|
return ["operational", "accelerating", "decelerating"].includes(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -553,6 +637,7 @@ class Machine {
|
|||||||
this.calcDistanceBEP(efficiency,cog,minEfficiency);
|
this.calcDistanceBEP(efficiency,cog,minEfficiency);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
calcDistanceFromPeak(currentEfficiency,peakEfficiency){
|
calcDistanceFromPeak(currentEfficiency,peakEfficiency){
|
||||||
@@ -583,7 +668,6 @@ class Machine {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Calculate the center of gravity for current pressure
|
// Calculate the center of gravity for current pressure
|
||||||
calcCog() {
|
calcCog() {
|
||||||
|
|
||||||
@@ -593,7 +677,7 @@ class Machine {
|
|||||||
const {efficiencyCurve, peak, peakIndex, minEfficiency } = this.calcEfficiencyCurve(powerCurve, flowCurve);
|
const {efficiencyCurve, peak, peakIndex, minEfficiency } = this.calcEfficiencyCurve(powerCurve, flowCurve);
|
||||||
|
|
||||||
// Calculate the normalized center of gravity
|
// Calculate the normalized center of gravity
|
||||||
const NCog = (flowCurve.y[peakIndex] - this.predictFlow.currentFxyYMin) / (this.predictFlow.currentFxyYMax - this.predictFlow.currentFxyYMin);
|
const NCog = (flowCurve.y[peakIndex] - this.predictFlow.currentFxyYMin) / (this.predictFlow.currentFxyYMax - this.predictFlow.currentFxyYMin); //
|
||||||
|
|
||||||
//store in object for later retrieval
|
//store in object for later retrieval
|
||||||
this.currentEfficiencyCurve = efficiencyCurve;
|
this.currentEfficiencyCurve = efficiencyCurve;
|
||||||
@@ -643,16 +727,39 @@ class Machine {
|
|||||||
return { cPower, cFlow };
|
return { cPower, cFlow };
|
||||||
}
|
}
|
||||||
|
|
||||||
calcEfficiency(power, flow, variant) {
|
calcEfficiency(power,flow,variant) {
|
||||||
|
|
||||||
|
const pressureDiff = this.measurements.type('pressure').variant('measured').difference('Pa');
|
||||||
|
const g = gravity.getStandardGravity();
|
||||||
|
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(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) {
|
if (power != 0 && flow != 0) {
|
||||||
// Calculate efficiency after measurements update
|
const specificFlow = flow / power;
|
||||||
this.measurements.type("efficiency").variant(variant).position('atEquipment').value((flow / power));
|
const specificEnergyConsumption = power / flow;
|
||||||
} else {
|
|
||||||
this.measurements.type("efficiency").variant(variant).position('atEquipment').value(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.measurements.type("efficiency").variant(variant).position('atEquipment').getCurrentValue();
|
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(POSITIONS.AT_EQUIPMENT).value(nHydraulicEfficiency);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//change this to nhydrefficiency ?
|
||||||
|
return this.measurements.type("efficiency").variant(variant).position(POSITIONS.AT_EQUIPMENT).getCurrentValue();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -698,26 +805,8 @@ class Machine {
|
|||||||
getOutput() {
|
getOutput() {
|
||||||
|
|
||||||
// Improved output object generation
|
// Improved output object generation
|
||||||
const output = {};
|
|
||||||
//build the output object
|
|
||||||
this.measurements.getTypes().forEach(type => {
|
|
||||||
this.measurements.getVariants(type).forEach(variant => {
|
|
||||||
|
|
||||||
const downstreamVal = this.measurements.type(type).variant(variant).position("downstream").getCurrentValue();
|
const output = this.measurements.getFlattenedOutput();
|
||||||
const upstreamVal = this.measurements.type(type).variant(variant).position("upstream").getCurrentValue();
|
|
||||||
|
|
||||||
if (downstreamVal != null) {
|
|
||||||
output[`downstream_${variant}_${type}`] = downstreamVal;
|
|
||||||
}
|
|
||||||
if (upstreamVal != null) {
|
|
||||||
output[`upstream_${variant}_${type}`] = upstreamVal;
|
|
||||||
}
|
|
||||||
if (downstreamVal != null && upstreamVal != null) {
|
|
||||||
const diffVal = this.measurements.type(type).variant(variant).difference().value;
|
|
||||||
output[`differential_${variant}_${type}`] = diffVal;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
//fill in the rest of the output object
|
//fill in the rest of the output object
|
||||||
output["state"] = this.state.getCurrentState();
|
output["state"] = this.state.getCurrentState();
|
||||||
@@ -728,6 +817,7 @@ class Machine {
|
|||||||
output["cog"] = this.cog; // flow / power efficiency
|
output["cog"] = this.cog; // flow / power efficiency
|
||||||
output["NCog"] = this.NCog; // normalized cog
|
output["NCog"] = this.NCog; // normalized cog
|
||||||
output["NCogPercent"] = Math.round(this.NCog * 100 * 100) / 100 ;
|
output["NCogPercent"] = Math.round(this.NCog * 100 * 100) / 100 ;
|
||||||
|
output["maintenanceTime"] = this.state.getMaintenanceTimeHours();
|
||||||
|
|
||||||
if(this.flowDrift != null){
|
if(this.flowDrift != null){
|
||||||
const flowDrift = this.flowDrift;
|
const flowDrift = this.flowDrift;
|
||||||
@@ -751,8 +841,8 @@ class Machine {
|
|||||||
module.exports = Machine;
|
module.exports = Machine;
|
||||||
|
|
||||||
/*------------------- Testing -------------------*/
|
/*------------------- Testing -------------------*/
|
||||||
/*
|
|
||||||
|
|
||||||
|
/*
|
||||||
curve = require('C:/Users/zn375/.node-red/public/fallbackData.json');
|
curve = require('C:/Users/zn375/.node-red/public/fallbackData.json');
|
||||||
|
|
||||||
//import a child
|
//import a child
|
||||||
@@ -769,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",
|
||||||
@@ -791,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",
|
||||||
@@ -845,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"]);
|
||||||
@@ -859,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();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user