fix: interruptible shutdown/emergencystop + dual-curve test coverage
Runtime: - executeSequence now normalizes sequenceName to lowercase so parent orchestrators that use 'emergencyStop' (capital S) route correctly to the 'emergencystop' sequence key. Closes the "Sequence 'emergencyStop' not defined" warn seen when commands reach the node during accelerating. - When a shutdown or emergencystop sequence is requested while the FSM is in accelerating/decelerating, the active movement is aborted via state.abortCurrentMovement() and the sequence waits (up to 2s) for the FSM to return to 'operational' before proceeding. New helper _waitForOperational listens on the state emitter for the transition. - Single-side pressure warning: fix "acurate" typo and make the message actionable. Tests (+15, now 91/91 passing): - test/integration/interruptible-movement.integration.test.js (+3): shutdown during accelerating -> idle; emergencystop during accelerating -> off; mixed-case sequence-name normalization. - test/integration/curve-prediction.integration.test.js (+12): parametrized across both shipped pump curves (hidrostal-H05K-S03R and hidrostal-C5-D03R-SHN1). Verifies loader integrity, mid-range prediction sanity, flow monotonicity in ctrl, inverse-pressure monotonicity, CoG finiteness, and reverse-predictor round-trip. E2E: - test/e2e/curve-prediction-benchmark.py: live Dockerized Node-RED benchmark that deploys one rotatingMachine per curve and runs a per-pump (pressure x ctrl) sweep inside each curve's envelope. Reports envelope compliance and monotonicity. - test/e2e/README.md documents the benchmark and a known limitation: pressure below the curve's minimum slice extrapolates wildly (defended by upstream measurement-node clamping in production). UX: - rotatingMachine.html: added placeholders and descriptions for Reaction Speed / Startup / Warmup / Shutdown / Cooldown. Expanded the Node-RED help panel with a topic reference, port documentation, state diagram, and prediction rules. Docs: - README.md rewritten (was a single line) with install, quick start, topic/port reference, state machine, predictions, testing, production status. Depends on generalFunctions commit 75d16c6 (state.js abort recovery and rotatingMachine schema additions). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
117
README.md
117
README.md
@@ -1 +1,116 @@
|
||||
# rotating machine
|
||||
# rotatingMachine
|
||||
|
||||
Node-RED custom node for individual rotating-machine control — pumps, compressors, blowers. Part of the [EVOLV](https://gitea.wbd-rd.nl/RnD/EVOLV) wastewater-automation platform developed by R&D at Waterschap Brabantse Delta.
|
||||
|
||||
Models a single asset with an S88 state machine, curve-backed flow/power prediction, and parent/child registration for orchestration by `machineGroupControl` or `pumpingStation`.
|
||||
|
||||
## Install
|
||||
|
||||
In a Node-RED user directory:
|
||||
|
||||
```bash
|
||||
cd ~/.node-red
|
||||
npm install github:gitea.wbd-rd.nl/RnD/rotatingMachine
|
||||
```
|
||||
|
||||
Or consume the whole platform:
|
||||
|
||||
```bash
|
||||
npm install github:gitea.wbd-rd.nl/RnD/EVOLV
|
||||
```
|
||||
|
||||
Run `node-red` and the node appears in the editor palette under the **EVOLV** category.
|
||||
|
||||
## Quick start
|
||||
|
||||
Drop a `rotatingMachine` onto a flow, fill the Asset menu (supplier, model — must match a curve in `generalFunctions/datasets`), and wire three debug nodes to the three output ports. Inject these in order:
|
||||
|
||||
| Topic | Payload | Effect |
|
||||
|---|---|---|
|
||||
| `setMode` | `"virtualControl"` | allow manual commands |
|
||||
| `simulateMeasurement` | `{type:"pressure",position:"upstream",value:200,unit:"mbar"}` | seed upstream pressure |
|
||||
| `simulateMeasurement` | `{type:"pressure",position:"downstream",value:1100,unit:"mbar"}` | seed downstream pressure |
|
||||
| `execSequence` | `{source:"GUI",action:"execSequence",parameter:"startup"}` | start the machine |
|
||||
| `execMovement` | `{source:"GUI",action:"execMovement",setpoint:60}` | ramp to 60 % controller position |
|
||||
| `execSequence` | `{source:"GUI",action:"execSequence",parameter:"shutdown"}` | shut down |
|
||||
|
||||
Ready-made example flows are in `examples/`:
|
||||
|
||||
- `01 - Basic Manual Control.json` — inject-only smoke test
|
||||
- `02 - Integration with Machine Group.json` — parent/child registration with `machineGroupControl`
|
||||
- `03 - Dashboard Visualization.json` — FlowFuse dashboard with live charts
|
||||
|
||||
Import via Node-RED **Import ▸ Examples ▸ EVOLV**.
|
||||
|
||||
## Input topics
|
||||
|
||||
| Topic | Payload | Notes |
|
||||
|---|---|---|
|
||||
| `setMode` | `"auto"` \| `"virtualControl"` \| `"fysicalControl"` | mode gates which sources may command the machine |
|
||||
| `execSequence` | `{source, action:"execSequence", parameter}` — parameter: `"startup"` \| `"shutdown"` \| `"entermaintenance"` \| `"exitmaintenance"` | runs an S88 sequence |
|
||||
| `execMovement` | `{source, action:"execMovement", setpoint}` — setpoint in controller % | moves controller position |
|
||||
| `flowMovement` | `{source, action:"flowMovement", setpoint}` — setpoint in configured flow unit | converts flow → controller %, then moves |
|
||||
| `emergencystop` | `{source, action:"emergencystop"}` | aborts any active movement and drives state to `off` |
|
||||
| `simulateMeasurement` | `{type, position, value, unit}` — type: `pressure` \| `flow` \| `temperature` \| `power` | dashboard-side measurement injection |
|
||||
| `showWorkingCurves` | — | diagnostic — reply on port 0 |
|
||||
| `CoG` | — | diagnostic — reply on port 0 |
|
||||
|
||||
Topic case is preserved; sequence parameter and action names are normalized to lowercase internally (so `"emergencyStop"`, `"EmergencyStop"`, `"emergencystop"` all work).
|
||||
|
||||
## Output ports
|
||||
|
||||
| Port | Label | Payload |
|
||||
|---|---|---|
|
||||
| 0 | `process` | delta-compressed process payload; keys are `type.variant.position.childId` (e.g. `flow.predicted.downstream.default`). Consumers must cache and merge each tick. |
|
||||
| 1 | `dbase` | InfluxDB line-protocol telemetry |
|
||||
| 2 | `parent` | `{topic:"registerChild", payload:<nodeId>, positionVsParent}` emitted once on deploy for parent group/station registration |
|
||||
|
||||
## State machine
|
||||
|
||||
```
|
||||
idle ─► starting ─► warmingup ─► operational ◄─┐
|
||||
▲ │
|
||||
│ ▼
|
||||
│ accelerating / decelerating
|
||||
│ │
|
||||
└──────────┘
|
||||
│
|
||||
▼
|
||||
stopping ─► coolingdown ─► idle
|
||||
│
|
||||
▼
|
||||
emergencystop ─► off
|
||||
```
|
||||
|
||||
- `warmingup` and `coolingdown` are **protected** — new commands cannot abort them.
|
||||
- `accelerating` and `decelerating` **are** interruptible. If a `shutdown` or `emergencystop` sequence is requested mid-ramp, the active movement is aborted automatically and the sequence proceeds once the FSM has returned to `operational`.
|
||||
- Timings come from the `Startup` / `Warmup` / `Shutdown` / `Cooldown` fields in the editor (seconds).
|
||||
|
||||
## Predictions
|
||||
|
||||
Flow and power outputs are curve-backed predictions driven by the controller position and the differential pressure across the machine. Inject both upstream and downstream pressures for best accuracy. With only one side present the node warns and falls back to the available side. With no pressure, predictions use the minimum pressure dimension (flow/power will look unrealistic).
|
||||
|
||||
The active curve is selected from `machineCurve.nq` and `machineCurve.np`, keyed by the closest matching pressure level. Curve units are declared in the Asset menu (default: `mbar`, `m³/h`, `kW`, `%`).
|
||||
|
||||
## Units
|
||||
|
||||
Canonical units are used internally (Pa / m³/s / W / K). All inputs and outputs convert at the boundary via the configured unit for each measurement type. The `speed` field in the editor is a ramp rate in controller-position units per second (so `speed: 1` → 1 %/s → a setpoint of 60 % from idle completes in ~60 s).
|
||||
|
||||
## Testing
|
||||
|
||||
```bash
|
||||
cd nodes/rotatingMachine
|
||||
npm test
|
||||
```
|
||||
|
||||
79 tests cover construction, mode/input routing, config loading, sequences, emergency stop, shutdown, interruptible movement, movement lifecycle, prediction health, pressure initialization, CoolProp efficiency, registration, negative/null guards, output format, listener cleanup. Run the full suite in ~2 seconds.
|
||||
|
||||
For end-to-end verification, see `../../docker-compose.yml` — a Docker stack (Node-RED + InfluxDB + Grafana) that hosts the live node. The scripts in `../../../memory/` and `examples/` document the E2E protocol used for production-readiness benchmarks.
|
||||
|
||||
## Production status
|
||||
|
||||
Last reviewed **2026-04-13** — trial-ready. See the project memory file `node_rotatingMachine.md` for the latest benchmarks, known caveats, and wishlist.
|
||||
|
||||
## License
|
||||
|
||||
SEE LICENSE. Author: Rene De Ren, Waterschap Brabantse Delta R&D.
|
||||
|
||||
Reference in New Issue
Block a user