Refactor of valveGroupControl to use the platform infrastructure (BaseDomain, BaseNodeAdapter, ChildRouter, commandRegistry, statusBadge). Extracts concerns into focused modules per .claude/refactor/MODULE_SPLIT.md generic template. Tests stay green; CONTRACT.md generated; legacy aliases preserved. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
3.9 KiB
3.9 KiB
valveGroupControl — 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 |
|---|---|---|---|
set.mode |
setMode |
string — one of auto, virtualControl, fysicalControl, maintenance |
Switches the control strategy via source.setMode(payload). |
set.position |
setpoint |
any |
Reserved for future per-valve positional override; currently a debug-logged no-op pending Phase 7. |
child.register |
registerChild |
string — the child node's Node-RED id |
Resolves the child via RED.nodes.getNode and registers it through childRegistrationUtils.registerChild(childObj.source, msg.positionVsParent). |
cmd.execSequence |
execSequence |
{ source, action, parameter } |
Forwards to source.handleInput(source, action, parameter). |
data.totalFlow |
totalFlowChange |
numeric, { value, position?, variant?, unit? }, or { source, action, ... } |
Updates total measured/predicted flow at the configured position; drives calcValveFlows to re-distribute across valves. |
cmd.emergencyStop |
emergencyStop, emergencystop |
optional { source } |
Runs the emergencystop sequence via handleInput. |
set.reconcileInterval |
setReconcileInterval |
numeric — seconds (> 0) | Re-tunes the periodic flow-reconciliation interval. Min clamp 100 ms. |
Aliases log a one-time deprecation warning the first time they fire.
Outputs (msg.topic on Port 0/1/2)
- Port 0 (process):
msg.topic = config.general.name. Payload built byoutputUtils.formatMsg(..., 'process')fromgetOutput()— delta-compressed (only changed fields are emitted). Output keys follow<position>_<variant>_<type>plusmodeandmaxDeltaP. - Port 1 (InfluxDB telemetry): same shape as Port 0, formatted with the
'influxdb'formatter. - Port 2 (registration): at startup the node sends one
{ topic: 'child.register', payload: <node.id>, positionVsParent }to the upstream parent.
Events emitted by source.emitter / source.measurements.emitter
output-changed(source.emitter) — public output state shifted; the adapter listens and pushes Ports 0/1.fluidContractChange(source.emitter) — group-level fluid contract (status / serviceType / sourceCount) changed. Parents (e.g. an upstream valve registering this VGC as its parent) subscribe to react.reconcileIntervalChange(source.emitter) — emitted bysetReconcileIntervalSeconds; the adapter restarts the tick loop.flow.predicted.atequipment(source.measurements.emitter) — total predicted group flow (sum of per-valve assigned flows).pressure.predicted.deltaMax(source.measurements.emitter) — max delta-P across registered valves.
The exact set is data-driven by which sources/valves register and what they publish; downstream consumers subscribe by event name.
Children registered by this node
valveGroupControl accepts two child classes through the
childRegistrationUtils handshake:
valve— an individual valve. Stored insource.valves[id]. VGC binds to the child'spositionChange(viachild.state.emitter) anddeltaPChange(viachild.emitter) events to re-distribute flow and re-compute group max delta-P.machine/rotatingmachine/machinegroup/machinegroupcontrol/pumpingstation/valvegroupcontrol— an upstream source. Stored insource.sources[id]. VGC subscribes to the source'sflow.predicted.*/flow.measured.*events to driveupdateFlow, and reads the child'sgetFluidContract()(if present) plusfluidContractChangeevents to aggregate the group's upstream service type (getFluidContract()exposes the resolved view).
Position labels accepted from children are upstream, downstream,
atEquipment (and case variants — normalised internally).