Files
EVOLV/wiki/manuals/nodes/rotatingMachine.md
znetsixe a1aa44f6ca
Some checks failed
CI / lint-and-test (push) Has been cancelled
docs: rotatingMachine trial-ready — submodule bumps, wiki manual, session note
Bumps:
- nodes/generalFunctions  024db55 -> 75d16c6  (FSM abort recovery + schema sync)
- nodes/rotatingMachine   07af7ce -> 17b8887  (interruptible sequences, dual-curve tests, rewritten README)

Wiki:
- wiki/manuals/nodes/rotatingMachine.md — new user manual covering inputs,
  outputs, state machine, supported curves, and troubleshooting.
- wiki/sessions/2026-04-13-rotatingMachine-trial-ready.md — session note
  with findings, fixes, test additions, and dual-curve E2E results.
- wiki/index.md — link both and bump updated date.

Status: rotatingMachine is now trial-ready. 91/91 unit tests green, live
Docker E2E verifies shutdown/emergency-stop during ramps and prediction
behaviour across both shipped pump curves (hidrostal-H05K-S03R,
hidrostal-C5-D03R-SHN1).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 13:22:10 +02:00

12 KiB
Raw Permalink Blame History

title, node, updated, status
title node updated status
rotatingMachine — User Manual rotatingMachine 2026-04-13 trial-ready

rotatingMachine — User Manual

The rotatingMachine node models a single pump, compressor, or blower. It runs an S88-style state machine, predicts flow and power from a supplier curve, and publishes process and telemetry data every second. It is the atomic control module beneath machineGroupControl and pumpingStation.

This manual is the operator-facing reference. For architecture and the 3-tier code layout see Node Architecture; for curve theory see 3D Pump Curves.

At a glance

Item Value
Node category EVOLV
Inputs 1 (message-driven)
Outputs 3 — process / dbase / parent
Tick period 1 s
State machine 10 states (S88)
Predictions curve-backed (nq flow, np power, reversed nq for ctrl)
Canonical units Pa, m³/s, W, K

Editor configuration

Field Default Meaning
Reaction Speed 1 Ramp rate in controller-position units per second. 1 = 1 %/s.
Startup Time 0 Seconds in the starting state.
Warmup Time 0 Seconds in the protected warmingup state.
Shutdown Time 0 Seconds in the stopping state.
Cooldown Time 0 Seconds in the protected coolingdown state.
Movement Mode staticspeed staticspeed = linear ramp; dynspeed = ease-in/out.
Process Output process Port 0 payload format: process (delta-compressed) / json / csv.
Database Output influxdb Port 1 payload format: influxdb line protocol / json / csv.
Asset (menu) Supplier, category, model (must match a curve file in generalFunctions/datasets), output flow unit, curve units.
Logger (menu) info, enabled Log level and toggle.
Position (menu) atEquipment upstream / atEquipment / downstream relative to parent. Icon and optional distance offset.

Tip. With Reaction Speed = 1 and Set 60% from idle, the controller takes ~60 s to reach 60 %. Scale Reaction Speed up to emulate a faster actuator (e.g. 20 gives 1 second per 20 % = 3 s to reach 60 %).

Input topics

Every command enters on the single input port. msg.topic selects the handler; msg.payload carries the arguments.

setMode

{ "topic": "setMode", "payload": "virtualControl" }

Valid values: auto, virtualControl, fysicalControl. The current mode gates which source may issue which action (mode/action/source policy lives in generalFunctions/src/configs/rotatingMachine.json).

execSequence

{ "topic": "execSequence",
  "payload": { "source": "GUI", "action": "execSequence", "parameter": "startup" } }

parameter values: startup, shutdown, entermaintenance, exitmaintenance. Case is normalized.

If a shutdown is issued while the machine is mid-ramp (accelerating / decelerating), the active movement is aborted and the shutdown proceeds as soon as the FSM has returned to operational.

execMovement

{ "topic": "execMovement",
  "payload": { "source": "GUI", "action": "execMovement", "setpoint": 60 } }

setpoint is expressed in controller units (0100 %).

flowMovement

{ "topic": "flowMovement",
  "payload": { "source": "parent", "action": "flowMovement", "setpoint": 150 } }

setpoint is expressed in the configured output flow unit (e.g. m³/h). The node converts flow → controller-% via the reversed nq curve and then drives execMovement.

emergencystop

{ "topic": "emergencystop",
  "payload": { "source": "GUI", "action": "emergencystop" } }

Aborts any active movement, runs the emergencystopoff transition. Allowed from every active state. Case-insensitive.

simulateMeasurement

Inject a dashboard-side measurement without wiring a sensor child. Useful for validation, smoke tests, demo flows.

{ "topic": "simulateMeasurement",
  "payload": { "type": "pressure", "position": "upstream", "value": 200, "unit": "mbar" } }

type: pressure / flow / temperature / power. unit is required and must be convertible to the canonical unit for the type.

Diagnostics

  • showWorkingCurves — snapshot of current curve slices + computed metrics; reply on port 0.
  • CoG — current centre-of-gravity (peak efficiency point) indicators; reply on port 0.

registerChild

Internal. Sensor children (typically measurement nodes) send this to bind themselves to the machine. The machine also emits one on port 2 shortly after deploy so a parent group/station can register it.

Output ports

Port 0 — process data

Delta-compressed payload. Only changed fields are emitted each tick. Keys use a 4-segment format:

<type>.<variant>.<position>.<childId>

Examples:

Key Meaning
flow.predicted.downstream.default predicted flow at discharge
flow.predicted.atequipment.default predicted flow at equipment
power.predicted.atequipment.default predicted electrical power draw
pressure.measured.downstream.dashboard-sim-downstream simulated discharge pressure
pressure.measured.upstream.<childId> real upstream sensor reading
state current FSM state
mode current mode
ctrl current controller position (0100 %)
NCog / cog normalized / absolute centre-of-gravity
runtime cumulative operational hours

