# rotatingMachine — Contract Hand-maintained for Phase 5; the `## Inputs` table is generated from `src/commands/index.js` (see Phase 9 generator). Keep ≤ 100 lines. ## Inputs (msg.topic on Port 0) | Canonical | Aliases (deprecated) | Payload | Effect | |---|---|---|---| | `set.mode` | `setMode` | `string` — one of the allowed mode names | Calls `source.setMode(payload)`. | | `cmd.startup` | — | `{ source?: string }` | Calls `source.handleInput(payload.source ?? 'parent', 'execSequence', 'startup')`. | | `cmd.shutdown` | — | `{ source?: string }` | Calls `source.handleInput(payload.source ?? 'parent', 'execSequence', 'shutdown')`. | | `cmd.estop` | `emergencystop` | `{ source?: string, action?: string }` | Calls `source.handleInput(payload.source ?? 'parent', payload.action ?? 'emergencystop')`. | | `execSequence` | — (legacy umbrella) | `{ source, action, parameter }` with `action ∈ {'startup','shutdown'}` | Content-based router: forwards to `cmd.startup` / `cmd.shutdown` handler based on `payload.action`. Unknown action logs `warn` and is dropped. Whole topic is legacy — prefer the canonical `cmd.*` topics. | | `set.setpoint` | `execMovement` | `{ source, action, setpoint }` — setpoint coerced to `Number` | Calls `source.handleInput(payload.source ?? 'parent', payload.action ?? 'execMovement', Number(payload.setpoint))`. | | `set.flow-setpoint` | `flowMovement` | `{ source, action, setpoint }` | Calls `source.handleInput(payload.source ?? 'parent', payload.action ?? 'flowMovement', Number(payload.setpoint))`. | | `data.simulate-measurement` | `simulateMeasurement` | `{ type, position?, value, unit, timestamp? }` — `type ∈ {pressure, flow, temperature, power}`; `position` defaults to `'atEquipment'` | Validated dispatch: rejects non-finite `value`, unsupported `type`, missing `unit`, or unit that fails `isUnitValidForType`. Pressure routes via `updateSimulatedMeasurement(type, position, value, ctx)`; flow/temperature/power route via `updateMeasured(value, position, ctx)`. The injected `childId/childName = 'dashboard-sim'` marks the source. | | `query.curves` | `showWorkingCurves` | none | Calls `source.showWorkingCurves()` and replies on **Port 0** with `{ topic: 'showWorkingCurves', payload: }` via `ctx.send`. | | `query.cog` | `CoG` | none | Calls `source.showCoG()` and replies on **Port 0** with `{ topic: 'showCoG', payload: }`. | | `child.register` | `registerChild` | `string` — child Node-RED id; `msg.positionVsParent` carries position | Resolves child via `RED.nodes.getNode(payload)` and registers it through `childRegistrationUtils.registerChild(child.source, msg.positionVsParent)`. Unknown ids log `warn`. | Aliases log a one-time deprecation warning the first time they fire. ### `execSequence` demux The pre-refactor topic `execSequence` carried `{ source, action, parameter }` where `action` selected the verb (`startup` or `shutdown`). The command registry does not natively dispatch by payload content, so `execSequence` keeps its own descriptor whose handler **forwards directly** to the canonical `cmd.startup` / `cmd.shutdown` handler based on `payload.action`. The deprecation warning fires once. Future-Phase-7 removal of `execSequence` is a behavioural change — callers must migrate to `cmd.startup` / `cmd.shutdown`. ## 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). On `query.curves` / `query.cog` the node additionally emits `{ topic: 'showWorkingCurves' | 'showCoG', payload: }` as a synchronous reply on Port 0. - **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: , positionVsParent }` to the upstream parent (typically a `machineGroupControl` or `pumpingStation`). `positionVsParent` defaults to `'atEquipment'`. ## Events emitted by `source.measurements.emitter` The `MeasurementContainer` fires `..` whenever the corresponding series receives a new value. Parents subscribe via the generic `child.measurements.emitter.on(eventName, ...)` handshake. rotatingMachine publishes: - `flow.predicted.atequipment`, `flow.predicted.downstream`, `flow.predicted.max`, `flow.predicted.min` — predicted operating point. - `power.predicted.atequipment` — predicted shaft power. - `temperature.measured.atequipment` — ambient/process temperature. - `atmPressure.measured.atequipment` — barometric reference. - `pressure.measured.upstream`, `pressure.measured.downstream`, `pressure.measured.differential` — when pressure children register or `data.simulate-measurement type=pressure` runs. - `flow.measured.`, `power.measured.atequipment`, `temperature.measured.` — when sensor children register or the `data.simulate-measurement` topic supplies values. Position labels are normalised to lowercase in the event name. The exact set is data-driven by which children register and what they publish. ## Events emitted by `source.state.emitter` - `positionChange` — fires when the position percentage changes (per movement tick). Data: `{ position, state, mode, timestamp }`. - `stateChange` — fires on transitions of the operating state machine (`idle → starting → warmingup → operational → accelerating → decelerating → stopping → coolingdown → idle`, plus `off`, `maintenance`). Data: the new state string. ## Children registered by this node rotatingMachine accepts `measurement` children through the `childRegistrationUtils` handshake. Children typically have `asset.type ∈ {pressure, flow, power, temperature}`. The machine subscribes to the matching `.measured.` event and mirrors the value into its own `MeasurementContainer`. Two **virtual** children are reserved by the `data.simulate-measurement` topic: incoming simulated values are tagged with `childId/childName = 'dashboard-sim'` so dashboard-driven inputs are distinguishable from real sensor children in downstream telemetry. Position labels accepted from children are `upstream`, `downstream`, `atEquipment` (and case variants — normalised internally).