From 02e6663af64d6e7266d3779cdd371ed1828a2f8a Mon Sep 17 00:00:00 2001 From: vps1_gitea_admin Date: Mon, 11 May 2026 18:30:16 +0000 Subject: [PATCH] refactor: regenerate Home.md with 14-section visual-first template --- Home.md | 242 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 242 insertions(+) diff --git a/Home.md b/Home.md index e69de29..a8b2e53 100644 --- a/Home.md +++ b/Home.md @@ -0,0 +1,242 @@ +# settler + +> **Reflects code as of `7bf464b` · regenerated `2026-05-11` via `npm run wiki:all`** +> If this banner is stale, the page may be out of date. Treat as informative, not authoritative. + +## 1. What this node is + +**settler** is an S88 Unit that models a secondary clarifier. It takes the upstream reactor's effluent stream, performs a 13-species TSS mass balance, and splits it into three Fluent envelopes: clarified effluent, surplus sludge, and return sludge. A downstream return pump (rotatingMachine child) draws the return-sludge flow. + +## 2. Position in the platform + +```mermaid +flowchart LR + upstream[reactor
upstream
Unit]:::unit + settler[settler
Unit]:::unit + downstream[reactor
downstream
Unit]:::unit + return[rotatingMachine
return pump
Equipment]:::equip + tss[measurement
type=quantity (tss)
position=atequipment]:::ctrl + + upstream -.stateChange.-> settler + settler -->|Fluent inlet=0,1,2| downstream + return -->|child.register downstream| settler + settler -.F_sr.-> return + tss -->|quantity (tss).measured.atequipment| settler + classDef unit fill:#50a8d9,color:#000 + classDef equip fill:#86bbdd,color:#000 + classDef ctrl fill:#a9daee,color:#000 +``` + +S88 colours: Unit `#50a8d9`, Equipment `#86bbdd`, Control Module `#a9daee`. Source of truth: `.claude/rules/node-red-flow-layout.md`. + +## 3. Capability matrix + +| Capability | Status | Notes | +|---|---|---| +| TSS mass-balance split (3 streams) | ✅ | Effluent / surplus / return derived from `F_in * Cs[12] / C_TS`. | +| Particulate zeroing in effluent | ✅ | Species 7–12 set to 0 in effluent when `F_s > 0`. | +| Particulate concentration in sludge | ✅ | Species 7–12 scaled by `F_in / F_s` in surplus + return. | +| Return-pump flow draw | ✅ | `F_sr` = min(pump flow, F_s). Surplus = F_s − F_sr. | +| F_s clamp to F_in | ✅ | Prevents negative effluent when X_TS_in > C_TS. | +| Manual influent override | ✅ | `data.influent` lets ops supply `{ F, C }` directly. | +| Multiple reactor upstreams | ❌ | Only one `upstreamReactor` slot; last registration wins. | +| Stateful FSM | ❌ | Stateless transform — recomputes on every push. | + +## 4. Code map + +```mermaid +flowchart TB + subgraph nodeRED["nodeClass.js — adapter (BaseNodeAdapter)"] + nc["buildDomainConfig()
static DomainClass = Settler
static commands"] + end + subgraph domain["specificClass.js — orchestrator (BaseDomain)"] + sc["Settler.configure()
ChildRouter rules
getEffluent — TSS split
_connectReactor (manual listener)"] + end + subgraph commands["src/commands/"] + cmds["index.js + handlers.js
data.influent + aliases"] + end + nc --> sc + nc --> cmds +``` + +| Module | Owns | Read first if you're changing… | +|---|---|---| +| `specificClass.js` | All domain logic: getEffluent split, reactor + machine + measurement wiring, getOutput, getStatusBadge. | Mass-balance math, child wiring, telemetry shape. | +| `commands/` | Single command (`data.influent`) + aliases + payload validation. | Manual-influent topic, new aliases. | + +Settler is small enough (~140 LOC) that no concern-split was needed (per P6.6). + +## 5. Topic contract + +> **Auto-generated** from `src/commands/index.js`. Do NOT hand-edit between the markers. Re-run `npm run wiki:contract`. + + + +| Canonical topic | Aliases | Payload | Unit | Effect | +|---|---|---|---|---| +| `data.influent` | `influent`, `setInfluent` | `any` | — | Push the influent stream (payload: {F: flow m3/h, C: [concentrations mg/L]}). | +| `child.register` | `registerChild` | `string` | — | Register a child node (typically a measurement) with this settler. | + + + +## 6. Child registration + +```mermaid +flowchart LR + subgraph kids["accepted children (softwareType)"] + m["measurement"]:::ctrl + r["reactor
upstream"]:::unit + mach["machine
downstream"]:::equip + end + m -->|"<type>.measured.<position>"| h_m[_connectMeasurement] + r -.stateChange.-> h_r[_connectReactor
manual listener] + mach -->|registered| h_mach[_connectMachine
sets returnPump] + h_r --> pull[upstreamReactor.getEffluent] + pull --> emit[notifyOutputChanged] + classDef ctrl fill:#a9daee,color:#000 + classDef unit fill:#50a8d9,color:#000 + classDef equip fill:#86bbdd,color:#000 +``` + +| softwareType | filter | wired to | side-effect | +|---|---|---|---| +| `measurement` | any | `_connectMeasurement` | Re-emits on settler's measurements; `quantity (tss)` updates `C_TS`. | +| `reactor` | `positionVsParent=upstream` (warns otherwise) | `_connectReactor` | Stores as `upstreamReactor`; subscribes to its **own** `emitter` (NOT `measurements.emitter`) for `'stateChange'`. | +| `machine` | `positionVsParent=downstream` | `_connectMachine` | Stores as `returnPump`; sets `machine.upstreamSource = settler`. | + +### 6.1 Reactor ↔ settler wiring (the load-bearing bit) + +The reactor pushes its `stateChange` event on `reactor.emitter`, not `reactor.measurements.emitter`. The standard `router.onMeasurement` path can't subscribe — so settler attaches the listener manually inside `_connectReactor`. On each fire, settler **pulls** the upstream effluent via `reactor.getEffluent` and copies it into `this.F_in` + `this.Cs_in`. + +`reactor.getEffluent` historically returned either an array (3-stream) or a single envelope — the 2026-03-02 `_connectReactor` fix preserves both shapes: + +```js +const raw = this.upstreamReactor.getEffluent; +const effluent = Array.isArray(raw) ? raw[0] : raw; +this.F_in = effluent.payload.F; +this.Cs_in = effluent.payload.C; +this.notifyOutputChanged(); +``` + +If you change the reactor's effluent shape, this is the line to update. + +## 7. Lifecycle — what one stateChange does + +```mermaid +sequenceDiagram + participant reactor as upstream reactor + participant settler as settler + participant pump as return pump child + participant downstream as downstream consumer + participant out as Port-0 output + + reactor->>settler: emitter.emit('stateChange') + settler->>reactor: pull getEffluent + reactor-->>settler: { F, C[13] } + settler->>settler: F_in = F, Cs_in = C + settler->>pump: read measurements.flow.measured.atequipment + pump-->>settler: returnFlow + settler->>settler: getEffluent — split into 3 inlets + settler->>out: [Fluent inlet=0, Fluent inlet=1, Fluent inlet=2] + out->>downstream: 3 msgs on Port 0 +``` + +The split runs lazily inside `getEffluent`: each call recomputes from current `F_in`, `Cs_in`, `C_TS`, and the pump's reported `flow.measured.atequipment`. + +## 8. Data model — `getOutput()` + +Port 0 carries the 3-envelope Fluent stream directly; Port 1 (this snapshot) is the scalar dashboard view. + + + +| Key | Type | Unit | Sample | +|---|---|---|---| +| `C_TS` | number | — | `2500` | +| `F_eff` | number | — | `0` | +| `F_in` | number | — | `0` | +| `F_return` | number | — | `0` | +| `F_surplus` | number | — | `0` | + + + +**Concrete sample** (typical operating point): + +```json +{ + "F_in": 1000, + "C_TS": 2500, + "F_eff": 850.0, + "F_surplus": 50.0, + "F_return": 100.0 +} +``` + +`F_eff + F_surplus + F_return = F_in` always holds (modulo float). Particulates concentrate by `F_in / F_s` in the surplus + return streams. + +## 9. Configuration — editor form ↔ config keys + +```mermaid +flowchart TB + subgraph editor["Node-RED editor form"] + f1[Name] + f2[Position vs parent] + f3[Logging level] + end + subgraph config["Domain config slice"] + c1[general.name] + c2[functionality.positionVsParent] + c3[general.logging.logLevel] + end + f1 --> c1 + f2 --> c2 + f3 --> c3 +``` + +| Form field | Config key | Default | Range | Where used | +|---|---|---|---|---| +| Name | `general.name` | `Settler` | string | display + Port-1 topic | +| ID | `general.id` | `null` | nullable string | child registration key | +| Software type | `functionality.softwareType` | `settler` | string | parent-side router filter | +| Position vs parent | `functionality.positionVsParent` | `downstream` | enum: `upstream` / `atEquipment` / `downstream` | parent-side routing | +| Logging level | `general.logging.logLevel` | `info` | enum | logger threshold | + +Settler has no operational config of its own — all behaviour is driven by the runtime state (`F_in`, `Cs_in`, `C_TS`). Tune behaviour by feeding it different reactor effluents or `C_TS` measurements. + +## 10. State chart + +Skipped — settler is stateless. Each `stateChange` (or `data.influent`, or `quantity (tss)` update) recomputes the 3 Fluent streams from scratch. + +## 11. Examples + +| Tier | File | What it shows | Status | +|---|---|---|---| +| Basic | `examples/basic.flow.json` | Inject `data.influent`, watch 3-stream split | ✅ in repo | +| Integration | `examples/integration.flow.json` | reactor (upstream) + settler + return pump | ✅ in repo | +| Edge | `examples/edge.flow.json` | F_s clamp + zero-influent fallback | ✅ in repo | + +One screenshot per tier where helpful. PNG ≤ 200 KB under `wiki/_partial-screenshots/settler/`. + +## 12. Debug recipes + +| Symptom | First thing to check | Where to look | +|---|---|---| +| `F_eff` negative or NaN | `C_TS` zero or `Cs_in[12]` huge. F_s clamp should prevent — confirm clamp present. | `specificClass.js → getEffluent` | +| Settler never updates after reactor changes | Reactor child not on `'upstream'` position, or listener attached to wrong emitter. | `_connectReactor` — listens on `reactor.emitter`, NOT `measurements.emitter`. | +| Return-sludge flow = 0 | `returnPump.measurements.type('flow').variant('measured').position('atEquipment')` empty. Wire a flow measurement on the pump. | `_connectMachine`, pump measurement chain. | +| 3 Fluent envelopes not arriving downstream | `payload.inlet` selector on the downstream reactor mismatches (0=eff, 1=surplus, 2=return). | downstream reactor's `data.fluent` handler. | +| `quantity (tss)` updates don't change `C_TS` | Measurement child's `asset.type` not `quantity (tss)` exactly. | `_updateMeasurement` switch. | + +## 13. When you would NOT use this node + +- Use settler for **secondary clarification** downstream of a biological reactor. For primary sedimentation (raw sewage), the species-7-12 zeroing is wrong — model that as a separate process. +- Don't use settler as a generic mass-balance node — the 13-species ASM3 vector is hard-coded. +- Skip settler when the downstream reactor doesn't need a 3-stream split (e.g. single-tank SBR). A direct reactor → reactor wire is lighter. + +## 14. Known limitations / current issues + +| # | Issue | Tracked in | +|---|---|---| +| 1 | Only one `upstreamReactor` slot — multi-reactor settlers not supported (last registration wins). | `_connectReactor` | +| 2 | TSS mass balance uses index 12 (`X_TS`) hard-coded — coupled tightly to ASM3 species ordering. | `getEffluent`, `_updateMeasurement` | +| 3 | Settler depends on `mathjs` (~14 MB install) but only uses it transitively via reactor; no direct mathjs call in settler code. | `package.json` | +| 4 | No flow-balance check at runtime — if particulate concentration drives F_s above F_in, the clamp masks an upstream bug rather than warning. | `getEffluent` |