feat(pumpingStation): realistic defaults, ramp-foot visual fix, manual-mode visibility, dashboard example

Editor + schema defaults
- pumpingStation.html: drag-in defaults now reflect a realistic basin
  (volume=50 m³, height=4 m, inflowLevel=1.5, outflowLevel=0.2,
  overflowLevel=3.8, startLevel=1, stopLevel=0.5, minLevel=0.3,
  maxLevel=3.8). Old defaults left every level field null.

Visual bug fix
- src/editor/mode-preview.js: the level-based ramp curve in the editor
  was being drawn with foot=startLevel via buildPath(start, start, max).
  The runtime in control/levelBased.js has always used inflowLevel as
  the ramp foot. Pass buildPath(start, upFoot, max) where upFoot falls
  back to start when inflowLevel is missing, matching the runtime.

Manual mode observability
- src/specificClass.js: store last forwarded demand on this._manualDemand;
  surface as `mode` and `manualDemand` in getOutput(); call
  notifyOutputChanged() on forwardDemandToChildren and on changeMode so
  Port 0/1 emit even with no children registered. Status badge compacted
  to `mode | dir% | net m³/h` + `Qd=X m³/h` in manual mode.

Examples cleanup
- Drop stale 02-Integration.json, 03-Dashboard.json, basic-dashboard.flow.json,
  standalone-demo.js.
- 01-Basic.json: numbered driver groups (1. Control mode … 4. Calibration),
  Debug-outputs group, fixed typos and HOW-TO-USE; Port 1 debug now active.
- New 02-Dashboard.json: FlowFuse Dashboard 2.0 with Controls (7 buttons),
  Status (7 ui-text rows), Trends (4 ui-charts: level / volume / volume% /
  flow in-out-net), Raw output (ui-template dumping every Port 0 field).
  Fan-out function pattern-matches the 4-segment measurement keys by
  prefix instead of hardcoding childId, converts flow m³/s → m³/h, and
  caches last-known values so deltas never blank a row.
- examples/README.md realigned to the two-file set.

Wiki
- Home.md: 5 image placeholders replaced with the provided screenshots
  (01-node-and-editor, 02-basic-flow, 03-wiring-standalone,
  04-wiring-integrated) and the demo GIF (01-basic-demo).
- Reference-Examples.md: shipped-files table reduced to 01-Basic +
  02-Dashboard, Example-01 section uses the screenshot + GIF, Example-02
  rewritten as Dashboard (kept screenshot/GIF callouts open for those
  captures), Example-03/Integration sections + their debug-recipes row
  removed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
znetsixe
2026-05-12 14:52:00 +02:00
parent 8507ee4e02
commit fe5fa3577b
17 changed files with 1649 additions and 3168 deletions

View File

