Compare commits

...

4 Commits

Author SHA1 Message Date
Rene De Ren
a18c36b2e5 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
aacbc1e99d 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
68576a8a36 Fix ESLint errors and bugs
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 13:39:57 +01:00
p.vanderwilt
442ddc60ed Fix syntax error 2025-10-01 11:50:35 +02:00
6 changed files with 25 additions and 33 deletions

View File

@@ -3,13 +3,12 @@ module.exports = function(RED) {
RED.nodes.createNode(this, config); RED.nodes.createNode(this, config);
var node = this; var node = this;
let name = config.name;
let F2 = parseFloat(config.F2); let F2 = parseFloat(config.F2);
const inlet_F2 = parseInt(config.inlet); const inlet_F2 = parseInt(config.inlet);
node.on('input', function(msg, send, done) { node.on('input', function(msg, send, done) {
switch (msg.topic) { switch (msg.topic) {
case "Fluent": case "Fluent": {
// conserve volume flow debit // conserve volume flow debit
let F_in = msg.payload.F; let F_in = msg.payload.F;
let F1 = Math.max(F_in - F2, 0); let F1 = Math.max(F_in - F2, 0);
@@ -24,6 +23,7 @@ module.exports = function(RED) {
send([msg_F1, msg_F2]); send([msg_F1, msg_F2]);
break; break;
}
case "clock": case "clock":
break; break;
default: default:

View File

@@ -3,13 +3,12 @@ module.exports = function(RED) {
RED.nodes.createNode(this, config); RED.nodes.createNode(this, config);
var node = this; var node = this;
let name = config.name;
let TS_set = parseFloat(config.TS_set); let TS_set = parseFloat(config.TS_set);
const inlet_sludge = parseInt(config.inlet); const inlet_sludge = parseInt(config.inlet);
node.on('input', function(msg, send, done) { node.on('input', function(msg, send, done) {
switch (msg.topic) { switch (msg.topic) {
case "Fluent": case "Fluent": {
// conserve volume flow debit // conserve volume flow debit
let F_in = msg.payload.F; let F_in = msg.payload.F;
let C_in = msg.payload.C; let C_in = msg.payload.C;
@@ -41,6 +40,7 @@ module.exports = function(RED) {
send([msg_F1, msg_F2]); send([msg_F1, msg_F2]);
break; break;
}
case "clock": case "clock":
break; break;
default: default:

View File

@@ -1,4 +1,5 @@
const { Reactor_CSTR, Reactor_PFR } = require('./specificClass.js'); const { Reactor_CSTR, Reactor_PFR } = require('./specificClass.js');
const { configManager } = require('generalFunctions');
class nodeClass { class nodeClass {
@@ -48,12 +49,13 @@ class nodeClass {
case "Dispersion": case "Dispersion":
this.source.setDispersion = msg; this.source.setDispersion = msg;
break; break;
case 'registerChild': case 'registerChild': {
// Register this node as a parent of the child node // Register this node as a parent of the child node
const childId = msg.payload; const childId = msg.payload;
const childObj = this.RED.nodes.getNode(childId); const childObj = this.RED.nodes.getNode(childId);
this.source.childRegistrationUtils.registerChild(childObj.source, msg.positionVsParent); this.source.childRegistrationUtils.registerChild(childObj.source, msg.positionVsParent);
break; break;
}
default: default:
console.log("Unknown topic: " + msg.topic); console.log("Unknown topic: " + msg.topic);
} }
@@ -69,20 +71,10 @@ class nodeClass {
* @param {object} uiConfig Config set in UI in node-red * @param {object} uiConfig Config set in UI in node-red
*/ */
_loadConfig(uiConfig) { _loadConfig(uiConfig) {
this.config = { const cfgMgr = new configManager();
general: {
name: uiConfig.name || this.name, // Build config: base sections + reactor-specific domain config
id: this.node.id, this.config = cfgMgr.buildConfig('reactor', uiConfig, this.node.id, {
unit: null,
logging: {
enabled: uiConfig.enableLog,
logLevel: uiConfig.logLevel
}
},
functionality: {
positionVsParent: uiConfig.positionVsParent || 'atEquipment', // Default to 'atEquipment' if not specified
softwareType: "reactor" // should be set in config manager
},
reactor_type: uiConfig.reactor_type, reactor_type: uiConfig.reactor_type,
volume: parseFloat(uiConfig.volume), volume: parseFloat(uiConfig.volume),
length: parseFloat(uiConfig.length), length: parseFloat(uiConfig.length),
@@ -106,7 +98,7 @@ class nodeClass {
parseFloat(uiConfig.X_TS_init) parseFloat(uiConfig.X_TS_init)
], ],
timeStep: parseFloat(uiConfig.timeStep) timeStep: parseFloat(uiConfig.timeStep)
} });
} }
/** /**
@@ -137,7 +129,7 @@ class nodeClass {
new_reactor = new Reactor_PFR(this.config); new_reactor = new Reactor_PFR(this.config);
break; break;
default: default:
console.warn("Unknown reactor type: " + uiConfig.reactor_type); console.warn("Unknown reactor type: " + this.config.reactor_type);
} }
this.source = new_reactor; // protect from reassignment this.source = new_reactor; // protect from reassignment

View File

@@ -19,7 +19,7 @@ class ASM3 {
nu_NO: 0.5, // anoxic reduction factor [-] nu_NO: 0.5, // anoxic reduction factor [-]
K_O: 0.2, // saturation constant S_0 [g O2 m-3] K_O: 0.2, // saturation constant S_0 [g O2 m-3]
K_NO: 0.5, // saturation constant S_NO [g NO3-N m-3] K_NO: 0.5, // saturation constant S_NO [g NO3-N m-3]
K_S: 10., // saturation constant S_s [g COD m-3] K_S: 10.0, // saturation constant S_s [g COD m-3]
K_STO: 0.1, // saturation constant X_STO [g X_STO g-1 X_H] K_STO: 0.1, // saturation constant X_STO [g X_STO g-1 X_H]
mu_H_max: 3., // maximum specific growth rate [d-1] mu_H_max: 3., // maximum specific growth rate [d-1]
K_NH: 0.01, // saturation constant S_NH3 [g NH3-N m-3] K_NH: 0.01, // saturation constant S_NH3 [g NH3-N m-3]
@@ -171,7 +171,7 @@ class ASM3 {
compute_rates(state, T = 20) { compute_rates(state, T = 20) {
// state: S_O, S_I, S_S, S_NH, S_N2, S_NO, S_HCO, X_I, X_S, X_H, X_STO, X_A, X_TS // state: S_O, S_I, S_S, S_NH, S_N2, S_NO, S_HCO, X_I, X_S, X_H, X_STO, X_A, X_TS
const rates = Array(12); const rates = Array(12);
const [S_O, S_I, S_S, S_NH, S_N2, S_NO, S_HCO, X_I, X_S, X_H, X_STO, X_A, X_TS] = state; const [S_O, , S_S, S_NH, , S_NO, S_HCO, , X_S, X_H, X_STO, X_A] = state;
const { k_H, K_X, k_STO, nu_NO, K_O, K_NO, K_S, K_STO, mu_H_max, K_NH, K_HCO, b_H_O, b_H_NO, b_STO_O, b_STO_NO, mu_A_max, K_A_NH, K_A_O, K_A_HCO, b_A_O, b_A_NO } = this.kin_params; const { k_H, K_X, k_STO, nu_NO, K_O, K_NO, K_S, K_STO, mu_H_max, K_NH, K_HCO, b_H_O, b_H_NO, b_STO_O, b_STO_NO, mu_A_max, K_A_NH, K_A_O, K_A_HCO, b_A_O, b_A_NO } = this.kin_params;
const { theta_H, theta_STO, theta_mu_H, theta_b_H_O, theta_b_H_NO, theta_b_STO_O, theta_b_STO_NO, theta_mu_A, theta_b_A_O, theta_b_A_NO } = this.temp_params; const { theta_H, theta_STO, theta_mu_H, theta_b_H_O, theta_b_H_NO, theta_b_STO_O, theta_b_STO_NO, theta_mu_A, theta_b_A_O, theta_b_A_NO } = this.temp_params;

View File

@@ -171,7 +171,7 @@ class ASM3 {
compute_rates(state, T = 20) { compute_rates(state, T = 20) {
// state: S_O, S_I, S_S, S_NH, S_N2, S_NO, S_HCO, X_I, X_S, X_H, X_STO, X_A, X_TS // state: S_O, S_I, S_S, S_NH, S_N2, S_NO, S_HCO, X_I, X_S, X_H, X_STO, X_A, X_TS
const rates = Array(12); const rates = Array(12);
const [S_O, S_I, S_S, S_NH, S_N2, S_NO, S_HCO, X_I, X_S, X_H, X_STO, X_A, X_TS] = state; const [S_O, , S_S, S_NH, , S_NO, S_HCO, , X_S, X_H, X_STO, X_A] = state;
const { k_H, K_X, k_STO, nu_NO, K_O, K_NO, K_S, K_STO, mu_H_max, K_NH, K_HCO, b_H_O, b_H_NO, b_STO_O, b_STO_NO, mu_A_max, K_A_NH, K_A_O, K_A_HCO, b_A_O, b_A_NO } = this.kin_params; const { k_H, K_X, k_STO, nu_NO, K_O, K_NO, K_S, K_STO, mu_H_max, K_NH, K_HCO, b_H_O, b_H_NO, b_STO_O, b_STO_NO, mu_A_max, K_A_NH, K_A_O, K_A_HCO, b_A_O, b_A_NO } = this.kin_params;
const { theta_H, theta_STO, theta_mu_H, theta_b_H_O, theta_b_H_NO, theta_b_STO_O, theta_b_STO_NO, theta_mu_A, theta_b_A_O, theta_b_A_NO } = this.temp_params; const { theta_H, theta_STO, theta_mu_H, theta_b_H_O, theta_b_H_NO, theta_b_STO_O, theta_b_STO_NO, theta_mu_A, theta_b_A_O, theta_b_A_NO } = this.temp_params;

View File

@@ -1,7 +1,7 @@
const ASM3 = require('./reaction_modules/asm3_class.js'); const ASM3 = require('./reaction_modules/asm3_class.js');
const { create, all, isArray } = require('mathjs'); const { create, all, isArray } = require('mathjs');
const { assertNoNaN } = require('./utils.js'); const { assertNoNaN } = require('./utils.js');
const { childRegistrationUtils, logger, MeasurementContainer } = require('generalFunctions'); const { childRegistrationUtils, logger, MeasurementContainer, POSITIONS } = require('generalFunctions');
const EventEmitter = require('events'); const EventEmitter = require('events');
const mathConfig = { const mathConfig = {
@@ -126,7 +126,6 @@ class Reactor {
position = measurement.config.functionality.positionVsParent; position = measurement.config.functionality.positionVsParent;
} }
const measurementType = measurement.config.asset.type; const measurementType = measurement.config.asset.type;
const key = `${measurementType}_${position}`;
const eventName = `${measurementType}.measured.${position}`; const eventName = `${measurementType}.measured.${position}`;
// Register event listener for measurement updates // Register event listener for measurement updates
@@ -160,11 +159,11 @@ class Reactor {
} }
_updateMeasurement(measurementType, value, position, context) { _updateMeasurement(measurementType, value, position, _context) {
this.logger.debug(`---------------------- updating ${measurementType} ------------------ `); this.logger.debug(`---------------------- updating ${measurementType} ------------------ `);
switch (measurementType) { switch (measurementType) {
case "temperature": case "temperature":
if (position == "atEquipment") { if (position == POSITIONS.AT_EQUIPMENT) {
this.temperature = value; this.temperature = value;
} }
break; break;
@@ -320,14 +319,15 @@ class Reactor_PFR extends Reactor {
return stateNew; return stateNew;
} }
_updateMeasurement(measurementType, value, position, context) { _updateMeasurement(measurementType, value, position, _context) {
switch(measurementType) { switch(measurementType) {
case "quantity (oxygen)": case "quantity (oxygen)": {
grid_pos = Math.round(position / this.config.length * this.n_x); let grid_pos = Math.round(position / this.config.length * this.n_x);
this.state[grid_pos][S_O_INDEX] = value; // naive approach for reconciling measurements and simulation this.state[grid_pos][S_O_INDEX] = value; // naive approach for reconciling measurements and simulation
break; break;
}
default: default:
super._updateMeasurement(measurementType, value, position, context); super._updateMeasurement(measurementType, value, position, _context);
} }
} }