groupcontrol.test.js comment confirms `setScaling is gone — handleInput now takes canonical m³/s directly` since the refactor. CONTRACT.md still listed it; contract-verify now agrees with the registry. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
3.7 KiB
machineGroupControl — Contract
Hand-maintained for Phase 4; 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 prioritycontrol, optimalcontrol, dynamiccontrol, … |
Switches the control strategy via source.setMode(payload). |
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). |
set.demand |
Qd |
numeric (number or numeric string) | Calls source.handleInput('parent', parseFloat(payload)). On success, replies on Port 0 with topic = source.config.general.name, payload = 'done'. Non-numeric payloads log error and are skipped. |
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). On a successfulset.demanddispatch the node additionally emits{ topic: <name>, payload: 'done' }as an acknowledgement. - Port 1 (InfluxDB telemetry): same shape as Port 0, formatted with the
'influxdb'formatter. - Port 2 (registration): at startup the node sends one
{ topic: 'registerChild', payload: <node.id>, positionVsParent }to the upstream parent.
Events emitted by source.measurements.emitter
The MeasurementContainer fires <type>.<variant>.<position> whenever
the corresponding series receives a new value. Parents subscribe via the
generic child.measurements.emitter.on(eventName, ...) handshake.
machineGroupControl publishes:
flow.predicted.atequipment— aggregated predicted group flow (sum of member-machine predicted flows at the group operating point).flow.predicted.downstream— mirror of the live group flow seen at the discharge header (written byhandlePressureChangefor downstream consumers such as pumpingStation).power.predicted.atequipment— aggregated predicted group power.efficiency.predicted.atequipment— group efficiency = flow/power at the selected operating point.Ncog.predicted.atequipment— group normalised cost-of-goods score.pressure.measured.upstream,pressure.measured.downstream,pressure.measured.differential— mirrored from header-side measurement children (asset.type='pressure'), when registered.
The exact set is data-driven by which children register and what they publish; downstream consumers should subscribe by event name, not assume a fixed catalogue.
Children registered by this node
machineGroupControl accepts two softwareTypes through the
childRegistrationUtils handshake:
machine— a rotatingMachine. Stored insource.machines[id]. The group subscribes to its child'spressure.measured.differential,pressure.measured.downstream, andflow.predicted.downstreamevents to triggerhandlePressureChange.measurement— a header-side sensor (typically a pressure transmitter at the discharge or suction manifold). The group subscribes to the matching<asset.type>.measured.<positionVsParent>event and mirrors the value into its own MeasurementContainer; pressure events also triggerhandlePressureChangeso optimalControl can use ONE header operating point for all pumps.
Position labels accepted from children are upstream, downstream,
atequipment (and case variants — normalised internally).