wiki: crisp overhaul — no decoration emoji, all 9 master pages refactored
Source-tree mirror of EVOLV.wiki.git refactor (27a42ee on wiki.git): - 7 master pages rewritten with clean design (Home, Architecture, Topology-Patterns, Topic-Conventions, Telemetry, Getting-Started, Glossary). Tables and Mermaid for visuals, gitea alert callouts for warnings, shields badges for metadata only. No emoji as decoration. - Archive.md becomes a removal-changelog pointing readers to git history and to the successor pages. - _Sidebar.md updated to navigate the new flat-name layout. - Concept / finding / manual pages: uniform mini-header (badges + "reference page" callout) added without rewriting domain content. - Every internal link now uses the flat naming that resolves on the live gitea wiki (Concept-ASM-Models, Finding-BEP-..., etc.). On wiki.git: 29 Archive-* pages hard-deleted (the git history preserves them; Archive.md documents the removal). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,80 +1,265 @@
|
||||
# Topic Conventions
|
||||
|
||||
> **Reflects code as of `9ab9f6b` · regenerated `2026-05-11`**
|
||||

|
||||

|
||||
|
||||
Naming rules, unit policy, and S88 colour palette. Source of truth: `.claude/refactor/CONTRACTS.md` §1.
|
||||
> [!NOTE]
|
||||
> Every `msg.topic` in EVOLV uses one of six prefixes. The prefix tells you the kind (setter vs trigger vs data vs query vs lifecycle vs event), not the target. Units are coerced before handlers run. S88 colours are mandatory in every diagram. Source of truth: `.claude/refactor/CONTRACTS.md` §1.
|
||||
|
||||
## Topic prefixes
|
||||
---
|
||||
|
||||
Every topic is `<prefix>.<verb>` lowercase. Five prefixes only.
|
||||
## The six prefixes
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
ui[UI / parent / driver]:::neutral
|
||||
node[Node]:::tier3
|
||||
child[Child]:::tier1
|
||||
ui["UI / parent / driver / function node"]
|
||||
node[Node]
|
||||
child["Child node"]
|
||||
ext["external consumers"]
|
||||
|
||||
ui -->|set.x / cmd.x| node
|
||||
node -->|evt.x| ui
|
||||
child -->|data.x| node
|
||||
node -->|data.x| child
|
||||
child -->|child.register| node
|
||||
ui -- "set. / cmd. / query." --> node
|
||||
node -. "evt." .-> ui
|
||||
node -. "evt." .-> ext
|
||||
child -- "data." --> node
|
||||
node -- "data." --> child
|
||||
child <-->|child.register| node
|
||||
|
||||
class ui,ext neutral
|
||||
class node tier3
|
||||
class child tier1
|
||||
|
||||
classDef neutral fill:#dddddd
|
||||
classDef tier3 fill:#50a8d9,color:#000
|
||||
classDef tier1 fill:#a9daee,color:#000
|
||||
```
|
||||
|
||||
| Prefix | Direction | Semantics | Examples |
|
||||
|---|---|---|---|
|
||||
| `set.` | inbound | Set a configurable value. **Idempotent**, no side-effects beyond storing the value. | `set.mode`, `set.demand`, `set.position` |
|
||||
| `cmd.` | inbound | Trigger an action. **Has side-effects** (state transitions, motor commands). | `cmd.startup`, `cmd.shutdown`, `cmd.calibrate`, `cmd.estop` |
|
||||
| `data.` | bidirectional | Carries measurement / process data. Used by `measurement → parent` and emitters. | `data.pressure`, `data.flow`, `data.temperature` |
|
||||
| `evt.` | outbound | Announces something happened. Consumer-driven. | `evt.state-change`, `evt.alarm`, `evt.health` |
|
||||
| `child.` | inbound (parent) | Child node lifecycle. | `child.register` (with legacy alias `registerChild`) |
|
||||
### Inbound (the node accepts on its input)
|
||||
|
||||
**Anti-patterns to avoid:**
|
||||
- ❌ A topic that does two things (`setStartup` to both set a flag *and* trigger startup). Split into `set.` + `cmd.`.
|
||||
- ❌ Reusing a `cmd.` topic for both inbound trigger and outbound ack — make a paired `evt.<verb>-complete`.
|
||||
- ❌ Per-node prefixes (`pump.set.demand`). The prefix is the *kind*, not the *target*.
|
||||
| Prefix | Idempotent | Meaning | Examples |
|
||||
|:---|:---|:---|:---|
|
||||
| `set.<noun>` | Yes | Setter. Replaces a state value with the supplied payload. Repeating with the same payload does nothing extra. | `set.mode`, `set.scaling`, `set.demand`, `set.inflow` |
|
||||
| `cmd.<verb>` | No | Imperative action. Triggers a transition or sequence. Repeating triggers it again (or is rejected). | `cmd.startup`, `cmd.shutdown`, `cmd.estop`, `cmd.calibrate` |
|
||||
| `data.<noun>` | n/a (values flow) | Bulk data input. Sensor readings, measurement values, raw streams. The node consumes them. | `data.measurement`, `data.flow`, `data.pressure` |
|
||||
| `query.<noun>` | Yes (read-only) | Synchronous query. The node responds on the same msg (or a sibling output). For dashboards / debug. | `query.curves`, `query.cog`, `query.snapshot` |
|
||||
| `child.<verb>` | n/a (plumbing) | Parent / child plumbing. Routed via Port 2. | `child.register`, `child.unregister` |
|
||||
|
||||
## Alias deprecation
|
||||
### Outbound (the node emits)
|
||||
|
||||
Legacy topic names (pre-refactor) are still accepted as aliases. The current alias map per node lives in `src/commands/index.js`. Common aliases:
|
||||
| Prefix | Meaning | Where it appears |
|
||||
|:---|:---|:---|
|
||||
| `evt.<noun>` | Event. A fact about something that just happened. Fire-and-forget — no consumer required. | `msg.topic` on Port 0; also fired on `this.emitter` for sibling modules |
|
||||
|
||||
The default measurement output (delta-compressed payload from `outputUtils.formatMsg`) keeps `msg.topic = config.general.name` per existing convention. `evt.*` is for additional event-shaped emissions, not the per-tick measurement stream.
|
||||
|
||||
> [!TIP]
|
||||
> The prefix is the kind, never the target. Don't write `pump.set.demand` — write `set.demand` and let routing handle which pump. The prefix system says explicitly what the message does; the target is identified by node id, not by topic.
|
||||
|
||||
> [!CAUTION]
|
||||
> One topic, two effects is a bug magnet. A topic like `setStartup` that both sets a flag and triggers startup should be split into `set.<noun>` and `cmd.<verb>`.
|
||||
|
||||
---
|
||||
|
||||
## Aliases and deprecation
|
||||
|
||||
Each `commands/index.js` declares the canonical name as `topic` and lists pre-refactor names in `aliases`. First use of each alias logs a one-time deprecation warning. Aliases are removed in Phase 7 after one release cycle.
|
||||
|
||||
```js
|
||||
{
|
||||
topic: 'set.mode',
|
||||
aliases: ['setMode', 'changemode'],
|
||||
payloadSchema: { type: 'string' },
|
||||
description: 'Switch the node between auto and manual control modes.',
|
||||
handler: handlers.setMode,
|
||||
}
|
||||
```
|
||||
|
||||
### Common alias map
|
||||
|
||||
| Canonical | Legacy aliases |
|
||||
|---|---|
|
||||
| `set.mode` | `setMode` |
|
||||
|:---|:---|
|
||||
| `set.mode` | `setMode`, `changemode` |
|
||||
| `set.demand` | `Qd`, `setDemand` |
|
||||
| `cmd.startup` | `execSequence` with `payload.action='startup'` |
|
||||
| `cmd.shutdown` | `execSequence` with `payload.action='shutdown'` |
|
||||
| `cmd.startup` | `execSequence` (with `payload.action='startup'`) |
|
||||
| `cmd.shutdown` | `execSequence` (with `payload.action='shutdown'`) |
|
||||
| `child.register` | `registerChild` |
|
||||
| `data.pressure` | `pressure` |
|
||||
| `data.flow` | `flow` |
|
||||
|
||||
Aliases are logged at debug level on use. Plan is to remove them in a future major version. Update integrations to canonical names.
|
||||
> [!IMPORTANT]
|
||||
> Update integrations to canonical names before Phase 7 ships. Aliases work today; they will be removed next major release.
|
||||
|
||||
## Unit policy
|
||||
---
|
||||
|
||||
Every node declares canonical + output units via `UnitPolicy.declare({canonical, output})`. The command registry coerces incoming `msg.unit` to the canonical unit before the handler runs. Outputs are emitted in the declared output unit (often human-friendly).
|
||||
## Payload schemas
|
||||
|
||||
`payloadSchema.type` accepts six values. Source: `.claude/refactor/CONTRACTS.md` §4.
|
||||
|
||||
| Type | Meaning |
|
||||
|:---|:---|
|
||||
| `'string'` | `typeof payload === 'string'` |
|
||||
| `'number'` | `typeof payload === 'number'` |
|
||||
| `'boolean'` | `typeof payload === 'boolean'` |
|
||||
| `'object'` | Non-null object. Optional `properties: { key: 'typeName' }` enforces per-key `typeof` (missing keys allowed) |
|
||||
| `'any'` | Anything passes. Use when handler accepts heterogeneous payloads |
|
||||
| `'none'` | Trigger-only. Handler invoked regardless of payload. If `msg.payload` is anything but `undefined` / `null`, registry logs a `warn` and still invokes the handler |
|
||||
|
||||
---
|
||||
|
||||
## Unit coercion (pre-dispatch)
|
||||
|
||||
A descriptor for a numeric setter or data topic may declare:
|
||||
|
||||
```js
|
||||
{
|
||||
topic: 'set.demand',
|
||||
units: { measure: 'volumeFlowRate', default: 'm3/h' },
|
||||
payloadSchema: { type: 'number' },
|
||||
handler: handlers.setDemand,
|
||||
}
|
||||
```
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
ui[UI message<br/>e.g. 50 m³/h]:::neutral
|
||||
coerce[unit coercion<br/>m³/h → m³/s]:::tier1
|
||||
sc[specificClass<br/>canonical m³/s]:::tier3
|
||||
out[output<br/>renders back to m³/h]:::tier2
|
||||
in["Inbound msg — payload=50, unit='m3/h'"]
|
||||
parse["Extract value+unit — 3 payload shapes accepted"]
|
||||
convert["convert(value).from(unit).to(default)"]
|
||||
handler["Handler receives msg.payload = canonical number, msg.unit = units.default"]
|
||||
|
||||
ui --> coerce --> sc --> out
|
||||
in --> parse --> convert --> handler
|
||||
|
||||
class in,handler neutral
|
||||
class parse,convert step
|
||||
|
||||
classDef neutral fill:#dddddd
|
||||
classDef tier1 fill:#a9daee,color:#000
|
||||
classDef tier3 fill:#50a8d9,color:#000
|
||||
classDef tier2 fill:#86bbdd,color:#000
|
||||
classDef step fill:#a9daee,color:#000
|
||||
```
|
||||
|
||||
### Three accepted payload shapes
|
||||
|
||||
| Shape | Example |
|
||||
|:---|:---|
|
||||
| Plain number | `msg.payload = 50; msg.unit = 'l/s'` |
|
||||
| Object with explicit unit | `msg.payload = { value: 50, unit: 'l/s' }` |
|
||||
| Object without unit (falls back to `msg.unit`) | `msg.payload = { value: 50 }; msg.unit = 'l/s'` |
|
||||
|
||||
### Behaviour on unit mismatch
|
||||
|
||||
| Situation | What the registry does |
|
||||
|:---|:---|
|
||||
| No unit supplied | Silently assume `units.default` |
|
||||
| Unit recognised + correct measure | Convert and rewrite payload |
|
||||
| Unit recognised, wrong measure | Log `warn` with accepted-unit list; fall through |
|
||||
| Unit unrecognised | Log `warn` with accepted-unit list; fall through |
|
||||
|
||||
The handler always sees a plain number in `units.default`. Source: `.claude/refactor/CONTRACTS.md` §4 ("Determine the unit-of-record").
|
||||
|
||||
---
|
||||
|
||||
## S88 colour palette
|
||||
|
||||
Every Mermaid diagram, every Node-RED node editor colour, every FlowFuse dashboard group uses this palette. Source: `.claude/rules/node-red-flow-layout.md` §14.
|
||||
|
||||
| Hex | S88 level | Used by |
|
||||
|:---|:---|:---|
|
||||
| `#0f52a5` | Area | Reserved — not used yet |
|
||||
| `#0c99d9` | Process Cell | pumpingStation |
|
||||
| `#50a8d9` | Unit | MGC, VGC, reactor, settler, monster |
|
||||
| `#86bbdd` | Equipment Module | rotatingMachine, valve, diffuser |
|
||||
| `#a9daee` | Control Module | measurement |
|
||||
| `#dddddd` | Utility / neutral | dashboardAPI, helper functions |
|
||||
|
||||
> [!WARNING]
|
||||
> Known palette outliers (pending cleanup, tracked in `.claude/refactor/OPEN_QUESTIONS.md`):
|
||||
> - `settler` editor colour is `#e4a363` (orange) — should be `#50a8d9`.
|
||||
> - `monster` editor colour is `#4f8582` (teal) — should be `#50a8d9`.
|
||||
> - `diffuser` registers under category `'wbd typical'` instead of `'EVOLV'`.
|
||||
>
|
||||
> Wiki diagrams use the correct S88 colour regardless of the editor mismatch.
|
||||
|
||||
---
|
||||
|
||||
## Measurement key shape
|
||||
|
||||
`MeasurementContainer` stores values under composite keys:
|
||||
|
||||
```
|
||||
<type>.<variant>.<position>.<childId>
|
||||
| | | |
|
||||
| | | +-- child id (or 'default' for internal computations)
|
||||
| | +------------- 'upstream' / 'downstream' / 'atequipment' / ... (always lowercase in keys)
|
||||
| +------------------------ 'measured' / 'predicted' / 'setpoint' / 'min' / 'max'
|
||||
+-------------------------------- 'flow' / 'pressure' / 'power' / 'temperature' / 'level'
|
||||
```
|
||||
|
||||
### Examples
|
||||
|
||||
| Key | Meaning |
|
||||
|:---|:---|
|
||||
| `flow.measured.downstream.dashboard-sim-downstream` | Externally measured downstream flow |
|
||||
| `flow.predicted.downstream.default` | The node's own prediction |
|
||||
| `power.measured.atequipment.default` | Measured power at the equipment |
|
||||
| `pressure.measured.upstream.<childId>` | Pressure from a specific measurement child |
|
||||
|
||||
> [!WARNING]
|
||||
> `position` is always lowercase in keys. The configuration form may use mixed case (`atEquipment`); the container normalises. Don't rely on the casing the form shows you.
|
||||
|
||||
---
|
||||
|
||||
## Status badge
|
||||
|
||||
`statusBadge.compose(state)` returns `{fill, shape, text}` for `node.status(...)`.
|
||||
|
||||
```js
|
||||
const { statusBadge } = require('generalFunctions');
|
||||
|
||||
statusBadge.compose(['OK', `flow=${flow.toFixed(1)} m3/h`])
|
||||
statusBadge.error(message)
|
||||
statusBadge.idle(label)
|
||||
```
|
||||
|
||||
| `fill` | `shape` | Meaning |
|
||||
|:---|:---|:---|
|
||||
| `blue` | `dot` | Normal / running |
|
||||
| `green` | `dot` | Success / running optimally |
|
||||
| `yellow` | `ring` | Degraded — attention needed |
|
||||
| `red` | `ring` | Fault — operator action required |
|
||||
| `grey` | `dot` | Initialising / no data yet |
|
||||
|
||||
> [!IMPORTANT]
|
||||
> Badges live in the domain, not the adapter. `nodeClass` calls `this.source.getStatusBadge()` once per second; the domain owns the shape. Source: `.claude/refactor/CONTRACTS.md` §7.
|
||||
|
||||
---
|
||||
|
||||
## HealthStatus
|
||||
|
||||
A standardised shape for nodes that compute prediction quality, drift, or general health. Source: `.claude/refactor/CONTRACTS.md` §9.
|
||||
|
||||
```json
|
||||
{
|
||||
"level": 1,
|
||||
"flags": ["pressure_init_warming"],
|
||||
"message": "warmup phase",
|
||||
"source": "rotatingMachine#pump-A"
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Type | Meaning |
|
||||
|:---|:---|:---|
|
||||
| `level` | `0 \| 1 \| 2 \| 3` | 0 = fine, 3 = unusable |
|
||||
| `flags` | `string[]` | Machine-readable tags (e.g. `no_pressure_input`) |
|
||||
| `message` | `string` | Single-line human summary |
|
||||
| `source` | `string \| null` | `<nodeType>#<id>` — for routing UI / alarm correlation |
|
||||
|
||||
Helpers compose multiple sub-statuses (flow drift + power drift + pressure init) into one node-level status.
|
||||
|
||||
---
|
||||
|
||||
## Canonical units (used in code)
|
||||
|
||||
Every node declares its `UnitPolicy`: what canonical (internal) unit it uses and what output unit to render to. Source: `.claude/refactor/CONTRACTS.md` §6.
|
||||
|
||||
| Quantity | Canonical (internal) | Common output |
|
||||
|---|---|---|
|
||||
|:---|:---|:---|
|
||||
| Flow | `m3/s` | `m3/h`, `l/s`, `gpm` |
|
||||
| Pressure | `Pa` | `bar`, `mbar`, `kPa` |
|
||||
| Power | `W` | `kW`, `MW` |
|
||||
@@ -82,104 +267,26 @@ flowchart LR
|
||||
| Level | `m` | `m`, `cm` |
|
||||
| Volume | `m3` | `m3`, `l` |
|
||||
|
||||
**Rule:** anywhere in `specificClass`, treat values as canonical. Conversion happens at the boundary (input coercion + output formatting).
|
||||
> [!TIP]
|
||||
> Inside `specificClass`, treat values as canonical. Conversion happens at the boundary: input coercion by the commands registry; output formatting by `outputUtils`.
|
||||
|
||||
## S88 colour palette
|
||||
### Dual access form
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
A[Area<br/>#0f52a5]:::area
|
||||
PC[Process Cell<br/>#0c99d9]:::pc
|
||||
UN[Unit<br/>#50a8d9]:::unit
|
||||
EM[Equipment Module<br/>#86bbdd]:::equip
|
||||
CM[Control Module<br/>#a9daee]:::ctrl
|
||||
UT[Utility / neutral<br/>#dddddd]:::neutral
|
||||
`UnitPolicy` exposes each accessor as both a method and a frozen property bag.
|
||||
|
||||
A --> PC --> UN --> EM --> CM
|
||||
UT -.- A
|
||||
|
||||
classDef area fill:#0f52a5,color:#fff
|
||||
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
|
||||
```js
|
||||
policy.canonical('flow') // 'm3/s' (method form)
|
||||
policy.canonical.flow // 'm3/s' (property form — preferred in hot paths)
|
||||
policy.output.pressure // 'mbar'
|
||||
```
|
||||
|
||||
| Hex | S88 level | Used by |
|
||||
|---|---|---|
|
||||
| `#0f52a5` | Area | (reserved — not in use yet) |
|
||||
| `#0c99d9` | Process Cell | pumpingStation |
|
||||
| `#50a8d9` | Unit | machineGroupControl, valveGroupControl, reactor, settler, monster |
|
||||
| `#86bbdd` | Equipment Module | rotatingMachine, valve, diffuser |
|
||||
| `#a9daee` | Control Module | measurement |
|
||||
| `#dddddd` | Utility / neutral | dashboardAPI, helper function nodes |
|
||||
|
||||
**Rule:** every Mermaid diagram in this wiki, every Node-RED node's editor colour, and every dashboard grouping uses this palette. Source of truth: `.claude/rules/node-red-flow-layout.md` §14.
|
||||
|
||||
**Known outliers** (pending cleanup, tracked in OPEN_QUESTIONS.md):
|
||||
- `settler` editor colour is `#e4a363` (orange) — should be `#50a8d9`.
|
||||
- `monster` editor colour is `#4f8582` (teal) — should be `#50a8d9`.
|
||||
- `diffuser` editor colour was missing pre-refactor; now `#86bbdd`.
|
||||
- `dashboardAPI` registers under category `'wbd typical'` instead of `'EVOLV'`.
|
||||
|
||||
## Measurement key shape
|
||||
|
||||
The `MeasurementContainer` stores values under composite keys:
|
||||
|
||||
```
|
||||
<type>.<variant>.<position>.<childId>
|
||||
```
|
||||
|
||||
| Segment | Examples |
|
||||
|---|---|
|
||||
| `type` | `flow`, `pressure`, `power`, `temperature`, `level` |
|
||||
| `variant` | `measured`, `predicted`, `setpoint`, `min`, `max` |
|
||||
| `position` | `upstream`, `downstream`, `atequipment`, `inlet`, `outlet` (always lowercase in keys) |
|
||||
| `childId` | The registering child's id, OR `default` for internal computations |
|
||||
|
||||
Examples:
|
||||
- `flow.measured.downstream.dashboard-sim-downstream` — externally measured downstream flow.
|
||||
- `flow.predicted.downstream.default` — node's own prediction.
|
||||
- `power.measured.atequipment.default` — measured power at the equipment.
|
||||
- `pressure.measured.upstream.<childId>` — pressure from a specific measurement child.
|
||||
|
||||
**Gotcha:** `position` is **always lowercase in keys**. The configuration form may use mixed case (`atEquipment`); the container normalises.
|
||||
|
||||
## Status badge
|
||||
|
||||
`statusBadge.compose(state)` returns `{fill, shape, text}` for `node.status(...)`:
|
||||
|
||||
| level | shape | fill | meaning |
|
||||
|---|---|---|---|
|
||||
| `info` | dot | blue | normal operation |
|
||||
| `success` | dot | green | success / running optimally |
|
||||
| `warning` | ring | yellow | degraded, attention needed |
|
||||
| `error` | ring | red | fault, operator action required |
|
||||
| `pending` | dot | grey | initialising / no data yet |
|
||||
|
||||
Composer reads from `HealthStatus.level` (0–3) — kept centralised so all nodes show consistent badges.
|
||||
|
||||
## HealthStatus shape
|
||||
|
||||
```json
|
||||
{
|
||||
"level": 0,
|
||||
"flags": ["pressure_init_warming"],
|
||||
"message": "warmup phase",
|
||||
"source": "rotatingMachine#pump-A"
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Range | Meaning |
|
||||
|---|---|---|
|
||||
| `level` | 0..3 | 0 = healthy, 1 = degraded, 2 = warning, 3 = error |
|
||||
| `flags` | string[] | Machine-readable reason codes. |
|
||||
| `message` | string | Human-readable summary (one line). |
|
||||
| `source` | string | `<nodeType>#<id>` — for routing UI / alarm correlation. |
|
||||
---
|
||||
|
||||
## Related pages
|
||||
|
||||
- [Architecture](Architecture) — generalFunctions API surface
|
||||
- [Telemetry](Telemetry) — Port-1 InfluxDB schema (where these conventions appear in stored data)
|
||||
- [Topology-Patterns](Topology-Patterns) — what topics flow where
|
||||
| Page | Why |
|
||||
|:---|:---|
|
||||
| [Architecture](Architecture) | Where these conventions are implemented in code |
|
||||
| [Telemetry](Telemetry) | What these keys look like in InfluxDB |
|
||||
| [Topology Patterns](Topology-Patterns) | Which topics flow between which nodes |
|
||||
| [Glossary](Glossary) | Domain terms used here |
|
||||
|
||||
Reference in New Issue
Block a user