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>
158 lines
9.7 KiB
Markdown
158 lines
9.7 KiB
Markdown
# Reference — Examples
|
|
|
|

|
|
|
|
> [!NOTE]
|
|
> Pending full node review (2026-05). Content reflects `CONTRACT.md` and current source only.
|
|
>
|
|
> Every example flow shipped under `nodes/valveGroupControl/examples/`, plus how to load them, what they show, and the debug recipes that go with them. Live source: `nodes/valveGroupControl/examples/`.
|
|
|
|
---
|
|
|
|
## Shipped examples
|
|
|
|
| File | Tier | Dependencies | What it shows |
|
|
|:---|:---:|:---|:---|
|
|
| `basic.flow.json` | 1 | EVOLV only | Inject `data.totalFlow` + 2 `valve` children registered as the group. No parent. Verifies Kv-share split and residual reconciliation. |
|
|
| `integration.flow.json` | 2 | EVOLV only | VGC + 2 valves + one upstream `rotatingMachine` source. Source's `flow.predicted.*` events drive the group; fluid-contract aggregation resolves to `liquid`. |
|
|
| `edge.flow.json` | 3 | EVOLV only | Edge cases: no valves available, residual non-convergence, cascaded VGC as upstream source. Currently a structural placeholder — TODO consult `examples/edge.flow.json` for actual scenarios. |
|
|
|
|
> [!IMPORTANT]
|
|
> The Tier-1 / Tier-2 / Tier-3 naming convention (`01 - <name>.json`) used by `rotatingMachine` has **not yet been applied** to VGC. Filenames are still the legacy `basic`/`integration`/`edge` triad. Migration tracked in `.agents/improvements/IMPROVEMENTS_BACKLOG.md`. When renaming, update `examples/README.md` in the same commit.
|
|
|
|
---
|
|
|
|
## 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/valveGroupControl/examples/basic.flow.json" \
|
|
http://localhost:1880/flows
|
|
```
|
|
|
|
---
|
|
|
|
## Example: basic.flow.json
|
|
|
|
> [!IMPORTANT]
|
|
> **TODO: not yet validated against live Node-RED.** Steps below are inferred from `CONTRACT.md` + the topic registry. Consult `examples/basic.flow.json` directly for exact node ids and inject payloads. Screenshot needed once validated — save under `wiki/_partial-screenshots/valveGroupControl/01-basic-editor.png`.
|
|
|
|
Single VGC with 2 `valve` children. Demonstrates:
|
|
|
|
1. Wire each valve's Port 2 to VGC's input so `child.register` arrives automatically on deploy.
|
|
2. Inject `data.totalFlow` with payload `{ value: 80, unit: "m3/h", position: "atEquipment", variant: "measured" }`. VGC runs `calcValveFlows`:
|
|
- Splits 80 m³/h by Kv share across the 2 valves.
|
|
- Each valve's `updateFlow('predicted', share, 'downstream')` push.
|
|
- Re-reads each valve's accepted `flow.predicted.downstream`.
|
|
- Residual loop runs up to `maxPasses: 2` until `|residual| < 0.001`.
|
|
3. Watch Port 0 debug: `atEquipment_predicted_flow` settles at the assigned total; `deltaMax_predicted_pressure` updates as each valve emits `deltaPChange`.
|
|
4. Toggle one valve to `off` — the next `data.totalFlow` (or tick) routes 100% to the remaining valve.
|
|
5. Set the offlined valve back to operational — the next tick re-includes it in the split.
|
|
|
|
### Try the residual pass
|
|
|
|
After the group settles at `80 m³/h`:
|
|
|
|
1. Inject `data.totalFlow = 200` (a value beyond aggregate Kv capacity). Watch `lastFlowSolve.residual` — it stays large because the valves cap at their accept limits. `flow.predicted.atEquipment` will read the sum of caps, not 200.
|
|
2. Inject `data.totalFlow = 0`. Every valve receives `updateFlow('predicted', 0, 'downstream')`; `maxDeltaP` collapses to 0.
|
|
|
|
---
|
|
|
|
## Example: integration.flow.json
|
|
|
|
> [!IMPORTANT]
|
|
> **TODO: not yet validated.** Screenshot needed once validated — save under `wiki/_partial-screenshots/valveGroupControl/02-integration-editor.png`.
|
|
|
|
VGC + 2 valves + 1 upstream `rotatingMachine`. Demonstrates:
|
|
|
|
- Source registration: the rotatingMachine's `child.register` (softwareType `rotatingmachine` → canonical `machine`) lands on VGC; `_registerSource` subscribes to `flow.predicted.downstream`, `flow.measured.downstream`, etc.
|
|
- Source-driven flow: a pump-state change emits `flow.predicted.downstream`; VGC's handler converts to `updateFlow('predicted', value, 'atEquipment', unit)` and re-runs `calcValveFlows`.
|
|
- Fluid-contract resolution: the pump's `getFluidContract()` (or fallback `DEFAULT_SOURCE_SERVICE_TYPE.rotatingmachine = 'liquid'`) produces `serviceType: 'liquid'`; `fluidContract` resolves to `{status: 'resolved', serviceType: 'liquid'}`.
|
|
|
|
---
|
|
|
|
## Example: edge.flow.json
|
|
|
|
> [!IMPORTANT]
|
|
> **TODO: structural placeholder.** Consult the JSON directly for the scenarios it currently exercises. The edge scenarios this node ought to test (per `CONTRACT.md` + source review):
|
|
>
|
|
> - **No available valves** — all in `off` / `maintenance`. Expected: status badge `'No valves'` red, every valve pushed `0`, `lastFlowSolve.assignedTotal = 0`.
|
|
> - **Residual non-convergence** — valve curve where Kv-share is a bad first estimate. Expected: loop exits after `maxPasses: 2` with non-zero residual; assignedTotal < target; behaviour graceful.
|
|
> - **Cascaded VGC** — upstream source is another VGC. Expected: source registered, `flow.*` events bound, `getFluidContract()` propagated up. **Not exercised in production.**
|
|
> - **Conflicting fluid contracts** — two sources advertise `liquid` vs `gas`. Expected: `fluidContract.status = 'conflict'`; `fluidContractChange` event emitted.
|
|
|
|
---
|
|
|
|
## 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 |
|
|
|:---|:---|:---|
|
|
| All valves receive `assigned flow = 0` | `getAvailableValves()` returns empty list: check every valve's state (`off` / `maintenance` excludes), `currentMode !== 'maintenance'`, and `kv > 0`. | `src/groupOps/flowDistribution.isValveAvailable` |
|
|
| Residual never converges | Pathological valve curve: Kv-share is a bad first estimate. Check `vgc.lastFlowSolve.residual` and `.passes` — if `passes === maxPasses` and residual is large, the loop ran out. Raise `flowReconciliation.maxPasses` (no editor field yet — TODO Phase 7). | `src/groupOps/flowDistribution.solveFlowDistribution` |
|
|
| Group `maxDeltaP` stale | Child `deltaPChange` not subscribed: valve emitter not exposed via `child.emitter`, or valve never registered. | `src/specificClass._bindValveEvents` |
|
|
| Service-type stays `unknown` | No upstream source registered, or all advertise unknown type. Check `vgc.sources` — if empty, no Port-2 child.register reached VGC. | `src/sources/fluidContract.refreshFluidContract` |
|
|
| `data.totalFlow` silently ignored | Mode rejects the source id: check `mode.allowedSources` for the current mode. `maintenance` rejects every source. Warn in log: `Source '<src>' is not valid for mode '<mode>'.` | `src/specificClass.isValidSourceForMode` |
|
|
| `set.position` has no effect | Known: handler is a debug-logged no-op pending Phase 7. See [Limitations](Reference-Limitations#set-position-is-a-no-op). | `src/commands/handlers.setPosition` |
|
|
| `set.reconcileInterval = 0` (or negative) silently does nothing | The handler validates `Number.isFinite(nextSec) && nextSec > 0`. Otherwise warns and returns. | `src/commands/handlers.setReconcileInterval` |
|
|
| Two emitters fight on the same flow channel (one source publishes `atEquipment`, another publishes `atequipment`) | Both event names are wired; the **last write wins** on each `updateFlow`. There is no source-priority logic. | `src/sources/fluidContract.SOURCE_FLOW_EVENTS` |
|
|
| Cascaded VGC not propagating flow | Accepted by router, but no tests — treat as experimental. | `src/sources/fluidContract.SOURCE_SOFTWARE_TYPES` |
|
|
| Output port-0 key shape differs from rotatingMachine's | VGC uses `<position>_<variant>_<type>` (same as MGC) — the inverse of rotatingMachine's `<type>.<variant>.<position>.<childId>`. Don't mix. | `src/io/output.getOutput` |
|
|
|
|
> Never ship `enableLog: 'debug'` in a demo — fills the container log within seconds and obscures real errors.
|
|
|
|
---
|
|
|
|
## Output coverage (TODO)
|
|
|
|
> [!IMPORTANT]
|
|
> Per `.claude/rules/output-coverage.md`: every output on every layer needs a manifest entry + populated + degraded test. **`test/_output-manifest.md` does not yet exist** for VGC. Backfill tracked in `.agents/improvements/IMPROVEMENTS_BACKLOG.md`.
|
|
>
|
|
> Minimum coverage to land before declaring trial-ready:
|
|
>
|
|
> - Port 0 / 1 keys: `mode`, `maxDeltaP`, `atEquipment_predicted_flow`, `atEquipment_measured_flow`, `deltaMax_predicted_pressure`. Each tested in populated AND degraded states.
|
|
> - Port 2: `child.register` payload shape.
|
|
> - Example flow function-node outputs: each `outputs > 1` fan-out enumerated; verify no `payload: null` literals (lint via `npm run lint:flow-outputs`).
|
|
|
|
---
|
|
|
|
## Related pages
|
|
|
|
| Page | Why |
|
|
|:---|:---|
|
|
| [Home](Home) | Intuitive overview |
|
|
| [Reference — Contracts](Reference-Contracts) | Topic + config + child filters |
|
|
| [Reference — Architecture](Reference-Architecture) | Code map, flow-distribution loop, source aggregation |
|
|
| [Reference — Limitations](Reference-Limitations) | Known issues and open questions |
|
|
| [machineGroupControl — Examples](https://gitea.wbd-rd.nl/RnD/machineGroupControl/wiki/Reference-Examples) | Sibling group-control demo flows |
|
|
| [valve wiki](https://gitea.wbd-rd.nl/RnD/valve/wiki/Home) | The child node VGC coordinates |
|
|
| [EVOLV — Topology Patterns](https://gitea.wbd-rd.nl/RnD/EVOLV/wiki/Topology-Patterns) | Where valveGroupControl fits in a larger plant |
|