updates
This commit is contained in:
@@ -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"));
|
||||
|
||||
@@ -30,7 +30,7 @@ class nodeClass {
|
||||
*/
|
||||
_attachInputHandler() {
|
||||
this.node.on('input', (msg, send, done) => {
|
||||
|
||||
try {
|
||||
switch (msg.topic) {
|
||||
case "clock":
|
||||
this.source.updateState(msg.timestamp);
|
||||
@@ -48,17 +48,24 @@ class nodeClass {
|
||||
case "Dispersion":
|
||||
this.source.setDispersion = msg;
|
||||
break;
|
||||
case 'registerChild':
|
||||
// Register this node as a parent of the child node
|
||||
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:
|
||||
console.log("Unknown topic: " + msg.topic);
|
||||
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,7 +165,7 @@ class nodeClass {
|
||||
_attachCloseHandler() {
|
||||
this.node.on('close', (done) => {
|
||||
clearInterval(this._tickInterval);
|
||||
done();
|
||||
if (typeof done === 'function') done();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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' },
|
||||
() => {},
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user