Refactor of reactor to use BaseNodeAdapter + commandRegistry + statusBadge. reactor follows the platform refactor plan in .claude/refactor/MODULE_SPLIT.md. Tests stay green; CONTRACT.md generated; legacy aliases preserved. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
3.1 KiB
3.1 KiB
reactor — Contract
Hand-maintained for Phase 6; the ## Inputs table is generated from
src/commands/index.js (see Phase 9 generator). Keep ≤ 80 lines.
Inputs (msg.topic on Port 0)
| Canonical | Aliases (deprecated) | Payload | Effect |
|---|---|---|---|
data.clock |
clock |
msg.timestamp (ms since epoch) |
Calls source.updateState(timestamp) — advances the ASM kinetics integrator by n_iter time steps that fit between currentTime and the supplied timestamp (scaled by speedUpFactor). |
data.fluent |
Fluent |
{ inlet: number, F: number, C: number[13] } |
Writes the per-inlet flow rate (F, m³/d) and concentration vector (C) into engine.Fs[inlet] / engine.Cs_in[inlet]. |
data.otr |
OTR |
numeric | Sets the externally-supplied oxygen transfer rate (used when kla is NaN). |
data.temperature |
Temperature |
numeric or { value: number } |
Sets engine.temperature (°C). Non-numeric payloads are warned and ignored. |
data.dispersion |
Dispersion |
numeric | PFR only — sets the axial dispersion coefficient D (m²/d). |
child.register |
registerChild |
child node id (string) | Looks up the sibling node via RED.nodes.getNode(id) and delegates to source.childRegistrationUtils.registerChild with msg.positionVsParent. |
Aliases log a one-time deprecation warning the first time they fire.
Outputs (msg.topic on Port 0/1/2)
- Port 0 (process): every tick emits the engine's effluent:
{ topic: 'Fluent', payload: { inlet: 0, F, C: number[13] }, timestamp }. For a PFR an additional{ topic: 'GridProfile', payload: { grid, n_x, d_x, length, species, timestamp } }message goes out on the same port before the effluent. - Port 1 (InfluxDB telemetry): formatted via
outputUtils.formatMsg(..., 'influxdb')fromgetOutput()— carriesflow_total,temperature, and one field per ASM3 species (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). - Port 2 (registration): at startup the node sends one
{ topic: 'child.register', payload: <node.id>, positionVsParent, distance }to its parent.
Events emitted by source.emitter
stateChange— fires after everyupdateState()that advances the integrator. Payload is the newcurrentTime(ms since epoch). Downstream reactors register viachild.registerand subscribe to this event to pull the upstream effluent on each advance.output-changed— base notification fired byupdateState()so the BaseNodeAdapter pipeline pushes outputs (currently used only as a heartbeat; effluent is emitted directly from the periodic tick).
Children accepted
measurement— subscribes to<type>.measured.<position>on the child'smeasurements.emitter. Recognised reconciliations:temperature.measured.atEquipmentwritesengine.temperature; PFR additionally honoursquantity (oxygen).measured.<distance>to reconcile dissolved-oxygen concentration into the nearest grid cell.reactor— registers as the upstream reactor; the downstreamupdateStatepulls the upstream effluent intoFs[0]/Cs_in[0]before integrating.