# Reference — Examples ![code-ref](https://img.shields.io/badge/code--ref-394a972-blue) > [!NOTE] > Every example flow shipped under `nodes/rotatingMachine/examples/`, plus how to load them, what they show, and the debug recipes that go with them. Live source: `nodes/rotatingMachine/examples/`. --- ## Shipped examples | File | Tier | Dependencies | What it shows | |:---|:---:|:---|:---| | `01 - Basic Manual Control.json` | 1 | EVOLV only | Single pump driven by inject buttons — mode switching, startup / shutdown / e-stop, control-% and flow-unit setpoints, simulated pressures, maintenance enter / leave. Debug taps on all three ports. | | `02 - Integration with Machine Group.json` | 2 | EVOLV only | Parent-child demo — one `machineGroupControl` with 2 `rotatingMachine` children. Auto-registration via Port 2 on deploy. Per-pump simulated pressures. | | `03 - Dashboard Visualization.json` | 3 | EVOLV + `@flowfuse/node-red-dashboard` | FlowFuse charts: flow / power / pressure trends, status panel, per-pump controls. | Three legacy files (`basic.flow.json`, `integration.flow.json`, `edge.flow.json`) are kept until the new Tier-2 has been fully Docker-validated; they predate the AssetResolver refactor and may need re-save in the editor before they deploy. --- ## Loading a flow ### Via the editor 1. Open the Node-RED editor at `http://localhost:1880`. 2. Menu → Import → drag the JSON file. 3. Click Deploy. (The numbered files contain spaces; in the editor's import dialog the filename is purely cosmetic.) ### Via the Admin API ```bash curl -X POST -H 'Content-Type: application/json' \ --data @"nodes/rotatingMachine/examples/01 - Basic Manual Control.json" \ http://localhost:1880/flows ``` --- ## Example 01 — Basic Manual Control Single-pump flow with one of every input you'd ever send. Validated against a live Node-RED instance (2026-03-05). ### Nodes on the tab | Type | Purpose | |:---|:---| | `comment` | Tab header / driver-group labels | | `inject` × 9 | Mode (auto / virtualControl), startup, shutdown, e-stop, setpoint = 30 / 60 / 100 %, simulated upstream + downstream pressures, simulate flow / power for drift | | `rotatingMachine` | The unit under test | | `debug` × 3 | Port 0 (process), Port 1 (telemetry), Port 2 (registration) | ### What to do after deploy 1. Click the two pressure simulations (upstream = 0 mbar, downstream = 1100 mbar). Once both land, `predictionPressureSource` flips from `null` to `dashboard-sim` and `predictionFlags` drops the `pressure_init_warming` flag. 2. Click `set.mode = virtualControl` so the GUI source is allowed. 3. Click `cmd.startup`. Watch Port 0 in the debug pane: `state` walks `idle → starting → warmingup → operational`. `runtime` starts accumulating. 4. Click `set.setpoint = 60` (control %). `state` goes `operational → accelerating → operational`; `ctrl` rises from 0 to 60 at the configured `Reaction Speed`. `flow.predicted.downstream.default` and `power.predicted.atequipment.default` update at every position tick. 5. Click `set.flow-setpoint = {value: 80, unit: 'm3/h'}` — same path, but the setpoint is a flow value; the node converts via `predictCtrl` to a control %. 6. Click `cmd.shutdown`. State: `operational → decelerating → stopping → coolingdown → idle`. The ramp-to-zero step is interruptible; the subsequent transitions are timed by `time.stopping` and `time.coolingdown`. > [!IMPORTANT] > **GIF needed.** Demo recording of steps 1–6 + the status badge progression. Save as `wiki/_partial-gifs/rotatingMachine/01-basic-demo.gif`, target ≤ 1 MB after `gifsicle -O3 --lossy=80`. ### Try the residue handler After the pump reaches `operational` at 60 %: 1. Send `set.setpoint = 20`. `state` goes `operational → decelerating → …`. 2. While `decelerating`, send `set.setpoint = 80`. 3. `state.moveTo` sees the residue, transitions back to `operational` synchronously, then ramps up to 80. No setpoint is lost. This is the same mechanism the MGC planner relies on for fast retargets. ### Try the sequence-abort token After the pump reaches `operational` at 60 %, simulate the Scenario-5 race: 1. Send `cmd.shutdown`. The pump begins ramping to zero. 2. *Within the ramp window*, send `set.setpoint = 60`. The new setpoint's residue-handler claims the FSM back to `operational`. 3. Watch the log: instead of the shutdown's for-loop continuing through `stopping → coolingdown → idle`, you'll see `Sequence 'shutdown' interrupted during ramp-down by external abort; not entering shutdown loop.` Without the token (pre-2026-05-15), the pump would have ended at `idle` despite the new setpoint — with `delayedMove = 60` sitting unused. --- ## Example 02 — Integration with Machine Group > [!IMPORTANT] > **Screenshot needed.** Editor capture of `02 - Integration with Machine Group.json`. Save as `wiki/_partial-screenshots/rotatingMachine/02-integration.png`. Replace this callout with the image link. One MGC + two rotatingMachine children. Demonstrates: - Auto-registration via Port 2 at deploy (each pump's `child.register` reaches the MGC; no manual wiring needed). - Independent per-pump controls (the injects still target each pump's input by id). - Group-level aggregation: MGC's Port 0 sums the children's predicted flow + power into the group aggregate. The MGC planner is exercised when MGC's `set.demand` fires (not in this example by default; add an inject if you want to see it). --- ## Example 03 — Dashboard Visualization > [!IMPORTANT] > **Screenshots needed.** Two captures: the editor tab and the rendered dashboard. Save as `wiki/_partial-screenshots/rotatingMachine/03-dashboard-editor.png` and `04-dashboard-rendered.png`. A single pump on a FlowFuse Dashboard 2.0 page with: - Control buttons (mode, startup, shutdown, e-stop) - A setpoint slider - Live status (state badge, ctrl%, predicted flow / power / efficiency) - Trend charts: flow, power, pressure, drift level Required: `@flowfuse/node-red-dashboard` installed in the Node-RED instance. --- ## Docker compose snippet To bring up Node-RED + InfluxDB with EVOLV nodes pre-loaded: ```yaml # docker-compose.yml (extract) services: nodered: build: ./docker/nodered ports: ['1880:1880'] volumes: - ./docker/nodered/data:/data/evolv influxdb: image: influxdb:2.7 ports: ['8086:8086'] ``` Full file: [EVOLV/docker-compose.yml](https://gitea.wbd-rd.nl/RnD/EVOLV/src/branch/development/docker-compose.yml). --- ## Debug recipes | Symptom | First thing to check | Where to look | |:---|:---|:---| | Editor throws `legacy asset field(s) [supplier]` on deploy | Flow predates the AssetResolver refactor. Re-open the node, pick the model from the asset menu, save. The registry derives supplier / category / type. | `src/nodeClass.js` `_rejectLegacyAssetFields`. | | `state` stuck on `idle` after `cmd.startup` | The action isn't allowed for this mode / source combination. Check `flowController` warn log for ` is not allowed in mode ` or ` is not allowed in mode `. | `_setupState`, `isValidSourceForMode`, `isValidActionForMode`. | | `flow.predicted.*` reads `0` or `NaN` | Pressure hasn't initialised. `predictionFlags` will include `pressure_init_warming`. Inject pressure via `data.simulate-measurement` or wire real measurement children. | `getMeasuredPressure` + `pressureSelector`. | | `predictionQuality: 'invalid'` from startup | Curve normalisation failed — null predictors installed. Look for `Curve normalization failed for model …` in the log. The asset / model is unrecognised, the unit isn't a flow unit, or the registry entry is missing. | `_setupCurves`. | | Drift level stays at `3` after startup | Fewer than `minSamplesForLongTerm = 10` paired samples have landed. Wait ~10 ticks; the level falls automatically. | `driftProfiles.minSamplesForLongTerm`. | | `cmd.estop` and then the pump won't restart | Allowed transitions out of `emergencystop` are `idle` / `off` / `maintenance`. Send `cmd.shutdown` to drop into `idle`, then `cmd.startup`. | `stateConfig.allowedTransitions.emergencystop`. | | Position bounces near the target | `dynspeed` (cubic ease-in-out) can overshoot at high speed. Try `staticspeed` (linear). Both modes have the same total duration. | `movement.mode`. | | Pump still drifts to `idle` after a mid-shutdown re-engage | Verify the submodule is at `394a972` or newer — the sequence-abort token in `state.js` + `sequenceController.js` is what closes that race. | `state.sequenceAbortToken`. | | `data.simulate-measurement` payloads aren't reflected on Port 0 | Payload shape: `{asset: {type: 'pressure', unit: 'mbar'}, value: 1100, position: 'downstream', childId: 'dashboard-sim-downstream'}`. Missing `asset.type` or `position` gets a `Unsupported simulateMeasurement type:` warn and is dropped. | `measurementHandlers.updateSimulatedMeasurement`. | | Per-pump Port 0 key names differ from what your dashboard expects | rotatingMachine uses `...` (e.g. `flow.predicted.downstream.default`). MGC uses `__`. Don't mix them. | `io/output.js`, `MeasurementContainer.getFlattenedOutput`. | > Never ship `enableLog: 'debug'` in a demo — fills the container log within seconds and obscures real errors. --- ## Related pages | Page | Why | |:---|:---| | [Home](Home) | Intuitive overview | | [Reference — Contracts](Reference-Contracts) | Topic + config + child filters | | [Reference — Architecture](Reference-Architecture) | Code map, FSM, prediction + drift pipeline | | [Reference — Limitations](Reference-Limitations) | Known issues and open questions | | [machineGroupControl — Examples](https://gitea.wbd-rd.nl/RnD/machineGroupControl/wiki/Reference-Examples) | Group-control demo flows | | [EVOLV — Topology Patterns](https://gitea.wbd-rd.nl/RnD/EVOLV/wiki/Topology-Patterns) | Where rotatingMachine fits in a larger plant |