From 460b872053ba07f48e6daea29eaa5c920cdab0e1 Mon Sep 17 00:00:00 2001 From: znetsixe <73483679+znetsixe@users.noreply.github.com> Date: Mon, 23 Feb 2026 12:51:10 +0100 Subject: [PATCH] updates --- reactor.html | 4 +- src/nodeClass.js | 68 ++++++++++++--------- src/specificClass.js | 32 +++++++++- test/edge/missing-child.edge.test.js | 4 +- test/edge/pfr-measurement-grid.edge.test.js | 5 +- 5 files changed, 74 insertions(+), 39 deletions(-) diff --git a/reactor.html b/reactor.html index 8fb9526..a591479 100644 --- a/reactor.html +++ b/reactor.html @@ -130,8 +130,8 @@ } // save position field - if (window.EVOLV?.nodes?.measurement?.positionMenu?.saveEditor) { - window.EVOLV.nodes.rotatingMachine.positionMenu.saveEditor(this); + if (window.EVOLV?.nodes?.reactor?.positionMenu?.saveEditor) { + window.EVOLV.nodes.reactor.positionMenu.saveEditor(this); } let volume = parseFloat($("#node-input-volume").typedInput("value")); diff --git a/src/nodeClass.js b/src/nodeClass.js index a9e53d3..62b8b66 100644 --- a/src/nodeClass.js +++ b/src/nodeClass.js @@ -30,35 +30,42 @@ class nodeClass { */ _attachInputHandler() { this.node.on('input', (msg, send, done) => { - - switch (msg.topic) { - case "clock": - this.source.updateState(msg.timestamp); - send([msg, null, null]); - break; - case "Fluent": - this.source.setInfluent = msg; - break; - case "OTR": - this.source.setOTR = msg; - break; - case "Temperature": - this.source.setTemperature = msg; - break; - case "Dispersion": - this.source.setDispersion = msg; - break; - case 'registerChild': - // Register this node as a parent of the child node - const childId = msg.payload; - const childObj = this.RED.nodes.getNode(childId); - this.source.childRegistrationUtils.registerChild(childObj.source, msg.positionVsParent); - break; - default: - console.log("Unknown topic: " + msg.topic); + try { + switch (msg.topic) { + case "clock": + this.source.updateState(msg.timestamp); + send([msg, null, null]); + break; + case "Fluent": + this.source.setInfluent = msg; + break; + case "OTR": + this.source.setOTR = msg; + break; + case "Temperature": + this.source.setTemperature = msg; + break; + case "Dispersion": + this.source.setDispersion = msg; + break; + case 'registerChild': { + const childId = msg.payload; + const childObj = this.RED.nodes.getNode(childId); + if (!childObj || !childObj.source) { + this.source?.logger?.warn(`registerChild skipped: missing child/source for id=${childId}`); + break; + } + this.source.childRegistrationUtils.registerChild(childObj.source, msg.positionVsParent); + break; + } + default: + this.source?.logger?.warn(`Unknown topic: ${msg.topic}`); + } + } catch (error) { + this.source?.logger?.error(`Input handler failure: ${error.message}`); } - if (done) { + if (typeof done === 'function') { done(); } }); @@ -137,7 +144,8 @@ class nodeClass { new_reactor = new Reactor_PFR(this.config); break; default: - console.warn("Unknown reactor type: " + uiConfig.reactor_type); + this.node.warn("Unknown reactor type: " + this.config.reactor_type + ". Falling back to CSTR."); + new_reactor = new Reactor_CSTR(this.config); } this.source = new_reactor; // protect from reassignment @@ -157,9 +165,9 @@ class nodeClass { _attachCloseHandler() { this.node.on('close', (done) => { clearInterval(this._tickInterval); - done(); + if (typeof done === 'function') done(); }); } } -module.exports = nodeClass; \ No newline at end of file +module.exports = nodeClass; diff --git a/src/specificClass.js b/src/specificClass.js index d34e6cc..e8d71ae 100644 --- a/src/specificClass.js +++ b/src/specificClass.js @@ -62,6 +62,24 @@ class Reactor { this.OTR = input.payload; } + /** + * Setter for reactor temperature [C]. + * Accepts either a direct numeric payload or { value } object payload. + * @param {object} input - Input object (msg) + */ + set setTemperature(input) { + const payload = input?.payload; + const rawValue = (payload && typeof payload === 'object' && payload.value !== undefined) + ? payload.value + : payload; + const parsedValue = Number(rawValue); + if (!Number.isFinite(parsedValue)) { + this.logger.warn(`Invalid temperature input: ${rawValue}`); + return; + } + this.temperature = parsedValue; + } + /** * Getter for effluent data. * @returns {object} Effluent data object (msg), defaults to inlet 0. @@ -323,8 +341,16 @@ class Reactor_PFR extends Reactor { _updateMeasurement(measurementType, value, position, context) { switch(measurementType) { case "quantity (oxygen)": - 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 + if (!Number.isFinite(position) || !Number.isFinite(value) || this.config.length <= 0) { + this.logger.warn(`Ignoring oxygen measurement update with invalid data (position=${position}, value=${value}).`); + break; + } + { + // Clamp sensor-derived position to valid PFR grid bounds. + const rawIndex = Math.round(position / this.config.length * this.n_x); + const grid_pos = Math.max(0, Math.min(this.n_x - 1, rawIndex)); + this.state[grid_pos][S_O_INDEX] = value; // reconcile measured oxygen concentration into nearest grid cell + } break; default: super._updateMeasurement(measurementType, value, position, context); @@ -416,4 +442,4 @@ module.exports = { Reactor_CSTR, Reactor_PFR }; // while (N < 5000) { // console.log(Reactor.tick(0.001)); // N += 1; -// } \ No newline at end of file +// } diff --git a/test/edge/missing-child.edge.test.js b/test/edge/missing-child.edge.test.js index 6d8184a..3e2cb0f 100644 --- a/test/edge/missing-child.edge.test.js +++ b/test/edge/missing-child.edge.test.js @@ -4,7 +4,7 @@ const assert = require('node:assert/strict'); const NodeClass = require('../../src/nodeClass'); const { makeNodeStub, makeREDStub } = require('../helpers/factories'); -test('registerChild with unknown node id currently throws (known robustness gap)', () => { +test('registerChild with unknown node id is ignored without throwing', () => { const inst = Object.create(NodeClass.prototype); const node = makeNodeStub(); @@ -18,7 +18,7 @@ test('registerChild with unknown node id currently throws (known robustness gap) inst._attachInputHandler(); - assert.throws(() => { + assert.doesNotThrow(() => { node._handlers.input( { topic: 'registerChild', payload: 'missing-child', positionVsParent: 'upstream' }, () => {}, diff --git a/test/edge/pfr-measurement-grid.edge.test.js b/test/edge/pfr-measurement-grid.edge.test.js index 16889ec..3932a6f 100644 --- a/test/edge/pfr-measurement-grid.edge.test.js +++ b/test/edge/pfr-measurement-grid.edge.test.js @@ -4,12 +4,13 @@ const assert = require('node:assert/strict'); const { Reactor_PFR } = require('../../src/specificClass'); const { makeReactorConfig } = require('../helpers/factories'); -test('oxygen measurement at exact reactor length overflows PFR grid index (known bounds gap)', () => { +test('oxygen measurement at exact reactor length is clamped to the last PFR grid index', () => { const reactor = new Reactor_PFR( makeReactorConfig({ reactor_type: 'PFR', length: 10, resolution_L: 5, n_inlets: 1 }), ); - assert.throws(() => { + assert.doesNotThrow(() => { reactor._updateMeasurement('quantity (oxygen)', 2.5, 10, {}); }); + assert.equal(reactor.state[reactor.n_x - 1][0], 2.5); });