Files
rotatingMachine/wiki/Home.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.0 KiB
Raw Blame History

rotatingMachine

code-ref s88 status

A rotatingMachine models a single pump, compressor, or blower. It loads a supplier characteristic curve, takes upstream + downstream pressure measurements (real or simulated), predicts the resulting flow + power, drives a startup / shutdown state machine, and assesses prediction drift against measured flow / power. Used as a child of machineGroupControl when grouped, or directly under pumpingStation for a one-pump station.


At a glance

Thing Value
What it represents One rotating asset on a curve — pump, blower, compressor
S88 level Equipment Module
Use it when You have a curve-modelled asset whose flow / power varies with header differential and you want predictions + drift
Don't use it for Passive non-return valves (valve), curveless assets (will silently emit zeros), groups (parent under machineGroupControl)
Children it accepts measurement (pressure / flow / power / temperature)
Parents it talks to machineGroupControl, pumpingStation, or any node that issues flowmovement / execsequence

How it fits

flowchart LR
    parent[machineGroupControl /<br/>pumpingStation]:::unit -->|flowmovement<br/>execsequence| rm[rotatingMachine<br/>Equipment]:::equip
    m_up[measurement<br/>pressure upstream]:::ctrl -.measured.-> rm
    m_dn[measurement<br/>pressure downstream]:::ctrl -.measured.-> rm
    sim[dashboard-sim-upstream /<br/>dashboard-sim-downstream<br/>(auto-registered virtual children)]:::ctrl -.measured.-> rm
    rm -->|child.register| parent
    rm -.->|flow.predicted.*<br/>power.predicted.atequipment| parent
    classDef unit fill:#50a8d9,color:#000
    classDef equip fill:#86bbdd,color:#000
    classDef ctrl fill:#a9daee,color:#000

S88 colours are anchored in .claude/rules/node-red-flow-layout.md.


Try it — 3-minute demo

Import the basic example flow, deploy, and drive a single pump through the full state machine.

curl -X POST -H 'Content-Type: application/json' \
  --data @nodes/rotatingMachine/examples/01\ -\ Basic\ Manual\ Control.json \
  http://localhost:1880/flow

What to click after deploy (the inject buttons map one-to-one to topics in Reference — Contracts):

  1. data.simulate-measurement (upstream + downstream) — injects ~0 mbar suction and ~1100 mbar discharge so the predictor has something to work with.
  2. set.mode = virtualControl — lets the GUI source drive the pump (parent path is for grouped use).
  3. cmd.startup — FSM runs idle &rarr; starting &rarr; warmingup &rarr; operational. runtime starts accumulating.
  4. set.setpoint = 60 (control %) — pump ramps from 0 to 60 at the configured Reaction Speed; state goes operational &rarr; accelerating &rarr; operational.
  5. 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. cmd.shutdownoperational &rarr; decelerating &rarr; stopping &rarr; coolingdown &rarr; idle.

Important

GIF needed. Demo recording of steps 16 with the live status panel. Save as wiki/_partial-gifs/rotatingMachine/01-basic-demo.gif, target ≤ 1 MB after gifsicle -O3 --lossy=80.


The seven things you'll send

Topic Aliases Payload What it does
set.mode setMode "auto" | "virtualControl" | "fysicalControl" Switch between parent-controlled, GUI-controlled, and physical-source-only. Each mode has its own allow-list for actions and sources.
cmd.startup any Run the configured startup sequence (default [starting, warmingup, operational]).
cmd.shutdown any Run the configured shutdown sequence (default [stopping, coolingdown, idle]). operational triggers a ramp-to-zero first.
cmd.estop emergencystop any Hard cut: runs the emergencystop sequence (default [emergencystop, off]). Reachable from every state.
set.setpoint execMovement {setpoint: number} (control %) Move to a control-% setpoint.
set.flow-setpoint flowMovement {setpoint: number} (flow, unit per units) Move to a flow setpoint. Converted to canonical m³/s, then to control % via predictCtrl.
data.simulate-measurement simulateMeasurement {asset: {type, unit}, value, position, childId?} Inject a virtual sensor reading (pressure / flow / power / temperature).

