Files
diffuser/wiki/Reference-Limitations.md
znetsixe 8c03fe774c docs(wiki): full 5-page wiki matching the rotatingMachine reference format
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>
2026-05-19 09:42:13 +02:00

128 lines
8.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Reference &mdash; Limitations
![code-ref](https://img.shields.io/badge/code--ref-4973a8b-blue)
> [!NOTE]
> Pending full node review (2026-05). What `diffuser` does not do, current rough edges, and open questions. Some items below are auditor inferences from CONTRACT.md and the source &mdash; flagged where uncertain. Open items live in `.agents/improvements/IMPROVEMENTS_BACKLOG.md` in the superproject.
---
## When you would not use this node
| Scenario | Use instead |
|:---|:---|
| Coarse-bubble / jet / surface aeration without a fine-bubble OTR curve | Model OTR externally and feed the reactor's α-OTR endpoint directly. |
| The blower already publishes oxygen-transfer telemetry | Don't duplicate &mdash; pipe the blower's `oKgo2H` straight into the reactor. |
| You only need flow-per-element warning bands without OTR | A `measurement` node with bands is lighter and has typed series. |
| A diffuser zone with multiple distinct membrane types | Instantiate one diffuser node per type and sum the `oKgo2H` upstream. |
| A non-fine-bubble curve at non-standard membrane area | Configure `diffuser.membraneAreaPerElement` to override the curve metadata. |
---
## Known limitations
### Stateless by design &mdash; no FSM
The diffuser has no state machine. The `idle` flag is a derived predicate (`i_flow ≤ 0`), not a state. There is no startup / warmup / cooldown / e-stop concept &mdash; the model is purely algebraic. The blower upstream owns those concerns.
If you need a "diffuser is offline / under maintenance" indicator, today that has to be carried by the parent or by a separate metadata path. Open question whether a future revision should introduce a soft `mode` field for dashboards.
### No typed `MeasurementContainer` emission
All output flows via `getOutput()` and the delta-compressed Port 0 message. The diffuser does not publish typed measurement series (`otr.measured.atequipment.<id>`, etc.) through `MeasurementContainer.emitter`. Parents that want OTR via the standard `ChildRouter` handshake must wait for the future phase that promotes `oOtr` / `oZoneOtr` to typed series. Tracked in `CONTRACT.md` `## Events emitted by source.measurements.emitter`.
### No mode / source allow-lists
Unlike rotatingMachine and pumpingStation, the diffuser accepts every input topic from every source. There is no `auto` / `virtualControl` / `fysicalControl` distinction. Open question whether this matters in practice &mdash; the diffuser has no "physical" path today (no real OTR sensor wired in).
### Alarm hysteresis is hardcoded
`warning.flow.min.hyst = 2` and `alarm.flow.min.hyst = 10` are literals in `configure()` (specificClass.js). They are not config-driven. If a particular installation has wider acceptable bands, you currently have to edit the source. Tracked.
### Asset model fallback can mask configuration drift
`_loadSpecs()` falls back to `DEFAULT_DIFFUSER_MODEL` (`gva-elastox-r`) when the configured model is missing from the registry. This is a deliberate availability-first choice (don't crash the constructor in production) &mdash; but it means a typo or stale asset id silently runs against the GVA curve instead of erroring. The constructor logs the fallback; consumers should monitor logs at deploy.
### `data.flow` clamps silently
`setFlow(v)` clamps to `Math.max(0, ...)`. Negative payloads land as zero with no warn. This mostly does the right thing (negative airflow is nonsensical) but a payload bug upstream is invisible. Open question whether to log at warn on clamp.
### No typed `MeasurementContainer` consumption
The diffuser is a leaf node and accepts no children. There is no path for a real OTR sensor (a hypothetical dissolved-oxygen probe) to feed into this node and refine the curve-based prediction. The whole pipeline is open-loop. Tracked as a future scope item.
### Hidden config defaults
`diffuser.localAtmPressure` (1013.25 mbar) and `diffuser.waterDensity` (997 kg/m³) are config keys but typically not exposed in the editor form. If your plant runs at significant altitude or the process water has unusual density, you have to override them via the raw config or a Setup-tab inject. Cosmetic; tracked.
### Pre-Phase-6 port-count migration
Pre-refactor the diffuser exposed **four** outputs: process, dbase, a dedicated reactor-control message with `topic: 'OTR'`, and parent registration. The reactor-control message merged into Port 0 as `oZoneOtr`; consumers reading the dedicated control port must migrate to `payload.oZoneOtr`. No alias is provided &mdash; the shape differs (single value vs full process payload). See [Migration notes](#migration-notes).
### Test scaffolding is placeholder
`test/basic/`, `test/integration/`, `test/edge/` each contain a single `structure-…test.js` placeholder file. `test/README.md` still says "Placeholder structure (diffuser currently has no runtime module files)" &mdash; the README is **stale**; the runtime modules exist (`src/specificClass.js`, `src/nodeClass.js`, `src/commands/*.js`). A real domain-test pass against the supplier curves is TODO.
---
## Open questions (tracked)
| Question | Where it lives |
|:---|:---|
| Should `oOtr` / `oZoneOtr` be promoted to typed `MeasurementContainer` series so parents can subscribe via `ChildRouter`? | Internal &mdash; future phase |
| Should alarm hysteresis (2 % / 10 %) be config-driven? | Internal |
| Should `setFlow(v)` log a warn on negative clamp? | Internal |
| Should the editor expose `localAtmPressure` / `waterDensity` for altitude / non-water cases? | Internal |
| Should a soft `mode` field exist for "offline / maintenance" dashboards even without an FSM? | Internal |
| Should the curve-fallback path warn at deploy AND every N ticks for a configurable window? | Internal |
| Refresh `test/README.md` &mdash; it incorrectly claims the node has no runtime modules | Internal &mdash; stale doc |
| Refresh `examples/README.md` &mdash; it is a one-line "Placeholder structure" | Internal &mdash; stale doc |
---
## Migration notes
### From pre-Phase-6 four-port output
Pre-Phase-6 the diffuser emitted on four ports:
| Port (old) | Topic | Replacement |
|:---:|:---|:---|
| 0 | process | unchanged |
| 1 | dbase | unchanged |
| 2 | `OTR` (single value, reactor control) | **merged into Port 0 as `payload.oZoneOtr`**. No alias provided. |
| 3 | parent registration | now Port 2 |
If you have a downstream `function` or `link out` that listens for `msg.topic === 'OTR'` on the legacy port-2, switch it to reading `payload.oZoneOtr` from the Port-0 process message. The shape differs (single number vs full process payload) so the migration is not mechanical &mdash; you must update the downstream node's payload-extraction logic too.
### From legacy alias topics
Aliases are accepted but log a one-time deprecation warning the first time they fire:
| Alias (deprecated) | Canonical |
|:---|:---|
| `air_flow` | `data.flow` |
| `density` | `set.density` |
| `height_water` | `set.water-height` |
| `header_pressure` | `set.header-pressure` |
| `elements` | `set.elements` |
| `alfaFactor` | `set.alfa-factor` |
There is no deprecation-removal date set. Migrate flows opportunistically.
### From `diffuser.density` semantics drift
An earlier refactor mis-tagged `diffuser.density` as "elements per m²". The current semantics &mdash; reflected in the schema description and the curve files &mdash; is **bottom-coverage percentage** (the fraction of tank floor occupied by membrane). Typical fine-bubble installs run 10&ndash;25 %. If a saved flow still carries the legacy elements-per-m² value (typically 2.4), update it to the bottom-coverage % the asset is actually installed at. The default has been updated to `15`.
---
## Related pages
| Page | Why |
|:---|:---|
| [Home](Home) | Intuitive overview |
| [Reference &mdash; Contracts](Reference-Contracts) | Topic + config + child registration |
| [Reference &mdash; Architecture](Reference-Architecture) | Code map, OTR / ΔP pipeline, output ports |
| [Reference &mdash; Examples](Reference-Examples) | Shipped flows + debug recipes |
| [reactor &mdash; Limitations](https://gitea.wbd-rd.nl/RnD/reactor/wiki/Reference-Limitations) | Where the typical parent currently consumes diffuser output |