@@ -9,12 +9,10 @@
## Shipped examples
| File | Tier | Tabs | What it shows |
|:---|:---:|:---|:---|
| `examples/01-Basic.json` | 1 | Process Plant | Single pumpingStation driven by inject nodes &mdash; no parent, no dashboard. |
| `examples/02-Integration.json` | 2 | Process Plant + Setup | Adds a `measurement` level child and a `machineGroupControl` parent with two `rotatingMachine` pumps. Demonstrates the Phase-2 parent / child handshake. |
| `examples/03-Dashboard.json` | 3 | Process Plant + Dashboard + Setup | Tier-2 plumbing plus a FlowFuse Dashboard 2.0 page with 3 charts (flow / level / volume %), text widgets, and 2 controls (mode dropdown + demand slider). |
| `examples/basic-dashboard.flow.json` | legacy | mixed | Pre-refactor flow kept for reference. Use `03-Dashboard.json` instead. |
| File | Tier | What it shows |
|:---|:---:|:---|
| `examples/01-Basic.json` | 1 | Single pumpingStation driven by inject nodes &mdash; no parent, no dashboard. Numbered driver groups for Mode / Flow signals / Operator demand / Calibration. |
| `examples/02-Dashboard.json` | 2 | Same command surface as Basic, driven by a FlowFuse Dashboard 2.0 page (Controls + live Status rows + 4 trend charts + raw-output table). |
---
@@ -39,93 +37,67 @@ curl -X POST -H 'Content-Type: application/json' \
## Example 01 &mdash; Basic standalone
> [!IMPORTANT]
> **Screenshot needed.** After importing `01-Basic.json`, capture the full Process Plant tab.
>
> Save as `wiki/_partial-screenshots/pumpingStation/05-ex01-basic.png`.
> Replace this callout with the image link.
![Basic example flow in Node-RED editor](_partial-screenshots/pumpingStation/02-basic-flow.png)
### Nodes on the tab
| Type | Purpose |
|:---|:---|
| `comment` | Tab header / instructions |
| `inject` &times; 6 | Buttons to send `set.mode`, `set.inflow`, `set.demand`, `cmd.calibrate.volume`, `cmd.calibrate.level` |
| `inject` &times; 7 | Buttons to send `set.mode` (manual / levelbased), `set.inflow`, `set.outflow`, `set.demand`, `cmd.calibrate.volume`, `cmd.calibrate.level` |
| `pumpingStation` | The unit under test |
| `function` | Merge Port-0 deltas into a single rolling snapshot |
| `debug` &times; 3 | Port 0 (process), Port 1 (InfluxDB), Port 2 (parent reg) |
Driver injects are wrapped in four numbered groups: **1. Control mode**, **2. Flow signals (inflow / outflow)**, **3. Operator demand (manual mode only)**, **4. Calibration**. Debug nodes sit in a separate **Debug outputs (sidebar)** group on the right.
### What to do after deploy
1. Click `set.mode = levelbased`.
2. Click `cmd.calibrate.level = 1.5 m` to anchor the volume integrator.
3. Click `set.inflow = 60 m³/h`.
4. Watch the Port-0 debug pane: `direction` flips to `filling`, `level` rises, `demand` follows the level curve, `etaSeconds` decreases.
5. Click `set.demand = 40 %` (only honoured in manual mode &mdash; for level-based, the controller decides demand from level).
1. (optional) Click `set.mode = manual` if you want `set.demand` to forward; otherwise leave it on the default `levelbased` and the ramp drives demand from level.
2. Click `set.inflow = 60 m³/h` &mdash; the basin starts filling. Watch Port 0 in the debug pane: `direction` flips to `filling`, `level` rises, predicted volume integrates.
3. In manual mode: click `set.demand = 40` &mdash; the value surfaces as `manualDemand` on Port 0/1 and in the node status badge.
4. Click `cmd.calibrate.volume = 25 m³` (or `cmd.calibrate.level = 1.5 m`) to snap the predicted-volume integrator.
> [!IMPORTANT]
> **GIF needed.** Record steps 1&ndash;4. Target 15&ndash;25 s, ≤ 1 MB after `gifsicle -O3 --lossy=80`.
>
> Save as `wiki/_partial-gifs/pumpingStation/02-ex01-demo.gif`.
> Replace this callout with the image link.
![Basic demo — level rises, demand follows](_partial-gifs/pumpingStation/01-basic-demo.gif)
---
## Example 02 &mdash; Integration with parent + children
## Example 02 &mdash; Dashboard
> [!IMPORTANT]
> **Screenshot needed.** After importing `02-Integration.json`, capture the full Process Plant tab.
> **Screenshot needed.** Two captures from `02-Dashboard.json`:
> 1. The editor tab (left controls column + pumpingStation + Live-status group on the right).
> 2. The rendered dashboard at `http://localhost:1880/dashboard/pumpingstation-basic`.
>
> Save as `wiki/_partial-screenshots/pumpingStation/06-ex02-integration.png`.
> Replace this callout with the image link.
> Save as `wiki/_partial-screenshots/pumpingStation/05-ex02-editor.png` and `06-ex02-dashboard.png`.
> Replace this callout with both image links.
### What it adds vs Example 01
| Addition | Why |
|:---|:---|
| `measurement` node feeding `level` | Replaces the inject-driven level path with a real measurement child |
| `machineGroupControl` (MGC) parent | Demand goes upward to the MGC instead of being applied directly |
| Two `rotatingMachine` pumps under the MGC | The MGC load-shares demand across them |
| `Setup` tab | Initial calibration injects fire once via `once: true` |
| FlowFuse `ui-base` + `ui-theme` + `ui-page` setup | One dashboard page hosting four widget groups |
| `ui-button` &times; 7 (Controls group) | Replace the inject buttons one-for-one &mdash; each carries the canonical `msg.topic` directly |
| `ui-text` &times; 7 (Status group) | Live readouts: Mode, Direction, Level, Volume, Volume %, percControl, Manual demand |
| `ui-chart` &times; 4 (Trends group) | Level (m), Volume (m³), Volume % (0&ndash;100), Flow (m³/h, multi-series Inflow / Outflow / Net) |
| `ui-template` (Raw output group) | Full key/value table of the latest Port 0 cache &mdash; every field the node emits, sorted |
| Fan-out function | Caches last-known values so delta-only Port 0 updates never blank a status row, and forwards numeric values to the charts |
This exercises the Phase-2 parent / child handshake: `child.register` is sent on Port 2 of each child to its parent, and the parent's `commandRegistry` dispatches into `ChildRouter.onRegister(...)`.
### What to do after deploy
1. Setup tab fires once, calibrating volume and setting mode.
2. The MGC reports its predicted flow back to the pumpingStation.
3. Click any inject in the Process Plant tab to perturb the basin.
4. Watch all three Port-0 debug taps: PS, MGC, both pumps.
---
## Example 03 &mdash; Dashboard
> [!IMPORTANT]
> **Screenshot needed.** Two captures from `03-Dashboard.json`:
> 1. The editor tab (Dashboard UI) showing the dashboard widgets and trend-feeder functions.
> 2. The rendered dashboard at `http://localhost:1880/dashboard`.
>
> Save as `wiki/_partial-screenshots/pumpingStation/07-ex03-editor.png` and `08-ex03-dashboard.png`.
> Replace this callout with both image links.
### What it adds vs Example 02
| Addition | Why |
|:---|:---|
| FlowFuse ui-base + ui-page + ui-group setup | One page, multiple grouped widgets |
| 3 ui-chart widgets | flow / level / volume % trends |
| ui-text widgets | live mode, demand, direction display |
| ui-dropdown for mode | operator-facing mode switch |
| ui-slider for demand | manual setpoint |
| Trend-feeder function | splits Port-0 deltas into one msg per chart with `msg.topic` set as series label |
The buttons fire the **same canonical `msg.topic`** as the inject nodes in Example 01 &mdash; there is no separate dashboard command surface to learn.
Required: `@flowfuse/node-red-dashboard` (Dashboard 2.0) installed in the Node-RED instance.
### What to do after deploy
1. Open `http://localhost:1880/dashboard/pumpingstation-basic`.
2. Click `Mode: Manual` or `Mode: Levelbased`.
3. Click `Inflow 60 m³/h` &mdash; Status panel level / volume / vol% rise; the Level / Volume / Flow charts plot the trends.
4. In manual mode click `Demand 40 m³/h` &mdash; `Manual demand` row updates, node badge appends `Qd=40 m³/h`.
5. Inspect the **Raw output** table at the bottom of the page for the full Port 0 surface (basin geometry, dryRunLevel, highVolumeSafetyLevel, predictedOverflowVolume, &hellip;).
> [!IMPORTANT]
> **GIF needed.** Slide the demand control and watch the trend charts react. 20&ndash;30 s is enough.
> **GIF needed.** Capture clicking through Mode &rarr; Inflow &rarr; Demand and the charts reacting. 20&ndash;30 s is enough.
>
> Save as `wiki/_partial-gifs/pumpingStation/03-ex03-dashboard.gif`.
> Save as `wiki/_partial-gifs/pumpingStation/02-ex02-dashboard.gif`.
> Replace this callout with the image link.
---
@@ -159,7 +131,6 @@ Full file: [EVOLV/docker-compose.yml](https://gitea.wbd-rd.nl/RnD/EVOLV/src/bran
| Level rises but `volume` stays at `minVol` | Volume integrator hasn't been calibrated. Send `cmd.calibrate.level = <real level>` once. |
| Demand stays at 0 % even though level is high | Mode might be `manual` &mdash; check `set.mode`. Or the safety layer is blocking (look at `safety.blocked` on Port 0). |
| Predicted volume drifts | Net-flow source is wrong. Look at `flowSource` on Port 0; it should match the highest-level aggregator you have wired in. |
| MGC and pumps don't see demand | `02-Integration.json` requires the MGC to register **before** the pumps. The Setup tab handles ordering. |
| `enableLog: 'debug'` floods the container log | Toggle it off in the node's config. Never ship a demo with debug logging enabled. |
---