# 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 by `outputUtils.formatMsg(..., 'process')` from `getOutput()` — delta-compressed (only changed fields are emitted). Output keys follow `__` plus `mode` and `maxDeltaP`. - **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: , 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 by `setReconcileIntervalSeconds`; 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 in `source.valves[id]`. VGC binds to the child's `positionChange` (via `child.state.emitter`) and `deltaPChange` (via `child.emitter`) events to re-distribute flow and re-compute group max delta-P. - `machine` / `rotatingmachine` / `machinegroup` / `machinegroupcontrol` / `pumpingstation` / `valvegroupcontrol` — an upstream **source**. Stored in `source.sources[id]`. VGC subscribes to the source's `flow.predicted.*` / `flow.measured.*` events to drive `updateFlow`, and reads the child's `getFluidContract()` (if present) plus `fluidContractChange` events 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).