diff --git a/wiki/Reference-Contracts.md b/wiki/Reference-Contracts.md index 703ac9a..8ed4c13 100644 --- a/wiki/Reference-Contracts.md +++ b/wiki/Reference-Contracts.md @@ -27,6 +27,48 @@ The **Unit** column reflects each descriptor's `units: { measure, default }` dec +### Input message examples + +One worked `msg` per accepted topic. Send these into **Port 0**. For unit-bearing +topics the commandRegistry converts `msg.unit` (or a `{ value, unit }` payload) to +the default unit *before* the handler runs — so the unit is optional and any +[compatible unit](https://gitea.wbd-rd.nl/RnD/EVOLV/wiki/Topic-Conventions) is accepted. + +```js +// 1. set.mode — switch control strategy +msg = { topic: 'set.mode', payload: 'manual' }; // manual | levelbased | flowbased | none + +// 2. child.register — register a child (usually arrives on Port 2 from the child; +// this is the manual form). payload = the child node's Node-RED id. +msg = { topic: 'child.register', payload: 'a1b2c3d4.ef567', positionVsParent: 'upstream' }; +// positionVsParent: upstream | downstream | atequipment (or in | out for predicted-flow children) + +// 3. cmd.calibrate.volume — seed the predicted-volume integrator (default m³) +msg = { topic: 'cmd.calibrate.volume', payload: 12.5 }; // 12.5 m³ +msg = { topic: 'cmd.calibrate.volume', payload: 12500, unit: 'L' }; // 12 500 L → auto-converted to 12.5 m³ + +// 4. cmd.calibrate.level — seed the predicted level (default m) +msg = { topic: 'cmd.calibrate.level', payload: 1.8 }; // 1.8 m + +// 5. set.inflow — push a measured inflow (default m³/h) +msg = { topic: 'set.inflow', payload: 45 }; // 45 m³/h +msg = { topic: 'set.inflow', payload: 12.5, unit: 'L/s' }; // 12.5 L/s → 45 m³/h +msg = { topic: 'set.inflow', payload: { value: 45, unit: 'm3/h' }, timestamp: 1716998400000 }; + +// 6. set.outflow — push a measured/forced outflow (default m³/h) +msg = { topic: 'set.outflow', payload: 30 }; // 30 m³/h drawn from the basin + +// 7. set.demand — operator outflow setpoint (default m³/h); ignored unless mode === 'manual' +msg = { topic: 'set.demand', payload: 120 }; // 120 m³/h + +// Built-in (every EVOLV node): query.units — ask which units each topic accepts. +// Replies on Port 0 with { topic:'query.units', payload:{ node, units } }. +msg = { topic: 'query.units', payload: null }; +``` + +> Deprecated aliases behave identically and log a one-time warning, e.g. +> `{ topic: 'q_in', payload: 45 }` ≡ `set.inflow`, `{ topic: 'Qd', payload: 120 }` ≡ `set.demand`. + --- ## Data model — `getOutput()` shape @@ -75,6 +117,52 @@ Sample values come from a stub instantiation in `wikiGen` — in a live depl > - `mode` — string, the active control strategy (`levelbased` / `manual` / `flowbased` / `none`). Echoes the most recent `set.mode` input. > - `manualDemand` — number (m³/h) or `null`. The operator outflow setpoint last accepted via `set.demand`; `null` outside `manual` mode. +### Output message examples + +The node emits on three ports every tick (`outputUtils.formatMsg`). Port 0 / Port 1 +fire only when at least one field changed (delta-compression); Port 2 fires once at +startup. `topic` is the station's configured name (here `"PS-Influent-01"`). + +```js +// Port 0 — process data. payload = only the keys that changed this tick. +msg = { + topic: 'PS-Influent-01', + payload: { + mode: 'levelbased', + direction: 'filling', + percControl: 25, + 'level.predicted.atequipment.default': 3.25, // m + 'volume.predicted.atequipment.default': 32.5, // m³ + timeleft: 400, // s, or null when steady + manualDemand: null // m³/h, or null outside manual mode + } +}; + +// Port 1 — InfluxDB telemetry. Same changed fields, wrapped for the InfluxDB node. +msg = { + topic: 'PS-Influent-01', + payload: { + measurement: 'PS-Influent-01', + fields: { percControl: 25, 'volume.predicted.atequipment.default': 32.5 }, + tags: { id: 'a1b2c3d4.ef567', softwareType: 'pumpingstation', type: 'pumpingStation' }, + timestamp: '2026-05-29T10:00:00.000Z' // Date + } +}; + +// Port 2 — registration handshake, sent once at startup to the upstream parent. +msg = { + topic: 'child.register', + payload: 'a1b2c3d4.ef567', // this node's id + positionVsParent: 'atEquipment', + distance: null +}; +``` + +> **Child-facing events** are not Port messages — they fire on +> `source.measurements.emitter` as `..`, e.g. event +> `volume.predicted.atequipment` with payload `{ value: 32.5, unit: 'm3', timestamp }`. +> Parents subscribe by event name. + --- ## Configuration schema — editor form to config keys