P9 follow-up: expand wiki template + add Home/Archive template
WIKI_TEMPLATE.md — extend the canonical per-node page from 9 to 14 sections:
+ Header band (commit hash + regen date)
+ Code map (flowchart TB w/ subgraphs over concern modules)
+ Child registration (mirrors ChildRouter declarations)
+ Data model — getOutput() (abstract schema + optional concrete sample)
+ Debug recipes (symptom → first thing to check)
+ AUTOGEN markers around topic-contract + data-model schema so the
Phase 9 regen script can rewrite in place.
+ 'Picking a visual' table: Mermaid is default, plots/SVG/screenshots
allowed where they serve the data.
+ Archive banner snippet.
WIKI_HOME_TEMPLATE.md (new) — Home.md + Archive.md templates:
- Platform-wide Mermaid graph of 11 active nodes, S88-coloured.
- Navigation table grouped by S88 level.
- Standards-pointer table to .claude/rules + .claude/refactor docs.
- Live refactor-status table for returning visitors.
- Archive index template with archival-date column.
No wiki pages written yet — next step is one worked example
(pumpingStation) before any change to the Gitea wiki repo.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
141
.claude/refactor/WIKI_HOME_TEMPLATE.md
Normal file
141
.claude/refactor/WIKI_HOME_TEMPLATE.md
Normal file
@@ -0,0 +1,141 @@
|
||||
# Platform wiki home — `Home.md` template
|
||||
|
||||
The landing page for the EVOLV Gitea wiki. Same visual-first rules as `WIKI_TEMPLATE.md`: diagrams lead, tables annotate, ≤ 60 words per paragraph.
|
||||
|
||||
`Home.md` answers three questions for a first-time visitor:
|
||||
|
||||
1. **What is this platform?** One paragraph.
|
||||
2. **What nodes exist and how do they relate?** One platform-wide Mermaid graph + a navigation table.
|
||||
3. **Where do I find the conventions?** A pointer table to the rule files in `.claude/`.
|
||||
|
||||
Plus a live refactor-status table so a returning visitor knows what changed since they last looked.
|
||||
|
||||
## Template — copy the block below as the seed for `Home.md`
|
||||
|
||||
```
|
||||
<!-- BEGIN TEMPLATE — Home.md -->
|
||||
|
||||
# EVOLV — Wastewater treatment plant automation
|
||||
|
||||
> **Reflects code as of `<git short hash>` · regenerated `<YYYY-MM-DD>` via `npm run wiki:home`**
|
||||
|
||||
EVOLV is a Node-RED node library for wastewater plant automation, developed by the R&D team at Waterschap Brabantse Delta. Nodes follow the ISA-88 (S88) batch control standard. The library exposes 11 active nodes spanning four S88 levels: from Process Cell down to Control Module, plus one utility node for dashboard integration.
|
||||
|
||||
## Platform overview
|
||||
|
||||
~~~mermaid
|
||||
flowchart TB
|
||||
subgraph PC["Process Cell"]
|
||||
ps[pumpingStation]:::pc
|
||||
end
|
||||
subgraph UN["Unit"]
|
||||
mgc[machineGroupControl]:::unit
|
||||
vgc[valveGroupControl]:::unit
|
||||
reactor[reactor]:::unit
|
||||
settler[settler]:::unit
|
||||
monster[monster]:::unit
|
||||
end
|
||||
subgraph EM["Equipment"]
|
||||
rm[rotatingMachine]:::equip
|
||||
v[valve]:::equip
|
||||
diff[diffuser]:::equip
|
||||
end
|
||||
subgraph CM["Control Module"]
|
||||
meas[measurement]:::ctrl
|
||||
end
|
||||
subgraph UT["Utility"]
|
||||
dash[dashboardAPI]:::neutral
|
||||
end
|
||||
ps --> mgc
|
||||
ps --> vgc
|
||||
mgc --> rm
|
||||
vgc --> v
|
||||
reactor --> diff
|
||||
meas -.data.-> rm
|
||||
meas -.data.-> v
|
||||
meas -.data.-> reactor
|
||||
meas -.data.-> settler
|
||||
classDef pc fill:#0c99d9,color:#fff
|
||||
classDef unit fill:#50a8d9,color:#000
|
||||
classDef equip fill:#86bbdd,color:#000
|
||||
classDef ctrl fill:#a9daee,color:#000
|
||||
classDef neutral fill:#dddddd,color:#000
|
||||
~~~
|
||||
|
||||
S88 colours: Process Cell `#0c99d9`, Unit `#50a8d9`, Equipment `#86bbdd`, Control Module `#a9daee`. Solid arrow = parent/child relationship. Dashed arrow = data flow (`measurement` feeds many node types).
|
||||
|
||||
## Live nodes
|
||||
|
||||
| S88 | Node | One-liner | Wiki |
|
||||
|---|---|---|---|
|
||||
| 🟦 Process Cell | **pumpingStation** | Manages a wet-well basin, hands demand to one or more group controllers. | [→](pumpingStation) |
|
||||
| 🔷 Unit | **machineGroupControl** | Load-sharing across a group of rotatingMachines. | [→](machineGroupControl) |
|
||||
| 🔷 Unit | **valveGroupControl** | Coordinated valve control across a group of valves. | [→](valveGroupControl) |
|
||||
| 🔷 Unit | **reactor** | Bioreactor — couples diffuser + measurements + kinetics. | [→](reactor) |
|
||||
| 🔷 Unit | **settler** | Settler / clarifier modelling. | [→](settler) |
|
||||
| 🔷 Unit | **monster** | Composite-sample sensor surrogate. | [→](monster) |
|
||||
| 🟦 Equipment | **rotatingMachine** | Single pump / compressor — curves, state machine, prediction. | [→](rotatingMachine) |
|
||||
| 🟦 Equipment | **valve** | Single valve actuator with FSM. | [→](valve) |
|
||||
| 🟦 Equipment | **diffuser** | Aeration diffuser, gas-side modelling. | [→](diffuser) |
|
||||
| 🔹 Control Module | **measurement** | Sensor signal-conditioning, scaling, calibration. | [→](measurement) |
|
||||
| ⚪ Utility | **dashboardAPI** | Bridge between FlowFuse dashboard widgets and EVOLV. | [→](dashboardAPI) |
|
||||
|
||||
## Standards & conventions
|
||||
|
||||
| Document | What it covers | Where |
|
||||
|---|---|---|
|
||||
| Node architecture (3-tier) | entry → nodeClass → specificClass | `.claude/rules/node-architecture.md` |
|
||||
| Flow layout (Node-RED tabs) | Tab boundaries, lanes, S88 colours, link channels | `.claude/rules/node-red-flow-layout.md` |
|
||||
| Topic naming (`set.` / `cmd.` / `evt.`) | Canonical input + output topics | `.claude/refactor/CONTRACTS.md` §1 |
|
||||
| Wiki page shape | Per-node page template | `.claude/refactor/WIKI_TEMPLATE.md` |
|
||||
| Wiki home shape | This page's template | `.claude/refactor/WIKI_HOME_TEMPLATE.md` |
|
||||
| generalFunctions stability rules | What's safe to change | `.claude/rules/general-functions.md` |
|
||||
|
||||
## Refactor status
|
||||
|
||||
| Tier | What | Status |
|
||||
|---|---|---|
|
||||
| 1 | Add infra in generalFunctions (additive only) | ✅ done |
|
||||
| 2 | Pilot: pumpingStation | ✅ done |
|
||||
| 3 | Convert measurement, MGC, rotatingMachine | ✅ done |
|
||||
| 4 | Convert valve, VGC, reactor, settler, monster, diffuser | ✅ done |
|
||||
| 4* | dashboardAPI | ⏸️ out of scope (no `generalFunctions` dep) |
|
||||
| 5 | Canonical topic names + alias deprecation | 🟡 partial |
|
||||
| 6 | development → main promotion | ⏳ pending Docker E2E |
|
||||
| 7 | Wiki refactor (this work) | 🟡 in progress |
|
||||
|
||||
## Archive
|
||||
|
||||
Pre-refactor pages live under `Archive/`. See [Archive index](Archive).
|
||||
|
||||
<!-- END TEMPLATE -->
|
||||
```
|
||||
|
||||
## Notes for the maintainer
|
||||
|
||||
- `npm run wiki:home` (not yet built) re-renders the platform Mermaid block if any node's `softwareType` registration changes. Until then, the diagram is hand-maintained.
|
||||
- Refactor-status rows flip as tiers land. Anyone landing a tier updates the table in the same PR.
|
||||
- The "Live nodes" table is hand-maintained but small — bulk changes happen only when a node is added or retired.
|
||||
- The Mermaid graph above mirrors what's in `.claude/rules/node-red-flow-layout.md` §10.1 (lane convention). If the rule changes, mirror it here.
|
||||
|
||||
## Archive index — `Archive.md` template
|
||||
|
||||
A separate page that lists every archived page with its archival date and the era it describes.
|
||||
|
||||
```
|
||||
<!-- BEGIN TEMPLATE — Archive.md -->
|
||||
|
||||
# Archive — pre-refactor wiki pages
|
||||
|
||||
Pages kept for historical reference. **Do not update them.** Corrections go on the current page; if you find a meaningful inaccuracy in the archived page, leave it and add a note to the *current* page explaining what changed.
|
||||
|
||||
| Page | Era | Archived on |
|
||||
|---|---|---|
|
||||
| [pumpingStation (pre-refactor)](Archive/pumpingStation-pre-refactor) | Pre-Tier-2 (May 2026) | 2026-05-11 |
|
||||
| [rotatingMachine (pre-refactor)](Archive/rotatingMachine-pre-refactor) | Pre-Tier-3 (May 2026) | 2026-05-11 |
|
||||
| ... | ... | ... |
|
||||
|
||||
Each archived page carries the standard banner at its top (see `WIKI_TEMPLATE.md` → Archive banner).
|
||||
|
||||
<!-- END TEMPLATE -->
|
||||
```
|
||||
@@ -1,48 +1,83 @@
|
||||
# Wiki page template — every node uses this shape
|
||||
|
||||
This is the canonical structure for every node's gitea wiki landing
|
||||
page. Visual-first, scannable, ≤ 60 words of unbroken prose anywhere.
|
||||
Canonical structure for every node's Gitea wiki landing page. **Visual-first**, scannable, ≤ 60 words per paragraph anywhere on the page.
|
||||
|
||||
## Why this shape
|
||||
|
||||
The platform now has 12 nodes with the same architectural skeleton
|
||||
(BaseDomain + BaseNodeAdapter + ChildRouter + commands registry).
|
||||
The wiki should mirror that uniformity: a reader can flip between
|
||||
nodes and find the same sections in the same order.
|
||||
The platform has 12 nodes that all share the same architectural skeleton (BaseDomain + BaseNodeAdapter + ChildRouter + commands registry). The wiki should mirror that uniformity: a reader flips between nodes and finds the same 14 sections in the same order. Diagrams lead. Tables annotate. Prose only fills gaps.
|
||||
|
||||
Mermaid blocks lead. Tables annotate. Prose only fills gaps.
|
||||
## Picking a visual
|
||||
|
||||
## Template — copy this whole file as the seed for each node's wiki
|
||||
The default is Mermaid (Gitea renders it natively). It's the right tool for graph-shaped things — neighbours, lifecycles, state machines, file maps. But Mermaid doesn't render data: when a section is about *what a curve looks like* or *what the predicted vs measured signal does over time*, use:
|
||||
|
||||
| Need | Tool | Where the artifact lives |
|
||||
|---|---|---|
|
||||
| Graph (nodes + edges, hierarchy, state) | Mermaid `flowchart` / `sequenceDiagram` / `stateDiagram-v2` | inline in the wiki page |
|
||||
| XY data (pump curves, prediction trace, drift over time) | Generated PNG/SVG via a small `npm run wiki:plots` script | committed under `wiki/_partial-plots/<NodeName>/*.svg` |
|
||||
| Table of facts / config / topics | Markdown table | inline |
|
||||
| Screenshot (dashboard, editor form) | PNG ≤ 200 KB | `wiki/_partial-screenshots/<NodeName>/*.png` |
|
||||
| ASCII layout (when Mermaid is overkill) | code block | inline |
|
||||
|
||||
Lead with the visual that serves the section. Don't gate it on "is this Mermaid".
|
||||
|
||||
## Section list
|
||||
|
||||
Sections 1–9 and 11–14 are mandatory for every node. Section 10 (State chart) is mandatory for stateful nodes (`rotatingMachine`, `valve`, `pumpingStation`, …) and skipped for pure aggregators (`measurement`, `dashboardAPI`).
|
||||
|
||||
| # | Section | Visual lead | Auto-gen? |
|
||||
|---|---|---|---|
|
||||
| 0 | Header band (git hash + regen date) | — | yes |
|
||||
| 1 | What this node is | — (single paragraph) | no |
|
||||
| 2 | Position in the platform | Mermaid `flowchart LR` | no |
|
||||
| 3 | Capability matrix | table | no |
|
||||
| 4 | Code map | Mermaid `flowchart TB` w/ subgraphs | no |
|
||||
| 5 | Topic contract | table | **yes** (`wiki:contract`) |
|
||||
| 6 | Child registration | Mermaid + table | no |
|
||||
| 7 | Lifecycle | Mermaid `sequenceDiagram` | no |
|
||||
| 8 | Data model — `getOutput()` | table + concrete sample | **yes** (`wiki:datamodel`) |
|
||||
| 9 | Configuration — form ↔ config | Mermaid `flowchart TB` | no |
|
||||
| 10 | State chart (stateful only) | Mermaid `stateDiagram-v2` | no |
|
||||
| 11 | Examples | table + screenshots | no |
|
||||
| 12 | Debug recipes | table | no |
|
||||
| 13 | When NOT to use this node | bullets | no |
|
||||
| 14 | Known limitations | table | no |
|
||||
|
||||
## Template — copy the block below as the seed for each node's wiki
|
||||
|
||||
(The block uses standard markdown syntax. The outer fence below is for visual delimitation in this README only; when seeding a new wiki page, copy the *content* between the `BEGIN TEMPLATE` / `END TEMPLATE` markers verbatim.)
|
||||
|
||||
```
|
||||
<!-- BEGIN TEMPLATE — wiki/<NodeName>.md -->
|
||||
|
||||
```markdown
|
||||
# <Node name>
|
||||
|
||||
> One-paragraph "what is this node". Cap 60 words. Plain English.
|
||||
> Example: "rotatingMachine models a single pump or compressor —
|
||||
> takes pressure measurements and predicts flow + power from supplier
|
||||
> curves. State machine drives startup/shutdown sequences."
|
||||
> **Reflects code as of `<git short hash>` · regenerated `<YYYY-MM-DD>` via `npm run wiki:all`**
|
||||
> If this banner is stale, the page may be out of date. Treat as informative, not authoritative.
|
||||
|
||||
## 1. Position in the platform
|
||||
## 1. What this node is
|
||||
|
||||
```mermaid
|
||||
One paragraph, ≤ 60 words. Plain English. State the *role*, not the *implementation*.
|
||||
|
||||
> Example: "**rotatingMachine** models a single pump or compressor. It takes pressure measurements from upstream and downstream, predicts the resulting flow + power from supplier-provided characteristic curves, and drives a state machine for startup/shutdown sequences. Used as a child of `machineGroupControl` when grouped, or directly under a `pumpingStation`."
|
||||
|
||||
## 2. Position in the platform
|
||||
|
||||
~~~mermaid
|
||||
flowchart LR
|
||||
parent[<Parent type — e.g. machineGroupControl>] -->|set.demand| this[<This node>]
|
||||
parent[machineGroupControl<br/>Unit]:::unit -->|set.demand| this[rotatingMachine<br/>Equipment]:::equip
|
||||
this -->|evt.state-change| parent
|
||||
sensor[<measurement>] -->|data.flow| this
|
||||
sensor_up[measurement up]:::ctrl -->|data.pressure| this
|
||||
sensor_dn[measurement down]:::ctrl -->|data.pressure| this
|
||||
this -->|child.register| parent
|
||||
classDef proc fill:#0c99d9,color:#fff
|
||||
classDef unit fill:#50a8d9
|
||||
classDef equip fill:#86bbdd
|
||||
classDef ctrl fill:#a9daee
|
||||
class this <classname-by-S88-level>
|
||||
```
|
||||
classDef unit fill:#50a8d9,color:#000
|
||||
classDef equip fill:#86bbdd,color:#000
|
||||
classDef ctrl fill:#a9daee,color:#000
|
||||
~~~
|
||||
|
||||
The Mermaid block shows the typical neighbours and the data direction.
|
||||
Pick the right `classDef` for the S88 level (Process Cell / Unit /
|
||||
Equipment / Control Module). Match the colour codes from
|
||||
`.claude/rules/node-red-flow-layout.md`.
|
||||
S88 colours are mandatory. Map: Process Cell `#0c99d9`, Unit `#50a8d9`, Equipment `#86bbdd`, Control Module `#a9daee`. Source of truth: `.claude/rules/node-red-flow-layout.md`.
|
||||
|
||||
## 2. Capability matrix
|
||||
## 3. Capability matrix
|
||||
|
||||
| Capability | Status | Notes |
|
||||
|---|---|---|
|
||||
@@ -50,54 +85,141 @@ Equipment / Control Module). Match the colour codes from
|
||||
| Receives manual setpoint | ✅ | Topic `set.setpoint` |
|
||||
| Auto-start on demand from parent | ✅ | |
|
||||
| Self-calibrating | ❌ | Calibration is operator-triggered (`cmd.calibrate`) |
|
||||
| Supports multi-parent registration | ⚠️ | Possible but not fully tested — see CONTRACT.md notes |
|
||||
| Supports multi-parent registration | ⚠️ | Possible but not fully tested — see CONTRACT.md |
|
||||
|
||||
Keep this table to ≤ 10 rows. For longer feature inventories, link out.
|
||||
Cap at 10 rows. Longer inventories link out.
|
||||
|
||||
## 3. Topic contract
|
||||
## 4. Code map
|
||||
|
||||
> Auto-generated from `src/commands/index.js`. Do NOT hand-edit.
|
||||
> Re-run `npm run wiki:contract` after any change to commands.
|
||||
~~~mermaid
|
||||
flowchart TB
|
||||
subgraph nodeRED["nodeClass.js — adapter (BaseNodeAdapter)"]
|
||||
nc["buildDomainConfig()<br/>static DomainClass, commands"]
|
||||
end
|
||||
subgraph domain["specificClass.js — orchestrator (BaseDomain)"]
|
||||
sc["Machine.configure()<br/>declares ChildRouter rules"]
|
||||
end
|
||||
subgraph concerns["src/ concern modules"]
|
||||
curves["curves/<br/>characteristic curve loader"]
|
||||
prediction["prediction/<br/>flow + power predictor"]
|
||||
drift["drift/<br/>prediction-vs-measured assessor"]
|
||||
flow["flow/<br/>aggregation + smoothing"]
|
||||
state["state/<br/>FSM transitions"]
|
||||
io["io/<br/>output formatting helpers"]
|
||||
display["display/<br/>status badge composition"]
|
||||
end
|
||||
nc --> sc
|
||||
sc --> concerns
|
||||
~~~
|
||||
|
||||
| Module | Owns | Read first if you're changing… |
|
||||
|---|---|---|
|
||||
| `curves/` | Supplier characteristic curves, interpolation | Curve fitting, asset selection |
|
||||
| `prediction/` | Flow + power predictors | Predicted output values |
|
||||
| `drift/` | Quality of prediction vs measurement | Health status / alarms |
|
||||
| `flow/` | Aggregation, smoothing | Flow reporting |
|
||||
| `state/` | FSM (off → idle → operational → …) | Startup / shutdown behaviour |
|
||||
|
||||
Update this section when you rename or split a directory.
|
||||
|
||||
## 5. Topic contract
|
||||
|
||||
> **Auto-generated** from `src/commands/index.js`. Do NOT hand-edit between the markers. Re-run `npm run wiki:contract`.
|
||||
|
||||
<!-- BEGIN AUTOGEN: topic-contract -->
|
||||
|
||||
| Canonical topic | Aliases | Payload | Effect |
|
||||
|---|---|---|---|
|
||||
| `set.mode` | `setMode` | `string` (`auto` \| `manual` \| `maintenance`) | Switches operating mode. |
|
||||
| `cmd.startup` | `execSequence` (with `payload.action='startup'`) | `{source: string}` | Triggers startup sequence (state machine). |
|
||||
| ... | ... | ... | ... |
|
||||
| `set.mode` | `setMode` | `string` (`auto`\|`manual`\|`maintenance`) | Switches operating mode. |
|
||||
| `cmd.startup` | `execSequence` (with `payload.action='startup'`) | `{source: string}` | Triggers startup sequence. |
|
||||
|
||||
## 4. Lifecycle — what one tick (or event) does
|
||||
<!-- END AUTOGEN: topic-contract -->
|
||||
|
||||
```mermaid
|
||||
## 6. Child registration
|
||||
|
||||
What children this node accepts and what it does with each event the child can emit. Mirrors the `ChildRouter` declarations in `specificClass.js` → `configure()`.
|
||||
|
||||
~~~mermaid
|
||||
flowchart LR
|
||||
subgraph kids["accepted children (softwareType)"]
|
||||
m_up["measurement<br/>type=pressure<br/>position=upstream"]:::ctrl
|
||||
m_dn["measurement<br/>type=pressure<br/>position=downstream"]:::ctrl
|
||||
end
|
||||
m_up -->|data.pressure| handler1[pressure handler<br/>updates measurements/upstream]
|
||||
m_dn -->|data.pressure| handler2[pressure handler<br/>updates measurements/downstream]
|
||||
handler1 --> recompute[prediction.recompute]
|
||||
handler2 --> recompute
|
||||
recompute --> emit[emitter.emit 'output-changed']
|
||||
classDef ctrl fill:#a9daee,color:#000
|
||||
~~~
|
||||
|
||||
| softwareType | filter | wired to | side-effect |
|
||||
|---|---|---|---|
|
||||
| `measurement` | `type=pressure, position=upstream` | `pressureHandlers.onUpstream` | prediction recomputes |
|
||||
| `measurement` | `type=pressure, position=downstream` | `pressureHandlers.onDownstream` | prediction recomputes |
|
||||
|
||||
## 7. Lifecycle — what one event (or tick) does
|
||||
|
||||
~~~mermaid
|
||||
sequenceDiagram
|
||||
participant parent
|
||||
participant node as this node
|
||||
participant sensor as measurement child
|
||||
participant out as Port-0 output
|
||||
|
||||
sensor->>node: data.flow (12 m³/h, downstream)
|
||||
node->>node: route → measurementHandlers
|
||||
node->>node: drift assess → predictionHealth update
|
||||
node->>node: getOutput composes snapshot
|
||||
sensor->>node: data.pressure (3.4 bar, upstream)
|
||||
node->>node: ChildRouter → pressure handler
|
||||
node->>node: prediction recomputes
|
||||
node->>node: drift assesses prediction vs measured
|
||||
node->>node: getOutput() composes snapshot
|
||||
node->>out: msg{topic, payload, [process|influx]}
|
||||
parent->>node: set.demand (15 m³/h)
|
||||
node->>node: control.dispatch
|
||||
node->>parent: child.register (handshake)
|
||||
```
|
||||
node->>node: state.handleInput → maybe transition
|
||||
~~~
|
||||
|
||||
Keep the diagram to one screen. If you have multiple distinct flows
|
||||
(idle vs running vs error), pick the most common one and link out
|
||||
to the others.
|
||||
One screen max. For multiple distinct flows (idle vs running vs error), pick the most common and link out to the rest.
|
||||
|
||||
## 5. Configuration — editor form & where each field lands
|
||||
## 8. Data model — `getOutput()`
|
||||
|
||||
```mermaid
|
||||
What lands on Port 0. Composed in domain `getOutput()`, then delta-compressed by `outputUtils.formatMsg`.
|
||||
|
||||
**Abstract schema** (always include):
|
||||
|
||||
<!-- BEGIN AUTOGEN: datamodel-schema -->
|
||||
|
||||
| Key | Type | Unit | Source |
|
||||
|---|---|---|---|
|
||||
| `<type>.<variant>.<position>.<childId>` | number | per `UnitPolicy.output(type)` | MeasurementContainer |
|
||||
| `state` | string | — | `state/` |
|
||||
| `predictionHealth.level` | 0–3 | — | `drift/` |
|
||||
| `predictionHealth.flags` | string[] | — | `drift/` |
|
||||
|
||||
<!-- END AUTOGEN: datamodel-schema -->
|
||||
|
||||
**Concrete sample** (include only when the *shape* is hard to grok from the schema — e.g. nested objects, sparse keys, or unit conventions a newcomer would get wrong):
|
||||
|
||||
~~~json
|
||||
{
|
||||
"flow.measured.downstream.default": 12.4,
|
||||
"pressure.measured.upstream.default": 3.4,
|
||||
"power.measured.atequipment.default": 18.2,
|
||||
"state": "operational",
|
||||
"predictionHealth": { "level": 1, "flags": ["pressure_init_warming"], "message": "warmup phase", "source": "rotatingMachine#pump-A" }
|
||||
}
|
||||
~~~
|
||||
|
||||
Concrete samples must come from a known-good test run — never made-up values. Regenerate when concern modules change shape.
|
||||
|
||||
## 9. Configuration — editor form ↔ config keys
|
||||
|
||||
~~~mermaid
|
||||
flowchart TB
|
||||
subgraph editor["Node-RED editor form"]
|
||||
f1[Mode]
|
||||
f2[Demand]
|
||||
f3[Threshold percent]
|
||||
f1[Mode dropdown]
|
||||
f2[Demand input]
|
||||
f3[Threshold %]
|
||||
end
|
||||
subgraph config["Domain config"]
|
||||
subgraph config["Domain config slice"]
|
||||
c1[control.mode]
|
||||
c2[control.targets.demand]
|
||||
c3[safety.thresholdPercent]
|
||||
@@ -105,28 +227,17 @@ flowchart TB
|
||||
f1 --> c1
|
||||
f2 --> c2
|
||||
f3 --> c3
|
||||
```
|
||||
~~~
|
||||
|
||||
| Form field | Config key | Default | Range |
|
||||
|---|---|---|---|
|
||||
| Mode | `control.mode` | `auto` | enum |
|
||||
| Demand | `control.targets.demand` | `0` | ≥ 0 |
|
||||
| Threshold % | `safety.thresholdPercent` | `95` | 0–100 |
|
||||
| Form field | Config key | Default | Range | Where used |
|
||||
|---|---|---|---|---|
|
||||
| Mode | `control.mode` | `auto` | enum | `control/strategies.js` |
|
||||
| Demand | `control.targets.demand` | `0` | ≥ 0 | `dispatch/` |
|
||||
| Threshold % | `safety.thresholdPercent` | `95` | 0–100 | `safety/guards.js` |
|
||||
|
||||
## 6. Examples
|
||||
## 10. State chart (stateful nodes only)
|
||||
|
||||
| Tier | File | What it shows |
|
||||
|---|---|---|
|
||||
| Basic | `examples/01-Basic.json` | Inject + dashboard, no parent |
|
||||
| Integration | `examples/02-Integration.json` | Wired to `<parent>` + 1 measurement child |
|
||||
| Dashboard | `examples/03-Dashboard.json` | Live FlowFuse charts of flow / power / state |
|
||||
|
||||
Include one screenshot per tier where helpful — keep them under 200 KB
|
||||
each. Link to a runnable docker compose snippet under `examples/README.md`.
|
||||
|
||||
## 7. State chart (only for stateful nodes)
|
||||
|
||||
```mermaid
|
||||
~~~mermaid
|
||||
stateDiagram-v2
|
||||
[*] --> off
|
||||
off --> idle: cmd.startup
|
||||
@@ -136,78 +247,100 @@ stateDiagram-v2
|
||||
coolingdown --> off: cooldown_time elapsed
|
||||
operational --> emergencystop: cmd.estop
|
||||
emergencystop --> off: cmd.reset
|
||||
```
|
||||
~~~
|
||||
|
||||
Skip this section for stateless / pure-aggregator nodes (e.g. measurement,
|
||||
dashboardAPI).
|
||||
Skip this section for stateless nodes (`measurement`, `dashboardAPI`).
|
||||
|
||||
## 8. When you would NOT use this node
|
||||
## 11. Examples
|
||||
|
||||
- "Use rotatingMachine for a SINGLE pump. For groups of 2+ pumps with
|
||||
load sharing, use machineGroupControl as the parent."
|
||||
- "Use measurement only for sensor signal conditioning. For setpoint
|
||||
injection, use the dashboard or a parent's command topic."
|
||||
| Tier | File | What it shows | Mandatory? |
|
||||
|---|---|---|---|
|
||||
| Basic | `examples/01-Basic.json` | Inject + dashboard, no parent | ✅ |
|
||||
| Integration | `examples/02-Integration.json` | Wired to `<parent>` + 1 child | ✅ if has parent |
|
||||
| Dashboard | `examples/03-Dashboard.json` | Live FlowFuse charts | ⭕ optional |
|
||||
|
||||
Two or three bullets, each one sentence. Forces explicit non-goals.
|
||||
One screenshot per tier where helpful. PNG ≤ 200 KB under `wiki/_partial-screenshots/<NodeName>/`. Docker compose snippet under `examples/README.md`.
|
||||
|
||||
## 9. Known limitations / current issues
|
||||
## 12. Debug recipes
|
||||
|
||||
How to diagnose the common failure modes. One table row per recipe.
|
||||
|
||||
| Symptom | First thing to check | Where to look |
|
||||
|---|---|---|
|
||||
| Status badge stuck on `⚠ no input` | Did the measurement child register? Watch Port 2. | Editor debug tap on Port 2 |
|
||||
| `flow.measured.downstream` not updating | Confirm the child's emitted topic matches the `ChildRouter` filter. | `specificClass.js` → `configure()` |
|
||||
| Prediction `level=3` | Run `enableLog: 'debug'` *temporarily*; look for drift evaluator output. | container log |
|
||||
|
||||
> Never ship `enableLog: 'debug'` in a demo — fills the container log within seconds and obscures real errors. Use only for live debugging.
|
||||
|
||||
## 13. When you would NOT use this node
|
||||
|
||||
Two or three bullets, one sentence each. Forces explicit non-goals.
|
||||
|
||||
- Use rotatingMachine for a **single** pump. For groups of 2+ pumps with load sharing, use `machineGroupControl` as the parent.
|
||||
- Don't use rotatingMachine to model a passive non-return valve — use `valve` (no curve, no FSM-driven motor).
|
||||
|
||||
## 14. Known limitations / current issues
|
||||
|
||||
| # | Issue | Tracked in |
|
||||
|---|---|---|
|
||||
| 1 | Drift confidence drops to 0 when pressure is missing for > 30 s | `OPEN_QUESTIONS.md` 2026-05-10 |
|
||||
| 2 | Multi-parent teardown ordering | `OPEN_QUESTIONS.md` 2026-05-10 |
|
||||
| 1 | Drift confidence drops to 0 when pressure missing > 30 s | `.claude/refactor/OPEN_QUESTIONS.md` |
|
||||
| 2 | Multi-parent teardown ordering | Gitea issue #42 |
|
||||
|
||||
Link to repo issues when they exist. Keep this table living — it's the
|
||||
contract with the user about what "works".
|
||||
Link to repo issues when they exist. Keep this table living — it's the contract with the user about what "works".
|
||||
|
||||
<!-- END TEMPLATE -->
|
||||
```
|
||||
|
||||
## Hard rules for editors
|
||||
|
||||
1. The Mermaid platform-position block is **section 1**, before any
|
||||
prose. Diagrams first.
|
||||
2. Each section opens with a diagram or table; prose annotates the
|
||||
visual, not the other way round.
|
||||
3. No more than **60 words of unbroken prose** anywhere on a page.
|
||||
4. The topic contract is **auto-generated** from `src/commands/index.js`.
|
||||
Don't hand-write it.
|
||||
5. Use `flowchart LR` for node connections, `sequenceDiagram` for
|
||||
tick-to-output flow, `stateDiagram-v2` for state machines.
|
||||
6. Skip `classDiagram` (we don't expose classes to users) and `gantt`
|
||||
(no schedules in node docs).
|
||||
1. Section 2 (Position in the platform) appears **before any prose**. Diagrams lead.
|
||||
2. Every section opens with a diagram, table, or chart. Prose annotates the visual; never the other way round.
|
||||
3. **Max 60 words per paragraph.** A paragraph longer than that splits into bullets or moves into a table.
|
||||
4. The topic contract (section 5) and data-model schema (section 8) are **auto-generated** between the `BEGIN AUTOGEN` / `END AUTOGEN` markers. Don't hand-edit between markers.
|
||||
5. Mermaid is the default for graph structures. Use generated SVG/PNG for XY data (curves, time series). Use tables for facts.
|
||||
6. Skip `classDiagram` (we don't expose classes to users) and `gantt` (no schedules in node docs).
|
||||
7. **Concrete sample payloads must come from a known-good test run.** Made-up numbers rot silently.
|
||||
8. S88 colour codes are non-negotiable in section 2. Match the palette in `.claude/rules/node-red-flow-layout.md`.
|
||||
|
||||
## Auto-generation notes (Phase 9 follow-up)
|
||||
## Archive banner — paste at the top of every archived page
|
||||
|
||||
The "Topic contract" table in section 3 should be generated by a small
|
||||
`npm run wiki:contract` script, per node. Implementation:
|
||||
|
||||
```js
|
||||
// scripts/generate-contract.js
|
||||
const commands = require('../src/commands');
|
||||
const rows = commands.map((c) => ({
|
||||
topic: c.topic,
|
||||
aliases: (c.aliases || []).join(', ') || '—',
|
||||
payload: describeSchema(c.payloadSchema),
|
||||
effect: c.description || '— (handler-only)',
|
||||
}));
|
||||
console.log(toMarkdownTable(rows));
|
||||
```
|
||||
> **⚠️ ARCHIVED — pre-refactor (Tier 1–4, 2026-05)**
|
||||
>
|
||||
> This page describes the architecture before the platform refactor.
|
||||
> The current page is **[<NodeName>](../<NodeName>)**.
|
||||
>
|
||||
> Kept for historical reference only. **Do not update.**
|
||||
```
|
||||
|
||||
`describeSchema` walks the lightweight schema (`{type, properties}`)
|
||||
and produces a one-line readable form. Wire each node's `package.json`:
|
||||
Archived pages move to `Archive/<NodeName>-pre-refactor.md` in the Gitea wiki repo. After moving, the page is read-only — corrections go on the current page, not the archive.
|
||||
|
||||
## Auto-generation — Phase 9 follow-up
|
||||
|
||||
Two scripts per node, wired in `package.json`:
|
||||
|
||||
```json
|
||||
"scripts": {
|
||||
"wiki:contract": "node scripts/generate-contract.js > wiki/_partial-topics.md"
|
||||
"wiki:contract": "node scripts/generate-contract.js > wiki/_partial-topics.md",
|
||||
"wiki:datamodel": "node scripts/generate-datamodel.js > wiki/_partial-datamodel.md",
|
||||
"wiki:all": "npm run wiki:contract && npm run wiki:datamodel"
|
||||
}
|
||||
```
|
||||
|
||||
Then a thin wiki page `<Node>.md` includes `_partial-topics.md` (or
|
||||
the section is regenerated in-place between markers).
|
||||
- **`generate-contract.js`** walks `src/commands/index.js`, emits one table row per descriptor between the topic-contract markers.
|
||||
- **`generate-datamodel.js`** instantiates the domain with the default config, calls `getOutput()`, emits the abstract schema between the datamodel-schema markers. If `wiki/sample-output.fixture.json` exists, the concrete-sample block below the markers is also overwritten.
|
||||
- `describeSchema` walks the lightweight `{type, properties}` schema and produces a one-line readable form.
|
||||
|
||||
## What lives in the gitea wiki vs the repo
|
||||
## What lives where
|
||||
|
||||
- `wiki/` directory in the repo (where present): canonical, version-
|
||||
controlled.
|
||||
- gitea wiki UI: cosmetic mirror — re-rendered from `wiki/` on push.
|
||||
- Auto-generated partials (`_partial-*.md`): committed under `wiki/`
|
||||
but generated; never hand-edited.
|
||||
| Artifact | Location | Hand-edited? |
|
||||
|---|---|---|
|
||||
| Canonical page source | `wiki/<NodeName>.md` in the node's repo | Yes (except inside AUTOGEN markers) |
|
||||
| Auto-generated partials | written inline between AUTOGEN markers | No — generated |
|
||||
| Plots | `wiki/_partial-plots/<NodeName>/*.svg` | No — generated |
|
||||
| Screenshots | `wiki/_partial-screenshots/<NodeName>/*.png` | Yes (committed) |
|
||||
| Gitea wiki UI | mirror — re-rendered from `wiki/` on push | No |
|
||||
| Archived pre-refactor pages | `Archive/<NodeName>-pre-refactor.md` in the wiki repo | No (read-only after archival) |
|
||||
|
||||
The Gitea wiki repo is separate from each node's source repo. The `wiki/` directory in each node's repo is canonical; a `wiki-sync` workflow (not yet built) mirrors it into the Gitea wiki repo on each push to `development` / `main`.
|
||||
|
||||
Reference in New Issue
Block a user