generalFunctions' commandRegistry._normaliseUnits now converts {value, unit}
or unit-tagged payloads to the descriptor's default unit (m3/h for set.demand)
before the handler runs. setDemand just reads Number(payload) — no inline
unit-conversion, no scaling state. Matches the same shift done in MGC for
unit-self-describing demand commands.
Pre-existing test failure: test/integration/basic-dashboard-flow.test.js
references examples/basic-dashboard.flow.json which was renamed to
02-Dashboard.json in commit fe5fa35 (feat(pumpingStation): … dashboard
example). The 3 stale-path failures are unrelated to this commit — they
were broken before this change.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
112 lines
3.6 KiB
JavaScript
112 lines
3.6 KiB
JavaScript
'use strict';
|
|
|
|
// Handler functions for pumpingStation commands. Each handler receives:
|
|
// source: the domain (specificClass) instance — has the public methods
|
|
// (changeMode, calibratePredicted*, setManualInflow, ...).
|
|
// msg: the Node-RED input message.
|
|
// ctx: { node, RED, send, logger } — provided by BaseNodeAdapter.
|
|
//
|
|
// Handlers are pure functions: they don't keep state. Validation that goes
|
|
// beyond the registry's typeof-check ladder lives here.
|
|
|
|
function _logger(source, ctx) {
|
|
return ctx?.logger || source?.logger || null;
|
|
}
|
|
|
|
exports.setMode = (source, msg) => {
|
|
source.changeMode(msg.payload);
|
|
};
|
|
|
|
exports.registerChild = (source, msg, ctx) => {
|
|
const log = _logger(source, ctx);
|
|
const childId = msg.payload;
|
|
const childObj = ctx?.RED?.nodes?.getNode?.(childId);
|
|
if (!childObj || !childObj.source) {
|
|
log?.warn?.(`registerChild: child '${childId}' not found or has no .source`);
|
|
return;
|
|
}
|
|
source.childRegistrationUtils.registerChild(childObj.source, msg.positionVsParent);
|
|
};
|
|
|
|
exports.calibrateVolume = (source, msg, ctx) => {
|
|
const log = _logger(source, ctx);
|
|
const v = parseFloat(msg.payload);
|
|
if (!Number.isFinite(v)) {
|
|
log?.warn?.(`cmd.calibrate.volume: non-numeric payload '${msg.payload}'`);
|
|
return;
|
|
}
|
|
source.calibratePredictedVolume(v);
|
|
};
|
|
|
|
exports.calibrateLevel = (source, msg, ctx) => {
|
|
const log = _logger(source, ctx);
|
|
const v = parseFloat(msg.payload);
|
|
if (!Number.isFinite(v)) {
|
|
log?.warn?.(`cmd.calibrate.level: non-numeric payload '${msg.payload}'`);
|
|
return;
|
|
}
|
|
source.calibratePredictedLevel(v);
|
|
};
|
|
|
|
exports.setInflow = (source, msg) => {
|
|
// Payload is either a number (legacy q_in shape) or
|
|
// { value, unit, timestamp } (richer object form).
|
|
const p = msg.payload;
|
|
let value;
|
|
let unit;
|
|
let timestamp;
|
|
if (p !== null && typeof p === 'object') {
|
|
value = Number(p.value);
|
|
unit = p.unit;
|
|
timestamp = p.timestamp || Date.now();
|
|
} else {
|
|
value = Number(p);
|
|
unit = msg?.unit;
|
|
timestamp = msg?.timestamp || Date.now();
|
|
}
|
|
source.setManualInflow(value, timestamp, unit);
|
|
};
|
|
|
|
exports.setOutflow = (source, msg) => {
|
|
// Manual q_out — basin-docs dashboard injects a drain rate without
|
|
// wiring a real pump. Same payload shape as q_in.
|
|
const p = msg.payload;
|
|
let value;
|
|
let unit;
|
|
let timestamp;
|
|
if (p !== null && typeof p === 'object') {
|
|
value = Number(p.value);
|
|
unit = p.unit;
|
|
timestamp = p.timestamp || Date.now();
|
|
} else {
|
|
value = Number(p);
|
|
unit = msg?.unit;
|
|
timestamp = msg?.timestamp || Date.now();
|
|
}
|
|
source.setManualOutflow(value, timestamp, unit);
|
|
};
|
|
|
|
exports.setDemand = (source, msg, ctx) => {
|
|
const log = _logger(source, ctx);
|
|
// generalFunctions/commandRegistry's _normaliseUnits has already converted
|
|
// msg.payload to m3/h (the descriptor's units.default — see
|
|
// commands/index.js). Accepts {value, unit} objects upstream; we just read
|
|
// the normalized number here. _manualDemand is stored in m3/h, no further
|
|
// conversion needed.
|
|
const demand = Number(msg?.payload);
|
|
if (!Number.isFinite(demand)) {
|
|
log?.warn?.(`set.demand: invalid Qd value '${JSON.stringify(msg?.payload)}'`);
|
|
return;
|
|
}
|
|
if (source.mode !== 'manual') {
|
|
log?.debug?.(
|
|
`set.demand ignored in '${source.mode}' mode; switch to manual to use the demand slider`
|
|
);
|
|
return;
|
|
}
|
|
// forwardDemandToChildren returns a promise — surface failures via logger.
|
|
Promise.resolve(source.forwardDemandToChildren(demand)).catch((err) => {
|
|
log?.error?.(`set.demand: failed to forward demand: ${err && err.message}`);
|
|
});
|
|
};
|