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>
149 lines
8.9 KiB
Markdown
149 lines
8.9 KiB
Markdown
# Reference — Examples
|
|
|
|

|
|
|
|
> [!NOTE]
|
|
> Every example flow shipped under `nodes/measurement/examples/`, plus how to load them, what they show, and the debug recipes that go with them. Live source: `nodes/measurement/examples/`.
|
|
>
|
|
> Pending full node review (2026-05). Tier-1/2/3 visual-first example flows are still TODO (tracked in the superproject `MEMORY.md` "TODO: Example Flows"). The current shipped flows pre-date the refactor; treat them as smoke tests, not as production templates.
|
|
|
|
---
|
|
|
|
## Shipped examples
|
|
|
|
| File | Tier | Dependencies | What it shows | Status |
|
|
|:---|:---:|:---|:---|:---|
|
|
| `basic.flow.json` | 1 | EVOLV only | Single measurement node driven by inject buttons — analog scalar input, scaling enabled, three debug taps on Port 0/1/2. | Legacy pre-refactor shape, still imports. |
|
|
| `integration.flow.json` | 2 | EVOLV only | Parent-child wiring — measurement registers as a child of another node and emits its `<type>.measured.<position>` events. | Legacy pre-refactor shape. |
|
|
| `edge.flow.json` | 3 | EVOLV only | Invalid / edge payload driving for robustness checks (non-numeric strings, object in analog mode, …). | Legacy pre-refactor shape. |
|
|
|
|
The three legacy files predate the AssetResolver refactor and the analog-vs-digital mode flag. They still deploy (the editor will accept the older shape and `nodeClass.buildDomainConfig` reshapes whatever it finds), but the recommended Tier-1/2/3 visual-first replacements are still to be written.
|
|
|
|
> [!IMPORTANT]
|
|
> **TODO — Tier-1/2/3 visual-first flows.** Replace the three legacy files with:
|
|
> - `01 - Basic Analog.json` — one measurement, inject + scaling + smoothing + outlier-detection toggle + simulator.
|
|
> - `02 - Integration with rotatingMachine.json` — measurement registered as a pressure sensor on a `rotatingMachine`, Port 2 auto-register on deploy, parent's prediction updates as the measurement value moves.
|
|
> - `03 - Digital Multi-Channel.json` — one measurement in `digital` mode with 2–3 channels (e.g. `level-a`, `temp-a`, `flow-a`) fed by a single object-payload inject.
|
|
|
|
---
|
|
|
|
## 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/measurement/examples/basic.flow.json \
|
|
http://localhost:1880/flows
|
|
```
|
|
|
|
---
|
|
|
|
## Example — `basic.flow.json`
|
|
|
|
Single-measurement flow with the minimum kit to exercise scaling.
|
|
|
|
### Nodes on the tab
|
|
|
|
| Type | Purpose |
|
|
|:---|:---|
|
|
| `inject` | One-shot `topic: 'measurement', payload: 42` (legacy alias of `data.measurement`) |
|
|
| `measurement` | The unit under test — analog mode, scaling enabled (0..100 → 0..10), `mean` smoothing, window 5 |
|
|
| `debug` × 3 | Port 0 (process), Port 1 (InfluxDB), Port 2 (registration) |
|
|
|
|
### What to do after deploy
|
|
|
|
1. Click the inject. Port 0 fires with `mAbs ≈ 4.2` (42 scaled into 0..10), `mPercent ≈ 42`.
|
|
2. Send another value via the same inject (edit the inject payload to `60`). `totalMinValue` / `totalMaxValue` start tracking, `mAbs` jumps to ~6.0.
|
|
3. Send `topic: 'set.simulator'` (use a second inject). `tick()` starts driving `inputValue` through `Simulator.step()` every 1000 ms; Port 0 updates appear automatically.
|
|
4. Send `topic: 'cmd.calibrate'`. If `stdDev <= 0.01` (the default `stabilityThreshold`), `config.scaling.offset` jumps to `inputMin - currentOutput`; if not, a warn appears in the log.
|
|
5. Send `topic: 'set.outlier-detection'`, then inject a wildly out-of-band value (e.g. `9999`). With outlier detection on the value is dropped with `Outlier detected. Ignoring value=9999`.
|
|
|
|
> [!IMPORTANT]
|
|
> **Screenshot needed.** Editor capture of `basic.flow.json` plus the Port 0 debug output. Save as `wiki/_partial-screenshots/measurement/basic-flow.png`. Replace this callout with the image link.
|
|
|
|
---
|
|
|
|
## Example — `integration.flow.json`
|
|
|
|
Demonstrates the parent-child handshake: the measurement node's Port 2 auto-fires `child.register` to its parent on deploy, and the parent then receives the `<type>.measured.<position>` event whenever a new reading lands.
|
|
|
|
> [!IMPORTANT]
|
|
> **Screenshot needed.** Editor capture of `integration.flow.json` showing the wiring. Save as `wiki/_partial-screenshots/measurement/integration-flow.png`.
|
|
|
|
> [!NOTE]
|
|
> TODO: confirm the integration flow targets a real EVOLV parent (e.g. `rotatingMachine`) versus a mock function node; if it's a mock, the Tier-2 replacement should use a real parent.
|
|
|
|
---
|
|
|
|
## Example — `edge.flow.json`
|
|
|
|
Drives the node with malformed inputs to verify the warn paths land cleanly:
|
|
|
|
- Non-numeric string in analog mode → `Invalid numeric measurement payload: <value>`.
|
|
- Object payload in analog mode → `analog mode received an object payload (keys: …). Switch Input Mode to 'digital' …`.
|
|
- Numeric scalar in digital mode → `digital mode received a number (…); expected an object …`.
|
|
- Outlier toggle on/off mid-stream → verifies `analogChannel.outlierDetection.enabled` mirrors `config.outlierDetection.enabled`.
|
|
|
|
> [!IMPORTANT]
|
|
> **Screenshot needed.** Editor capture of `edge.flow.json` plus the log lines each inject triggers. Save as `wiki/_partial-screenshots/measurement/edge-flow.png`.
|
|
|
|
---
|
|
|
|
## Debug recipes
|
|
|
|
| Symptom | First thing to check | Where to look |
|
|
|:---|:---|:---|
|
|
| Parent never receives `<type>.measured.<position>` | `asset.type` must match the parent's filter exactly (e.g. `flow` — not `flow-electromagnetic`). Position labels lowercase in the event name. | `config.asset.type` + parent's `childRegistrationUtils` filter. |
|
|
| Outliers seem to pass through | `outlierDetection.enabled` may be off (default `false`). Toggle with `set.outlier-detection`. With `<2` samples in the buffer, `_isOutlier` returns `false` regardless. | `Channel._isOutlier`. |
|
|
| `cmd.calibrate` does nothing | Calibrator requires `stdDev <= calibration.stabilityThreshold` over `storedValues`. If `storedValues.length < 2`, `isStable()` returns `false` (legacy shape). | `src/calibration/calibrator.js` `isStable`, `calibrate`. |
|
|
| Digital payload silently dropped | Unknown channel keys are reported only at `debug` log level (`digital payload contained unmapped keys`). Numeric values that fail `Number.isFinite` warn at `warn`. | `Measurement.handleDigitalPayload`. |
|
|
| Simulator still running after toggle off | `tick()` reads `config.simulation.enabled` each tick. Confirm the toggle actually mutated the config (the `set.simulator` handler is idempotent — it just flips). | `Measurement.tick`, `toggleSimulation`. |
|
|
| Port 0 emits nothing after `data.measurement` | Analog: `_writeOutput` only emits when `rounded !== outputAbs`. A repeated identical value is silent by design. | `Channel._writeOutput`. |
|
|
| `mPercent` is stuck at `0` or unbounded | `processRange <= 0` (i.e. `absMax <= absMin`); percent falls back to `totalMinValue / totalMaxValue` which start at `0` / `0`. Configure `absMin < absMax`. | `Channel._computePercent`. |
|
|
| Scaling output looks clamped | `_applyScaling` clamps the input to `[inputMin, inputMax]` before mapping. Wide-band sensors need `inputMin / inputMax` set to the full physical range. | `Channel._applyScaling`. |
|
|
| `mAbs` jumps after `cmd.calibrate` | Expected. Calibration sets `config.scaling.offset = baseline - currentOutputAbs`, which makes the next reading land on the baseline (`inputMin` when scaling enabled, `absMin` otherwise). | `Calibrator.calibrate`. |
|
|
| Legacy `setpoint` / `simulator` topics work without warning | First fire emits a one-time deprecation warning via `BaseNodeAdapter`'s alias handling. Subsequent fires are silent — the topic still works. | `commands/index.js` `aliases`. |
|
|
|
|
> Never ship `enableLog: 'debug'` in a demo — fills the container log within seconds and obscures real errors.
|
|
|
|
---
|
|
|
|
## 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).
|
|
|
|
---
|
|
|
|
## Related pages
|
|
|
|
| Page | Why |
|
|
|:---|:---|
|
|
| [Home](Home) | Intuitive overview |
|
|
| [Reference — Contracts](Reference-Contracts) | Topic + config + child registration |
|
|
| [Reference — Architecture](Reference-Architecture) | Code map + per-`Channel` pipeline + lifecycle |
|
|
| [Reference — Limitations](Reference-Limitations) | Known issues and open questions |
|
|
| [rotatingMachine — Examples](https://gitea.wbd-rd.nl/RnD/rotatingMachine/wiki/Reference-Examples) | Most common consumer of measurement |
|
|
| [EVOLV — Topology Patterns](https://gitea.wbd-rd.nl/RnD/EVOLV/wiki/Topology-Patterns) | Where measurement fits in a larger plant |
|