Consumers must cache and merge deltas. The example flow 01 - Basic Manual Control.json includes a function node that does exactly this — reuse its logic in your own flows.

Port 1 — dbase (InfluxDB)

InfluxDB line-protocol payload formatted for the telemetry bucket. Tags are low-cardinality fields (node name, machine type); measurements are numeric values. See the InfluxDB Schema Design page for the full tag/field contract.

Port 2 — parent

{ topic: "registerChild", payload: <this-node-id>, positionVsParent } — emitted once ~180 ms after deploy so a downstream parent group can discover this machine. Subsequent commands and data flow through the parent's input port.

State machine

                    ┌────────────────────────────┐
                    │          operational        │◄────┐
                    └────┬──────────┬────────┬────┘     │
                         │          │        │          │
        execMovement     │          │        │          │
        execMovement     │          │        │          │
            ▼            ▼          ▼        ▼          │
      accelerating   decelerating   │    emergencystop ─► off
            │            │          │
            └─── (abort)─┘          │
                         │          │
                    ┌────▼──────────▼────┐
                    │      stopping       │
                    └────────┬─────────────┘
                             │
                       coolingdown
                             │
                           idle
                             │
                          starting
                             │
                         warmingup
                             │
                       (operational)

Protected states (cannot be aborted by a new command): warmingup, coolingdown.

Interruptible states: accelerating, decelerating. A shutdown or emergencystop issued during a ramp aborts the ramp and drives the FSM correctly to idle / off.

Active states (contribute to runtime): operational, starting, warmingup, accelerating, decelerating.

Predictions and pressure

Flow and power are curve-backed. The curve set is indexed by the differential pressure across the machine:

  1. Best: both upstream and downstream pressures present → real Δp.
  2. Degraded: only one side present → falls back to that side with a warn.
  3. Minimum: no pressure → fDimension = 0; flow and power predictions use the lowest curve slice and will look unrealistic.

Pressure sources are resolved in priority order real sensor child > virtual dashboard child > aggregated fallback. Real-child values always win.

Predictions are only emitted while the FSM is in an active state (operational, starting, warmingup, accelerating, decelerating). In idle, stopping, coolingdown, off, maintenance the outputs are clamped to zero.

Supported curves and verification

Model Pressure envelope Flow envelope Power envelope
hidrostal-H05K-S03R 700 3900 mbar (33 slices) 9.5 227 m³/h 8.2 65.1 kW
hidrostal-C5-D03R-SHN1 400 2900 mbar (26 slices) 6.4 52.5 m³/h 0.55 31.5 kW

Both curves are covered by unit tests (test/integration/curve-prediction.integration.test.js) and a live E2E benchmark (test/e2e/curve-prediction-benchmark.py) that sweeps each pump through its own pressure × controller envelope. Last green run: 2026-04-13 — 12/12 samples per curve inside envelope, ctrl-monotonic, inverse-pressure monotonic.

Pressure out of envelope is not clamped. If a measured pressure falls below the curve's minimum slice, the node extrapolates and may produce implausibly large flow values (e.g. H05K at 400 mbar, ctrl 20 % → flow ≈ 30 000 m³/h; real envelope max is 227). Use realistic sensor ranges on your pressure measurement children.

Example flows

In the editor: Import ▸ Examples ▸ EVOLV ▸ rotatingMachine.

  • 01 - Basic Manual Control.json — single machine, inject-only. Good for smoke-testing a node installation.
  • 02 - Integration with Machine Group.jsonmachineGroupControl with two pumps as children. Good for verifying registration and parent orchestration.
  • 03 - Dashboard Visualization.json — FlowFuse dashboard with live charts. Depends on @flowfuse/node-red-dashboard.

Troubleshooting

Symptom Likely cause Fix
Editor says pressure not initialized, status ring is yellow No pressure child wired yet and no simulated pressure injected. Inject a simulateMeasurement of type pressure (both sides preferred) or wire a measurement child.
Predictions are enormous at ctrl = 0 % At near-zero controller position with high backpressure, the intercept of the curve gives a nominally-nonzero flow. This is a curve-data artefact, not a runtime bug. Confirm the curve with Rene / supplier data. For a conservative prediction use a lower Reaction Speed or constrain setpoint ≥ 10 %.
"Transition aborted" / "Movement aborted" in logs Expected during shutdown / emergencystop issued during a ramp — the fix path intentionally aborts the active move. None — informational only.
Status bar shows pressure not initialized even after inject simulateMeasurement payload missing unit or with a non-convertible value. Include unit (e.g. "mbar") and a finite number in value.
Shutdown does nothing and no error Machine is in warmingup or coolingdown (protected). Wait for the phase to complete (≤ configured seconds) and retry.

Running it locally

git clone --recurse-submodules https://gitea.wbd-rd.nl/RnD/EVOLV.git
cd EVOLV
docker compose up -d
# Node-RED: http://localhost:1880   InfluxDB: :8086   Grafana: :3000

Then in Node-RED: Import ▸ Examples ▸ EVOLV ▸ rotatingMachine ▸ 01 - Basic Manual Control.

Testing

cd nodes/rotatingMachine
npm test

Unit tests (79) cover construction, mode gating, sequences, interruptible movement, emergency stop, shutdown, efficiency/CoG, pressure initialization, output formatting, listener cleanup. See also examples/README.md for the flow-level test matrix.

Production status

See the project memory entry node_rotatingMachine.md for the latest benchmarks and wishlist. Trial-ready as of 2026-04-13 following the interruptibility + schema-sync fixes documented in session 2026-04-13.