Plus two query topics for dashboards:

Topic Aliases Returns on the reply port
query.curves showWorkingCurves The working curves (flow / power / efficiency) at the current operating point.
query.cog CoG The centre-of-gravity (CoG) of the η curve.

What you'll see come out

Sample Port 0 message (delta-compressed, while operational at ~60 % control):

{
  "topic": "rotatingMachine#pump_a",
  "payload": {
    "state": "operational",
    "ctrl": 60.0,
    "mode": "auto",
    "runtime": 0.024,
    "flow.predicted.downstream.default": 12.4,
    "flow.predicted.atequipment.default": 12.4,
    "power.predicted.atequipment.default": 18.2,
    "pressure.measured.upstream.dashboard-sim-upstream": 0,
    "pressure.measured.downstream.dashboard-sim-downstream": 1100,
    "predictionQuality": "good",
    "predictionConfidence": 0.92,
    "predictionPressureSource": "dashboard-sim",
    "predictionFlags": [],
    "cog": 0.62, "NCog": 0.71, "NCogPercent": 62,
    "effDistFromPeak": 0.04, "effRelDistFromPeak": 0.12
  }
}

Key shape: <type>.<variant>.<position>.<childId> — the inverse of MGC's key shape, because rotatingMachine emits per-measurement snapshots. The trailing <childId> is the registering child's id (dashboard-sim-upstream, dashboard-sim-downstream, or default for own predictions). Position labels are normalised to lowercase in keys.

Field Meaning
state Current FSM state. See Architecture — FSM.
ctrl Control-axis position (0..100).
mode One of auto / virtualControl / fysicalControl.
runtime Accumulated hours in active states (operational and movement variants).
flow.predicted.{downstream,atequipment}.default Predicted flow at the current operating point (canonical m³/s; renders to m3/h).
power.predicted.atequipment.default Predicted shaft power (canonical W; renders to kW).
predictionQuality good / warming / degraded / invalid — derived by predictionHealth from drift + pressure availability.
predictionPressureSource dashboard-sim (virtual children active) or a real-child id (real children preferred).
predictionFlags Reason codes when health < good (e.g. pressure_init_warming, flow_high_drift).
cog / NCog / NCogPercent Centre-of-gravity metric on the η curve. NCog is normalised 0..1.
effDistFromPeak / effRelDistFromPeak Distance from the η peak (absolute and 0..1 relative).

The new bit — sequence-abort token

When a parent MGC sends a new demand, it calls abortMovement to interrupt any in-flight accelerating / decelerating movement. Before 2026-05-15 that abort only stopped the moveTo — an in-flight executeSequence('shutdown') for-loop would keep transitioning the FSM through stopping &rarr; coolingdown &rarr; idle, fighting the new dispatch's residue-handler.

The pump now carries a monotonic sequenceAbortToken on its state object. External aborts (the kind MGC fires) advance it; sequence-internal aborts (e.g. shutdown's own pre-empt of its ramp-down step) do not. executeSequence captures the token at entry and bails out before its next transition if the counter has advanced.

Net effect: a mid-decel re-engage takes the pump cleanly back to operational, without the orphaned shutdown completing in the background. warmingup and coolingdown remain protected at the stateManager layer — safety guarantees are unchanged.

See Architecture — FSM for the full mechanism.


Need more?

Page What you'll find
Reference — Contracts Full topic contract, config schema, child registration filters
Reference — Architecture Code map, FSM, prediction pipeline, drift, lifecycle
Reference — Examples Shipped example flows + debug recipes
Reference — Limitations When not to use, known limitations, open questions

EVOLV master wiki · Topology Patterns · Topic Conventions