Files
rotatingMachine/wiki/Reference-Examples.md
znetsixe 5ea0b0bda6 feat(state): honor sequenceAbortToken so external aborts cleanly break sequences
Consumer half of the abort-token mechanism added in generalFunctions
state.js. executeSequence captures host.state.sequenceAbortToken at
entry, then re-checks before every state transition and after the
optional ramp-down. If MGC (or any external caller) bumps the token
mid-sequence, the loop bails out cleanly — no more barge-through where
a pre-empted shutdown advances through stopping → coolingdown after a
fresh demand has already engaged the pump.

Without this the MGC rendezvous planner can't reliably re-dispatch a
pump that's mid-shutdown: the new flowmovement claims the gate, but
the old shutdown's for-loop keeps running on microtasks and steps the
FSM into idle/off underneath it.

Also: wiki regen following the same visual-first 14-section template as
the other EVOLV nodes — Reference-{Architecture,Contracts,Examples,
Limitations}.md split with _Sidebar.md index.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-17 19:44:48 +02:00

9.9 KiB
Raw Blame History

Reference — Examples

code-ref

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

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 &rarr; starting &rarr; warmingup &rarr; operational. runtime starts accumulating.
  4. Click set.setpoint = 60 (control %). state goes operational &rarr; accelerating &rarr; 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 &rarr; decelerating &rarr; stopping &rarr; coolingdown &rarr; 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 16 + 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 &rarr; decelerating &rarr; ….
  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 &rarr; coolingdown &rarr; 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:

# 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.


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 <source> is not allowed in mode <mode> or <action> is not allowed in mode <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 <type>.<variant>.<position>.<childId> (e.g. flow.predicted.downstream.default). MGC uses <position>_<variant>_<type>. 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.


Page Why
Home Intuitive overview
Reference — Contracts Topic + config + child filters
Reference — Architecture Code map, FSM, prediction + drift pipeline
Reference — Limitations Known issues and open questions
machineGroupControl — Examples Group-control demo flows
EVOLV — Topology Patterns Where rotatingMachine fits in a larger plant