Files
settler/wiki/Reference-Examples.md

140 lines
8.1 KiB
Markdown
Raw Permalink Normal View History

# Reference — Examples
![code-ref](https://img.shields.io/badge/code--ref-a3583a3-blue)
> [!NOTE]
> Pending full node review (2026-05). The shipped example flows are **stub level**: each is a 4-node skeleton (tab + node + inject + debug) that proves the node loads in Node-RED but does **not** exercise the reactor → settler → return-pump chain or the TSS mass-balance math. Production-grade examples are TODO. See [Reference — Limitations — Example flows](Reference-Limitations#example-flows-are-stub-level).
---
## Shipped examples
| File | Tier | Dependencies | What it shows |
|:---|:---:|:---|:---|
| `basic.flow.json` | 1 (stub) | EVOLV only | Loads a single settler node, wires a `ping` inject to its input, taps Port 0 (only) to a debug node. Inject payload is the string `"1"` — will be rejected by `data.influent`'s payload validator (warn logged). Useful only to verify the node type registers. |
| `integration.flow.json` | 2 (stub) | EVOLV only | Same shape; inject sends `topic = registerChild`, payload `example-child-id`. The lookup will fail (no such node) and log a warn. Does **not** exercise reactor or pump wiring. |
| `edge.flow.json` | 3 (stub) | EVOLV only | Same shape; inject sends `topic = doesNotExist`. Verifies the registry rejects unknown topics. |
### Status
| Aspect | State |
|:---|:---|
| Loads in Node-RED on deploy | yes |
| Drives `data.influent` with a valid payload | no |
| Drives the reactor → settler → return-pump chain | no |
| Shows the 3-stream Fluent split on Port 0 | no |
| Has a dashboard tier | no |
| Validated against a live Node-RED instance | no |
---
## 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/settler/examples/basic.flow.json \
http://localhost:1880/flows
```
---
## TODO — production-grade example set
The matching `rotatingMachine` repo ships three tiers; settler needs the same. Tracking placeholder:
| Tier | Proposed filename | What it should show |
|:---|:---|:---|
| 1 | `01 - Basic Manual Influent.json` | Single settler + inject sending `data.influent` with a realistic `{F, C}` payload. Three debug taps (one per Port 0 stream by `payload.inlet`). Operator can vary F and watch the split rebalance. |
| 2 | `02 - Reactor and Return Pump.json` | One `reactor` (upstream) + one `settler` + one `rotatingMachine` (return pump, downstream). Auto-registration via Port 2. Drive the reactor with an inject; settler should re-split on every reactor `stateChange`. |
| 3 | `03 - Dashboard Visualization.json` | FlowFuse Dashboard 2.0 page: F_in / F_eff / F_surplus / F_return trend chart, C_TS gauge, status badges per stream. Required: `@flowfuse/node-red-dashboard` installed. |
> [!IMPORTANT]
> **Screenshots needed** once the production-grade examples land. Save as `wiki/_partial-screenshots/settler/01-basic-editor.png`, `02-reactor-pump-editor.png`, `03-dashboard-rendered.png`, ≤ 200 KB each.
---
## What the basic example would do (sketch — not yet shipped)
Operator workflow once a real Tier-1 ships:
1. Deploy the flow.
2. Send `data.influent` with payload:
```json
{ "F": 1000, "C": [0,0,0,0,0,0,0,30,80,400,200,80,3000] }
```
Twelve soluble species at low concentrations + index 12 `X_TS = 3000` mg/L.
3. Observe three Port-0 messages arrive simultaneously, each with `topic = "Fluent"` and `payload.inlet` ∈ {0, 1, 2}.
4. With default `C_TS = 2500` mg/L:
- `F_s = 1000 * 3000 / 2500 = 1200` — but clamped to `F_in = 1000`. **The clamp fires** — this is the input edge case [Reference — Limitations — no-flow-balance warning](Reference-Limitations#no-flow-balance-warning) refers to.
- `F_eff = 0`. The clarified-effluent envelope carries zero flow but still has its `C` vector (with species 7–12 zeroed).
- `F_sr` is 0 (no pump wired) → `F_surplus = 1000`, `F_return = 0`.
5. Send `data.influent` with `X_TS = 1500` mg/L instead. Now `F_s = 600`, `F_eff = 400`, `F_surplus = 600`, `F_return = 0`. Realistic split.
> [!NOTE]
> Pending full node review (2026-05). The clamp-fires behaviour above is intended (per code comment in `getEffluent`) but produces no operator-visible warning. Tracked.
---
## 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
> [!NOTE]
> Pending full node review (2026-05). Recipes below are grounded in the source; the symptom-side has not been confirmed against a live deployment.
| Symptom | First thing to check | Where to look |
|:---|:---|:---|
| `F_eff` negative or `NaN` | `C_TS` is zero or `Cs_in[12]` is huge. The `F_s` clamp should prevent negatives — confirm the clamp `min(..., F_in)` is present. Likely the clamp fires but masks a deeper input problem. | `src/specificClass.js#getEffluent` line `const F_s = Math.min(...)`. |
| Settler never updates after reactor changes | Reactor child is not on `'upstream'` position (warn logged but registration proceeds), or the listener is attached to the wrong emitter. | `_connectReactor` — listens on `reactor.emitter`, **NOT** `reactor.measurements.emitter`. |
| Return-sludge flow stays at 0 | `returnPump.measurements.type('flow').variant('measured').position('atEquipment')` has no current value. Wire a flow measurement child on the pump (with `asset.type='flow'`, `positionVsParent='atEquipment'`). | `_connectMachine`, pump's MeasurementContainer chain. |
| Three Fluent envelopes do not arrive at the downstream consumer | `payload.inlet` selector on the downstream reactor / pump mismatches (0 = effluent, 1 = surplus, 2 = return). | The downstream consumer's inlet routing. |
| `quantity (tss)` updates don't change `C_TS` | Measurement child's `asset.type` must be the literal string `"quantity (tss)"` (with the space + parenthesised "tss"). | `src/specificClass.js#_updateMeasurement` switch case. |
| `data.influent` inject is silently dropped | Payload must be an object `{F, C}`. A string, number, or array logs a warn (`data.influent expects an object {F, C}; got <type>`) and short-circuits. | `src/commands/handlers.js#dataInfluent`. |
| Settler logs an `error` `Type '<x>' not recognized for measured update.` | A measurement child has registered with an `asset.type` settler doesn't recognise. The re-emit still happened &mdash; the error is about the absence of a `_updateMeasurement` switch case. Currently only `quantity (tss)` mutates state. | `_updateMeasurement`. |
| `child.register` topic logs `child.register skipped: missing child/source for id=<x>` | The given node id doesn't resolve to a Node-RED node with a `.source` (i.e. an EVOLV domain). Verify the id is correct and the target node has already been deployed. | `src/commands/handlers.js#childRegister`. |
| Status badge stuck on `idle: no influent` | `F_in <= 0`. Either no reactor has fired `stateChange` yet, or `data.influent` has not been sent with a positive `F`. | `src/specificClass.js#getStatusBadge`. |
> Never ship `enableLog: 'debug'` in a demo &mdash; fills the container log within seconds and obscures real errors.
---
## Related pages
| Page | Why |
|:---|:---|
| [Home](Home) | Intuitive overview |
| [Reference &mdash; Contracts](Reference-Contracts) | Topic + config + child filters |
| [Reference &mdash; Architecture](Reference-Architecture) | Code map, reactor &harr; settler wiring, mass-balance math |
| [Reference &mdash; Limitations](Reference-Limitations) | Known issues and open questions |
| [reactor &mdash; Examples](https://gitea.wbd-rd.nl/RnD/reactor/wiki/Reference-Examples) | The upstream parent &mdash; how to drive `stateChange` |
| [EVOLV &mdash; Topology Patterns](https://gitea.wbd-rd.nl/RnD/EVOLV/wiki/Topology-Patterns) | Where settler fits in a larger plant |