Replaces the prior stub/partial wiki with a Home + Reference-{Architecture,
Contracts,Examples,Limitations} + _Sidebar structure. Topic-contract and
data-model sections wrapped in AUTOGEN markers for the future wiki-gen tool.
Source-vs-spec contradictions surfaced and flagged inline (not silently
fixed). Pending-review notes mark sections that need a full node review.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
144 lines
8.9 KiB
Markdown
144 lines
8.9 KiB
Markdown
# Reference — Examples
|
|
|
|

|
|
|
|
> [!NOTE]
|
|
> Every example flow shipped under `nodes/valve/examples/`, plus how to load them, what they show, and the debug recipes that go with them. Live source: `nodes/valve/examples/`.
|
|
>
|
|
> Pending full node review (2026-05). The shipped flows are currently minimal stubs; tiered demos (matching the `rotatingMachine` template) are on the backlog.
|
|
|
|
---
|
|
|
|
## Shipped examples
|
|
|
|
| File | Tier | Dependencies | What it shows |
|
|
|:---|:---:|:---|:---|
|
|
| `basic.flow.json` | 1 (stub) | EVOLV only | Minimal: one `inject` → one `valve` → one `debug`. Sanity check that the node loads. |
|
|
| `integration.flow.json` | 2 (stub) | EVOLV only | Same shape as basic; placeholder for VGC + measurement integration. |
|
|
| `edge.flow.json` | 3 (stub) | EVOLV only | Placeholder for edge cases (gas-choke, e-stop, invalid setpoints). |
|
|
|
|
> [!IMPORTANT]
|
|
> **Tiered example flows TODO.** Replace the three stubs with `01 - Basic Manual Control.json` / `02 - Integration with Valve Group.json` / `03 - Dashboard Visualization.json` following the `rotatingMachine` template. Track in `.agents/improvements/IMPROVEMENTS_BACKLOG.md` and validate live against Docker-stack Node-RED. Screenshots / GIFs land under `wiki/_partial-screenshots/valve/` and `wiki/_partial-gifs/valve/`.
|
|
|
|
---
|
|
|
|
## 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.
|
|
|
|
### Via the Admin API
|
|
|
|
```bash
|
|
curl -X POST -H 'Content-Type: application/json' \
|
|
--data @"nodes/valve/examples/basic.flow.json" \
|
|
http://localhost:1880/flow
|
|
```
|
|
|
|
Use `POST /flow` (single tab, full replace) or `POST /flows` (full deploy) depending on whether other tabs are already loaded.
|
|
|
|
---
|
|
|
|
## Driving the basic flow manually
|
|
|
|
The shipped `basic.flow.json` has a single `inject` wired to the valve. To exercise the FSM + hydraulic model, send the following sequence by hand (e.g. via additional inject nodes you wire in, or the Admin API):
|
|
|
|
1. `set.mode` — payload `"virtualControl"` — lets the GUI source drive the valve.
|
|
2. `cmd.startup` — payload `{}`. FSM walks `idle → starting → warmingup → operational`. Watch `state` on Port 0.
|
|
3. `set.position` — payload `{"setpoint": 60}`. FSM goes `operational → accelerating → operational`; `percentageOpen` ramps 0 → 60 at `movement.speed` %/s.
|
|
4. `data.flow` — payload `{"variant": "measured", "value": 25, "position": "downstream", "unit": "m3/h"}`. Flow lands in MeasurementContainer; `MeasurementRouter.updateFlow` recomputes deltaP. `delta_predicted_pressure` appears on Port 0; `evt.deltaPChange` fires upward.
|
|
5. `data.flow` — payload `{"variant": "measured", "value": 0, "position": "downstream", "unit": "mbar"}` to push downstream pressure as well (needed for the gas-flow path).
|
|
6. `cmd.shutdown` — payload `{}`. Because the valve is `operational`, the controller first ramps `percentageOpen` to 0, then `state` transitions `stopping → coolingdown → idle`.
|
|
|
|
> [!IMPORTANT]
|
|
> **GIF needed.** Demo recording of steps 1–6 + status badge progression. Save as `wiki/_partial-gifs/valve/01-basic-demo.gif`, target ≤ 1 MB after `gifsicle -O3 --lossy=80`.
|
|
|
|
### Try the position-residue handler
|
|
|
|
After the valve reaches `operational` at 60 %:
|
|
|
|
1. Send `set.position = {setpoint: 20}`. State goes `operational → decelerating → ...`.
|
|
2. While `decelerating`, send `set.position = {setpoint: 80}`.
|
|
3. `state.moveTo` recognises the residue state, transitions back to `operational` synchronously, then ramps up to 80. No setpoint is lost.
|
|
|
|
This is the same residue mechanism `rotatingMachine` uses for fast retargets.
|
|
|
|
### Try the e-stop sequence
|
|
|
|
From `operational`, send `cmd.estop`. The valve runs the `emergencystop` sequence (`[emergencystop, off]`). Allowed transitions out of `emergencystop` are `idle` / `off` / `maintenance`. To restart, drop to `idle` first (`cmd.shutdown` from `off` may not work depending on the state graph — TODO: confirm).
|
|
|
|
---
|
|
|
|
## Integration with `valveGroupControl`
|
|
|
|
> [!IMPORTANT]
|
|
> **TODO: Tier-2 example.** A proper integration flow with `valveGroupControl` + 2×valve children + an upstream `rotatingMachine` / `pumpingStation` for fluid-contract tracking is on the backlog. Screenshot under `wiki/_partial-screenshots/valve/02-integration.png`.
|
|
|
|
When built, the integration flow will demonstrate:
|
|
|
|
- Auto-registration via Port 2 at deploy — each valve's `child.register` reaches the VGC; no manual wiring needed.
|
|
- Upstream-source registration — a `rotatingMachine` registered as a child of the valve feeds `getFluidContract()` into `FluidCompatibility`. Status flips from `pending` / `compatible` / `mismatch` based on `serviceType` agreement.
|
|
- `evt.deltaPChange` propagation from each valve to the VGC for group-level deltaP aggregation.
|
|
|
|
---
|
|
|
|
## Dashboard visualization
|
|
|
|
> [!IMPORTANT]
|
|
> **TODO: Tier-3 example.** A FlowFuse Dashboard 2.0 page (`@flowfuse/node-red-dashboard`) with control buttons (mode, startup, shutdown, e-stop, position slider), live status (state badge, position %, deltaP, flow), and trend charts (deltaP, position) is on the backlog. Save as `03 - Dashboard Visualization.json`.
|
|
|
|
---
|
|
|
|
## Docker compose snippet
|
|
|
|
To bring up Node-RED + InfluxDB with EVOLV nodes pre-loaded:
|
|
|
|
```yaml
|
|
# 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](https://gitea.wbd-rd.nl/RnD/EVOLV/src/branch/development/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`. |
|
|
| Status badge shows `⚠ <message>` (yellow ring) | `getFluidCompatibility().status` is `mismatch` or `conflict`. An upstream source advertised a service type that doesn't match this valve's expected type. | `src/fluid/fluidCompatibility.js`, `getFluidCompatibility()`. |
|
|
| `delta_predicted_pressure` stuck at `0` or missing | `kv` is 0 (valve closed), the FSM isn't in `operational` / `accelerating` / `decelerating`, or no flow has landed. For gas flow, also needs a finite `downstream_measured_pressure`. | `state.getCurrentState()`, `percentageOpen`, `MeasurementRouter.updateDeltaP`. |
|
|
| `set.position` has no effect | Source not in `mode.allowedSources[currentMode]`. Watch for `Source '...' is not valid for mode '...'` in the warn log. | `src/flow/flowController.js` `isValidSourceForMode`. |
|
|
| `data.flow` payloads aren't reflected on Port 0 | Payload shape: `{variant: 'measured'\|'predicted', value: <number>, position: <string>, unit?: 'm3/h'}`. Missing `variant` warns `Unrecognized variant '...' for flow update`. Missing `value` warns `Received null or undefined value for flow update`. | `src/measurement/measurementRouter.js` `updateFlow`. |
|
|
| Gas-flow deltaP saturates at a ceiling | The choked-flow cap fired (`isChoked: true` in `hydraulicDiagnostics`). Increase `gasChokedRatioLimit` or revise downstream pressure. | `src/hydraulicModel.js` `_calculateGasDeltaP`. |
|
|
| `query.curve` returns empty `valveCurve` | `asset.model` not found by `assetResolver`; the predictor falls back to inline `asset.valveCurve` — check that exists. | `src/curve/supplierCurve.js` `SupplierCurvePredictor.snapshot()`. |
|
|
| FSM stuck in `accelerating` / `decelerating` | A move was aborted with `returnToOperationalOnAbort = false`. Send a new `set.position` — the residue handler in `state.moveTo` transitions back to `operational` first. | `generalFunctions/src/state/state.js` `moveTo` residue branch. |
|
|
| Per-valve Port 0 key names differ from what your dashboard expects | valve uses `<position>_<variant>_<type>` (e.g. `delta_predicted_pressure`, `downstream_measured_flow`). `rotatingMachine` uses `<type>.<variant>.<position>.<childId>`. Don't mix them. | `src/io/output.js` `buildOutput`. |
|
|
|
|
> Never ship `enableLog: 'debug'` in a demo — fills the container log within seconds and obscures real errors.
|
|
|
|
---
|
|
|
|
## Related pages
|
|
|
|
| Page | Why |
|
|
|:---|:---|
|
|
| [Home](Home) | Intuitive overview |
|
|
| [Reference — Contracts](Reference-Contracts) | Topic + config + child filters |
|
|
| [Reference — Architecture](Reference-Architecture) | Code map, FSM, hydraulic-model pipeline |
|
|
| [Reference — Limitations](Reference-Limitations) | Known issues and open questions |
|
|
| [valveGroupControl — Examples](https://gitea.wbd-rd.nl/RnD/valveGroupControl/wiki/Reference-Examples) | Group-control demo flows |
|
|
| [EVOLV — Topology Patterns](https://gitea.wbd-rd.nl/RnD/EVOLV/wiki/Topology-Patterns) | Where valve fits in a larger plant |
|