wiki: rewrite Home.md per visual-first 14-section template
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
27
wiki/Home.md
27
wiki/Home.md
@@ -1,6 +1,6 @@
|
|||||||
# machineGroupControl
|
# machineGroupControl
|
||||||
|
|
||||||
> **Reflects code as of `afc304b` · regenerated `2026-05-11` via `npm run wiki:all`**
|
> **Reflects code as of `7d19fc1` · 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.
|
> If this banner is stale, the page may be out of date. Treat as informative, not authoritative.
|
||||||
|
|
||||||
## 1. What this node is
|
## 1. What this node is
|
||||||
@@ -41,7 +41,7 @@ S88 colours: Process Cell `#0c99d9`, Unit `#50a8d9`, Equipment `#86bbdd`, Contro
|
|||||||
| Optimal control | ✅ | `mode='optimalcontrol'`. |
|
| Optimal control | ✅ | `mode='optimalcontrol'`. |
|
||||||
| Group efficiency + BEP distance | ✅ | `GroupEfficiency`. |
|
| Group efficiency + BEP distance | ✅ | `GroupEfficiency`. |
|
||||||
| Header-pressure equalisation | ✅ | `operatingPoint.equalize()`. |
|
| Header-pressure equalisation | ✅ | `operatingPoint.equalize()`. |
|
||||||
| Demand serialisation (latest-wins) | ✅ | Inline gate; deferred call drains on completion. |
|
| Demand serialisation (latest-wins) | ✅ | `DemandDispatcher` / `LatestWinsGate.fireAndWait`. |
|
||||||
| Forced shutdown on `Qd ≤ 0` | ✅ | `turnOffAllMachines()`. |
|
| Forced shutdown on `Qd ≤ 0` | ✅ | `turnOffAllMachines()`. |
|
||||||
|
|
||||||
## 4. Code map
|
## 4. Code map
|
||||||
@@ -60,7 +60,8 @@ flowchart TB
|
|||||||
combi["combinatorics/<br/>validPumpCombinations"]
|
combi["combinatorics/<br/>validPumpCombinations"]
|
||||||
opt["optimizer/<br/>BEP-Grav / NCog selectors"]
|
opt["optimizer/<br/>BEP-Grav / NCog selectors"]
|
||||||
efficiency["efficiency/<br/>GroupEfficiency + BEP dist"]
|
efficiency["efficiency/<br/>GroupEfficiency + BEP dist"]
|
||||||
dispatch["control/<br/>strategies (equalFlow / prioPct)"]
|
ctrl["control/<br/>strategies (equalFlow / prioPct)"]
|
||||||
|
dispatch["dispatch/<br/>DemandDispatcher (LatestWinsGate)"]
|
||||||
io["io/<br/>output + status"]
|
io["io/<br/>output + status"]
|
||||||
commands["commands/<br/>topic registry + handlers"]
|
commands["commands/<br/>topic registry + handlers"]
|
||||||
end
|
end
|
||||||
@@ -70,6 +71,7 @@ flowchart TB
|
|||||||
sc --> combi
|
sc --> combi
|
||||||
sc --> opt
|
sc --> opt
|
||||||
sc --> efficiency
|
sc --> efficiency
|
||||||
|
sc --> ctrl
|
||||||
sc --> dispatch
|
sc --> dispatch
|
||||||
sc --> io
|
sc --> io
|
||||||
nc --> commands
|
nc --> commands
|
||||||
@@ -83,7 +85,7 @@ flowchart TB
|
|||||||
| `optimizer/` | Best-combination selectors | Optimiser selection method, scoring math. |
|
| `optimizer/` | Best-combination selectors | Optimiser selection method, scoring math. |
|
||||||
| `efficiency/` | Group efficiency, BEP distance | BEP gravitation tuning, peak math. |
|
| `efficiency/` | Group efficiency, BEP distance | BEP gravitation tuning, peak math. |
|
||||||
| `control/strategies.js` | Per-mode dispatch (priority, prioPct) | Mode behaviour, priorityList usage. |
|
| `control/strategies.js` | Per-mode dispatch (priority, prioPct) | Mode behaviour, priorityList usage. |
|
||||||
| `dispatch/` | Demand fan-out helpers (legacy alongside inline gate) | Serialisation, mid-flight overrides. |
|
| `dispatch/` | `DemandDispatcher` wrapping `LatestWinsGate.fireAndWait` | Demand serialisation, mid-flight overrides. |
|
||||||
| `commands/` | Input-topic registry and handlers | New input topics, payload validation. |
|
| `commands/` | Input-topic registry and handlers | New input topics, payload validation. |
|
||||||
| `io/` | `getOutput`, `getStatusBadge` | Output shape, dashboard badge. |
|
| `io/` | `getOutput`, `getStatusBadge` | Output shape, dashboard badge. |
|
||||||
|
|
||||||
@@ -175,16 +177,17 @@ What lands on Port 0. Composed in `io/output.js → getOutput(this)` and delta-c
|
|||||||
{
|
{
|
||||||
"mode": "optimalcontrol",
|
"mode": "optimalcontrol",
|
||||||
"scaling": "normalized",
|
"scaling": "normalized",
|
||||||
"flow.predicted.atequipment.<nodeId>": 0.0125,
|
"atEquipment_predicted_flow": 42.5,
|
||||||
"flow.predicted.downstream.<nodeId>": 0.0125,
|
"downstream_predicted_flow": 42.5,
|
||||||
"power.predicted.atequipment.<nodeId>": 1800,
|
"atEquipment_predicted_power": 18.0,
|
||||||
"efficiency.predicted.atequipment.<nodeId>": 0.65,
|
"atEquipment_predicted_efficiency": 0.65,
|
||||||
|
"atEquipment_predicted_Ncog": 1.23,
|
||||||
"absDistFromPeak": 0.02,
|
"absDistFromPeak": 0.02,
|
||||||
"relDistFromPeak": 0.10
|
"relDistFromPeak": 0.10
|
||||||
}
|
}
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
The `<nodeId>` segment is the Node-RED node id assigned at deploy time.
|
Key format from `io/output.js`: `<position>_<variant>_<type>` (e.g. `atEquipment_predicted_flow`). Output units: flow in `m3/h`, power in `kW`, pressure in `mbar`.
|
||||||
|
|
||||||
## 9. Configuration — editor form ↔ config keys
|
## 9. Configuration — editor form ↔ config keys
|
||||||
|
|
||||||
@@ -236,7 +239,7 @@ stateDiagram-v2
|
|||||||
turning_off --> idle_disp: all machines acknowledged shutdown
|
turning_off --> idle_disp: all machines acknowledged shutdown
|
||||||
```
|
```
|
||||||
|
|
||||||
While `dispatching`, additional `handleInput` calls overwrite `_delayedCall` (latest-wins); the gate drains the latest one on completion. `turnOffAllMachines()` clears `_delayedCall` to make turn-off the final intent.
|
While `dispatching`, additional `handleInput` calls are absorbed by `DemandDispatcher` (latest-wins). A superseded call resolves with `{ superseded: true }`. `turnOffAllMachines()` calls `cancelPending()` so turn-off is always the final intent.
|
||||||
|
|
||||||
## 11. Examples
|
## 11. Examples
|
||||||
|
|
||||||
@@ -255,7 +258,7 @@ Tier 1/2/3 visual-first example flows are still TODO (see `MEMORY.md` "TODO: Exa
|
|||||||
| No combination selected | Demand outside `[dynamicTotals.flow.min, max]` — clamped on entry; `_optimalControl` returns early if combinations empty. | `validPumpCombinations` + warn log. |
|
| No combination selected | Demand outside `[dynamicTotals.flow.min, max]` — clamped on entry; `_optimalControl` returns early if combinations empty. | `validPumpCombinations` + warn log. |
|
||||||
| Group flow stuck at zero | Machines never reach an `ACTIVE_STATE` — check per-pump startup logs. | `isMachineActive`. |
|
| Group flow stuck at zero | Machines never reach an `ACTIVE_STATE` — check per-pump startup logs. | `isMachineActive`. |
|
||||||
| Priority-percentage mode warns and exits | Mode requires `scaling='normalized'`. Set both. | `_runDispatch` switch. |
|
| Priority-percentage mode warns and exits | Mode requires `scaling='normalized'`. Set both. | `_runDispatch` switch. |
|
||||||
| Stale flow setpoints on chained calls | Dispatch gate may have collapsed multiple calls — confirm `_delayedCall` was honoured. | `handleInput` finally block. |
|
| Stale flow setpoints on chained calls | Dispatch gate may have superseded intermediate calls — callers should check `result.superseded`. | `DemandDispatcher` / `LatestWinsGate`. |
|
||||||
| Header pressure not equalising | Pressure children must register with `asset.type='pressure'` and a matching position. | `operatingPoint.equalize`. |
|
| Header pressure not equalising | Pressure children must register with `asset.type='pressure'` and a matching position. | `operatingPoint.equalize`. |
|
||||||
| Optimiser picks unexpected combo | Verify `optimization.method` and per-method scoring (NCog vs BEP-Grav). | `optimizer/`. |
|
| Optimiser picks unexpected combo | Verify `optimization.method` and per-method scoring (NCog vs BEP-Grav). | `optimizer/`. |
|
||||||
|
|
||||||
@@ -275,3 +278,5 @@ Tier 1/2/3 visual-first example flows are still TODO (see `MEMORY.md` "TODO: Exa
|
|||||||
| 2 | Mid-flight setpoint overrides on `accelerating` / `decelerating` rely on `abortActiveMovements` per dispatch — a sequence with no awaitable `abortMovement` will warn but proceed. | `abortActiveMovements`. |
|
| 2 | Mid-flight setpoint overrides on `accelerating` / `decelerating` rely on `abortActiveMovements` per dispatch — a sequence with no awaitable `abortMovement` will warn but proceed. | `abortActiveMovements`. |
|
||||||
| 3 | Power-cap parameter exposed but not surfaced as a topic input — only programmatic via `handleInput(source, demand, powerCap)`. | `commands/index.js` — no canonical topic. |
|
| 3 | Power-cap parameter exposed but not surfaced as a topic input — only programmatic via `handleInput(source, demand, powerCap)`. | `commands/index.js` — no canonical topic. |
|
||||||
| 4 | Tier 1/2/3 visual-first example flows not yet written. | P9 follow-up. |
|
| 4 | Tier 1/2/3 visual-first example flows not yet written. | P9 follow-up. |
|
||||||
|
| 5 | **`maxEfficiency` naming bug** — `GroupEfficiency.calcGroupEfficiency` returns `{ maxEfficiency, lowestEfficiency }` but `maxEfficiency` is actually the **mean cog** across all machines (not the maximum). The name is deliberately preserved for behavioural parity; callers using it as "the peak" will over-estimate the BEP target. | `efficiency/groupEfficiency.js` comment + `OPEN_QUESTIONS.md` 2026-05-10. |
|
||||||
|
| 6 | **`calcAbsoluteTotals` implicit pressure-key coupling** — iterates `machine.predictFlow.inputCurve` and re-uses the same pressure key to index `machine.predictPower.inputCurve[pressure]`. If the two curves were sampled at different pressures the lookup is `undefined` and the call throws. Enforcement or defensive skip deferred to P5 (rotatingMachine curveLoader). | `totals/totalsCalculator.js` + `OPEN_QUESTIONS.md` 2026-05-10. |
|
||||||
|
|||||||
Reference in New Issue
Block a user