Compare commits

..

3 Commits

Author SHA1 Message Date
Rene De Ren
548778c3f5 Expose output format selectors in editor 2026-03-12 16:39:25 +01:00
Rene De Ren
d594131cfc 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
aaa88a7792 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 137 additions and 134 deletions

View File

@@ -42,32 +42,13 @@ 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) {
// Resolve flow unit with validation before building config
const flowUnit = this._resolveUnitOrFallback(uiConfig.unit, 'volumeFlowRate', 'm3/h', 'flow'); const flowUnit = this._resolveUnitOrFallback(uiConfig.unit, 'volumeFlowRate', 'm3/h', 'flow');
const resolvedUiConfig = { ...uiConfig, unit: flowUnit };
// Merge UI config over defaults // Build config: base sections handle general, asset, functionality
this.config = { const cfgMgr = new configManager();
general: { this.config = cfgMgr.buildConfig(this.name, resolvedUiConfig, node.id);
name: uiConfig.name,
id: node.id, // node.id is for the child registration process
unit: flowUnit,
logging: {
enabled: uiConfig.enableLog,
logLevel: uiConfig.logLevel
}
},
asset: {
uuid: uiConfig.uuid || uiConfig.assetUuid || null,
tagCode: uiConfig.tagCode || uiConfig.assetTagCode || null,
supplier: uiConfig.supplier,
category: uiConfig.category, //add later to define as the software type
type: uiConfig.assetType,
model: uiConfig.model,
unit: flowUnit
},
functionality: {
positionVsParent: uiConfig.positionVsParent || 'atEquipment', // Default to 'atEquipment' if not specified
}
};
// Utility for formatting outputs // Utility for formatting outputs
this._output = new outputUtils(); this._output = new outputUtils();
@@ -145,8 +126,8 @@ class nodeClass {
const v = this.source; const v = this.source;
try { try {
const mode = v.currentMode; // modus is bijv. auto, manual, etc. const mode = v.currentMode;
const state = v.state.getCurrentState(); //is bijv. operational, idle, off, etc. const state = v.state.getCurrentState();
const fluidCompatibility = typeof v.getFluidCompatibility === "function" const fluidCompatibility = typeof v.getFluidCompatibility === "function"
? v.getFluidCompatibility() ? v.getFluidCompatibility()
: null; : null;
@@ -158,13 +139,12 @@ class nodeClass {
: ""; : "";
const flowUnit = v?.unitPolicy?.output?.flow || this.config.general.unit || "m3/h"; const flowUnit = v?.unitPolicy?.output?.flow || this.config.general.unit || "m3/h";
const pressureUnit = v?.unitPolicy?.output?.pressure || "mbar"; const pressureUnit = v?.unitPolicy?.output?.pressure || "mbar";
// check if measured flow is available otherwise use predicted flow
const flow = Math.round(v.measurements.type("flow").variant("predicted").position("downstream").getCurrentValue(flowUnit)); const flow = Math.round(v.measurements.type("flow").variant("predicted").position("downstream").getCurrentValue(flowUnit));
let deltaP = v.measurements.type("pressure").variant("predicted").position("delta").getCurrentValue(pressureUnit); let deltaP = v.measurements.type("pressure").variant("predicted").position("delta").getCurrentValue(pressureUnit);
if (deltaP !== null) { if (deltaP !== null) {
deltaP = parseFloat(deltaP.toFixed(0)); deltaP = parseFloat(deltaP.toFixed(0));
} //afronden op 4 decimalen indien geen "null" }
if(isNaN(deltaP)) { if(isNaN(deltaP)) {
deltaP = "∞"; deltaP = "∞";
} }
@@ -200,7 +180,6 @@ class nodeClass {
break; break;
} }
let status; let status;
switch (state) { switch (state) {
case "off": case "off":
@@ -210,16 +189,16 @@ class nodeClass {
status = { fill: "blue", shape: "dot", text: `${mode}: ${symbolState}` }; status = { fill: "blue", shape: "dot", text: `${mode}: ${symbolState}` };
break; break;
case "operational": case "operational":
status = { fill: "green", shape: "dot", text: `${mode}: ${symbolState} | ${roundedPosition}% | 💨${flow}${flowUnit} | ΔP${deltaP} ${pressureUnit}`}; //deltaP toegevoegd status = { fill: "green", shape: "dot", text: `${mode}: ${symbolState} | ${roundedPosition}% | 💨${flow}${flowUnit} | ΔP${deltaP} ${pressureUnit}`};
break; break;
case "starting": case "starting":
status = { fill: "yellow", shape: "dot", text: `${mode}: ${symbolState}` }; status = { fill: "yellow", shape: "dot", text: `${mode}: ${symbolState}` };
break; break;
case "warmingup": case "warmingup":
status = { fill: "green", shape: "dot", text: `${mode}: ${symbolState} | ${roundedPosition}% | 💨${flow}${flowUnit} | ΔP${deltaP} ${pressureUnit}`}; //deltaP toegevoegd status = { fill: "green", shape: "dot", text: `${mode}: ${symbolState} | ${roundedPosition}% | 💨${flow}${flowUnit} | ΔP${deltaP} ${pressureUnit}`};
break; break;
case "accelerating": case "accelerating":
status = { fill: "yellow", shape: "dot", text: `${mode}: ${symbolState} | ${roundedPosition}% | 💨${flow}${flowUnit} | ΔP${deltaP} ${pressureUnit}` }; //deltaP toegevoegd status = { fill: "yellow", shape: "dot", text: `${mode}: ${symbolState} | ${roundedPosition}% | 💨${flow}${flowUnit} | ΔP${deltaP} ${pressureUnit}` };
break; break;
case "stopping": case "stopping":
status = { fill: "yellow", shape: "dot", text: `${mode}: ${symbolState}` }; status = { fill: "yellow", shape: "dot", text: `${mode}: ${symbolState}` };
@@ -228,7 +207,7 @@ class nodeClass {
status = { fill: "yellow", shape: "dot", text: `${mode}: ${symbolState}` }; status = { fill: "yellow", shape: "dot", text: `${mode}: ${symbolState}` };
break; break;
case "decelerating": case "decelerating":
status = { fill: "yellow", shape: "dot", text: `${mode}: ${symbolState} - ${roundedPosition}% | 💨${flow}${flowUnit} | ΔP${deltaP} ${pressureUnit}`}; //deltaP toegevoegd status = { fill: "yellow", shape: "dot", text: `${mode}: ${symbolState} - ${roundedPosition}% | 💨${flow}${flowUnit} | ΔP${deltaP} ${pressureUnit}`};
break; break;
default: default:
status = { fill: "grey", shape: "dot", text: `${mode}: ${symbolState}` }; status = { fill: "grey", shape: "dot", text: `${mode}: ${symbolState}` };

View File

@@ -703,7 +703,7 @@ class Valve {
this.logger.debug(`Updating pressure: variant=${variant}, value=${value}, position=${position}`); this.logger.debug(`Updating pressure: variant=${variant}, value=${value}, position=${position}`);
switch (variant) { switch (variant) {
case ("measured"): case ("measured"): {
// put value in measurements container // put value in measurements container
this._writeMeasurement("pressure", "measured", position, Number(value), unit); this._writeMeasurement("pressure", "measured", position, Number(value), unit);
// get latest downstream pressure measurement // get latest downstream pressure measurement
@@ -712,18 +712,20 @@ class Valve {
const predictedFlow = this._readMeasurement("flow", "predicted", "downstream", FORMULA_UNITS.flow); const predictedFlow = this._readMeasurement("flow", "predicted", "downstream", FORMULA_UNITS.flow);
const activeFlow = Number.isFinite(predictedFlow) ? predictedFlow : measuredFlow; const activeFlow = Number.isFinite(predictedFlow) ? predictedFlow : measuredFlow;
// update predicted flow measurement // update predicted flow measurement
this.updateDeltaPKlep(activeFlow,this.kv,measuredDownStreamP,this.rho,this.T); //update deltaP based on new flow this.updateDeltaPKlep(activeFlow,this.kv,measuredDownStreamP,this.rho,this.T);
break; break;
}
case ("predicted"): case ("predicted"): {
// put value in measurements container // put value in measurements container
this._writeMeasurement("pressure", "predicted", position, Number(value), unit); this._writeMeasurement("pressure", "predicted", position, Number(value), unit);
const predictedDownStreamP = this._readMeasurement("pressure", "predicted", "downstream", FORMULA_UNITS.pressure); const predictedDownStreamP = this._readMeasurement("pressure", "predicted", "downstream", FORMULA_UNITS.pressure);
const measuredFlowFromPred = this._readMeasurement("flow", "measured", "downstream", FORMULA_UNITS.flow); const measuredFlowFromPred = this._readMeasurement("flow", "measured", "downstream", FORMULA_UNITS.flow);
const predictedFlowFromPred = this._readMeasurement("flow", "predicted", "downstream", FORMULA_UNITS.flow); const predictedFlowFromPred = this._readMeasurement("flow", "predicted", "downstream", FORMULA_UNITS.flow);
const activeFlowFromPred = Number.isFinite(predictedFlowFromPred) ? predictedFlowFromPred : measuredFlowFromPred; const activeFlowFromPred = Number.isFinite(predictedFlowFromPred) ? predictedFlowFromPred : measuredFlowFromPred;
this.updateDeltaPKlep(activeFlowFromPred,this.kv,predictedDownStreamP,this.rho,this.T); //update deltaP based on new flow this.updateDeltaPKlep(activeFlowFromPred,this.kv,predictedDownStreamP,this.rho,this.T);
break; break;
}
default: default:
this.logger.warn(`Unrecognized variant '${variant}' for flow update.`); this.logger.warn(`Unrecognized variant '${variant}' for flow update.`);
@@ -784,23 +786,25 @@ class Valve {
this.logger.debug(`Updating flow: variant=${variant}, value=${value}, position=${position}`); this.logger.debug(`Updating flow: variant=${variant}, value=${value}, position=${position}`);
switch (variant) { switch (variant) {
case ("measured"): case ("measured"): {
// put value in measurements container // put value in measurements container
this._writeMeasurement("flow", "measured", position, Number(value), unit); this._writeMeasurement("flow", "measured", position, Number(value), unit);
// get latest downstream pressure measurement // get latest downstream pressure measurement
const measuredDownStreamP = this._readMeasurement("pressure", "measured", "downstream", FORMULA_UNITS.pressure); const measuredDownStreamP = this._readMeasurement("pressure", "measured", "downstream", FORMULA_UNITS.pressure);
const measuredFlow = this._readMeasurement("flow", "measured", position, FORMULA_UNITS.flow); const measuredFlow = this._readMeasurement("flow", "measured", position, FORMULA_UNITS.flow);
// update predicted flow measurement // update predicted flow measurement
this.updateDeltaPKlep(measuredFlow,this.kv,measuredDownStreamP,this.rho,this.T); //update deltaP based on new flow this.updateDeltaPKlep(measuredFlow,this.kv,measuredDownStreamP,this.rho,this.T);
break; break;
}
case ("predicted"): case ("predicted"): {
// put value in measurements container // put value in measurements container
this._writeMeasurement("flow", "predicted", position, Number(value), unit); this._writeMeasurement("flow", "predicted", position, Number(value), unit);
const predictedDownStreamP = this._readMeasurement("pressure", "measured", "downstream", FORMULA_UNITS.pressure); const predictedDownStreamP = this._readMeasurement("pressure", "measured", "downstream", FORMULA_UNITS.pressure);
const predictedFlow = this._readMeasurement("flow", "predicted", position, FORMULA_UNITS.flow); const predictedFlow = this._readMeasurement("flow", "predicted", position, FORMULA_UNITS.flow);
this.updateDeltaPKlep(predictedFlow,this.kv,predictedDownStreamP,this.rho,this.T); //update deltaP based on new flow this.updateDeltaPKlep(predictedFlow,this.kv,predictedDownStreamP,this.rho,this.T);
break; break;
}
default: default:
this.logger.warn(`Unrecognized variant '${variant}' for flow update.`); this.logger.warn(`Unrecognized variant '${variant}' for flow update.`);

View File

@@ -22,6 +22,8 @@
// Define specific properties // Define specific properties
speed: { value: 1, required: true }, speed: { value: 1, required: true },
processOutputFormat: { value: "process" },
dbaseOutputFormat: { value: "influxdb" },
//define asset properties //define asset properties
uuid: { value: "" }, uuid: { value: "" },
@@ -113,6 +115,24 @@
<input type="number" id="node-input-speed" style="width:60%;" /> <input type="number" id="node-input-speed" style="width:60%;" />
</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>
<!-- Optional Extended Fields: supplier, cat, type, model, unit --> <!-- Optional Extended Fields: supplier, cat, type, model, unit -->
<!-- Asset fields will be injected here --> <!-- Asset fields will be injected here -->
<div id="asset-fields-placeholder"></div> <div id="asset-fields-placeholder"></div>