# diffuser — Contract Hand-maintained for Phase 6; the `## Inputs` table is generated from `src/commands/index.js` (see Phase 9 generator). Keep ≤ 100 lines. ## Inputs (msg.topic on Port 0) | Canonical | Aliases (deprecated) | Payload | Effect | |---|---|---|---| | `data.flow` | `air_flow` | `number` — airflow in Nm³/h | Calls `source.setFlow(payload)`; clamps to ≥ 0 and recomputes OTR. | | `set.density` | `density` | `number` — diffuser density (per m²) | Calls `source.setDensity(payload)` and recomputes. | | `set.water-height` | `height_water` | `number` — water column height in m | Calls `source.setWaterHeight(payload)`; clamps to ≥ 0 and recomputes head + total pressure. | | `set.header-pressure` | `header_pressure` | `number` — header gauge pressure in mbar | Calls `source.setHeaderPressure(payload)` and recomputes. | | `set.elements` | `elements` | `number` — element count (rounded; must be > 0) | Calls `source.setElementCount(payload)` and recomputes per-element flow. | | `set.alfa-factor` | `alfaFactor` | `number` — alpha correction (≥ 0) | Calls `source.setAlfaFactor(payload)` and recomputes oxygen output. | Aliases log a one-time deprecation warning the first time they fire. ## Outputs (msg.topic on Port 0/1/2) - **Port 0 (process):** `msg.topic = config.general.name`. Payload built by `outputUtils.formatMsg(..., 'process')` from `getOutput()` — delta-compressed (only changed fields are emitted). Fields: - `iPressure`, `iMWater`, `iFlow` — echoed inputs. - `nFlow` — normalised airflow (Nm³/h). - `oOtr` — interpolated oxygen transfer rate (g O₂ / Nm³). - `oPLoss` — total head loss (mbar) = static head + diffuser ΔP. - `oKgo2H` — kg O₂ per hour at current operating point. - `oFlowElement` — flow per element (Nm³/h/element). - `efficiency` — combined OTR/ΔP efficiency (0–100). - `slope` — local OTR-vs-flow slope. - `oZoneOtr` — reactor zone OTR (kg O₂ / m³ / day) computed against `diffuser.zoneVolume`; `0` when zone volume is unset. - `idle` — true when `data.flow ≤ 0`. - `warning`, `alarm` — string arrays describing flow-per-element band excursions. - **Port 1 (InfluxDB telemetry):** same shape as Port 0, formatted with the `'influxdb'` formatter. - **Port 2 (registration):** at startup the node sends one `{ topic: 'child.register', payload: , positionVsParent, distance }` to the upstream parent (typically a reactor). `positionVsParent` defaults to `'atEquipment'`. ## Port-count change (Phase 6) Pre-refactor the diffuser exposed 4 outputs (process, dbase, reactor control with `topic: 'OTR'`, parent registration). The reactor control message merged into Port 0 as `oZoneOtr`; consumers that previously listened to the dedicated control port should switch to reading `payload.oZoneOtr` from the process output. The legacy `OTR` topic is removed in this refactor — there is no alias, since the data shape differs (single value vs full process payload). ## Events emitted by `source.measurements.emitter` None today. The diffuser does not currently publish typed measurements through `MeasurementContainer`; all output flows via `getOutput()`. A future phase may promote `oOtr` and `oZoneOtr` to typed series so parent reactors can subscribe through the standard `ChildRouter` handshake. ## Events emitted by `source.emitter` - `output-changed` — fires whenever an input setter recomputes the oxygen-transfer state. `BaseNodeAdapter` listens and pushes the delta-compressed Port 0 / Port 1 messages. ## Children registered by this node None. The diffuser is a leaf Equipment Module; it registers itself with its parent (reactor / process cell) via the Port 2 handshake.