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

8.1 KiB
Raw Blame History

Reference — Limitations

code-ref

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 — 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 — 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 — 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 — 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 — 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) — 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 — the shape differs (single value vs full process payload). See 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)" — 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 — 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 — it incorrectly claims the node has no runtime modules Internal — stale doc
Refresh examples/README.md — it is a one-line "Placeholder structure" Internal — 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 — 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 — reflected in the schema description and the curve files — is bottom-coverage percentage (the fraction of tank floor occupied by membrane). Typical fine-bubble installs run 1025 %. 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.


Page Why
Home Intuitive overview
Reference — Contracts Topic + config + child registration
Reference — Architecture Code map, OTR / ΔP pipeline, output ports
Reference — Examples Shipped flows + debug recipes
reactor — Limitations Where the typical parent currently consumes diffuser output