P9.3: wiki/Home.md following 14-section visual-first template + wiki:* scripts
Auto-generated topic-contract + data-model sections via shared wikiGen script. Hand-written Mermaid diagrams for position-in-platform, code map, child registration, lifecycle, configuration, state chart (where applicable). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -4,7 +4,10 @@
|
|||||||
"description": "Control module valve",
|
"description": "Control module valve",
|
||||||
"main": "valve.js",
|
"main": "valve.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "node --test test/basic/*.test.js test/integration/*.test.js test/edge/*.test.js"
|
"test": "node --test test/basic/*.test.js test/integration/*.test.js test/edge/*.test.js",
|
||||||
|
"wiki:contract": "node ../generalFunctions/scripts/wikiGen.js contract ./src/commands/index.js --write ./wiki/Home.md",
|
||||||
|
"wiki:datamodel": "node ../generalFunctions/scripts/wikiGen.js datamodel ./src/specificClass.js --write ./wiki/Home.md",
|
||||||
|
"wiki:all": "npm run wiki:contract && npm run wiki:datamodel"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
|||||||
259
wiki/Home.md
Normal file
259
wiki/Home.md
Normal file
@@ -0,0 +1,259 @@
|
|||||||
|
# valve
|
||||||
|
|
||||||
|
> **Reflects code as of `e27135b` · 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
|
||||||
|
|
||||||
|
**valve** models a single throttling valve. It loads a supplier characteristic curve (Kv-vs-position), drives an FSM-style move sequence for opening/closing, and recomputes pressure drop across the valve from current flow + Kv. Used standalone or as a child of `valveGroupControl` when grouped.
|
||||||
|
|
||||||
|
## 2. Position in the platform
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
vgc[valveGroupControl<br/>Unit]:::unit -->|set.position| this[valve<br/>Equipment]:::equip
|
||||||
|
src[machine / MGC / PS<br/>upstream source]:::unit -->|child.register| this
|
||||||
|
meas[measurement<br/>type=pressure / flow]:::ctrl -.data.-> this
|
||||||
|
this -->|child.register| vgc
|
||||||
|
this -->|evt.deltaPChange| vgc
|
||||||
|
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 |
|
||||||
|
|---|---|---|
|
||||||
|
| Predicts deltaP from flow + Kv | ✅ | Hydraulic model picks liquid vs gas formula per `serviceType`. |
|
||||||
|
| Loads supplier curve by model name | ✅ | `asset.model` resolved through `loadModel`; inline curve override supported. |
|
||||||
|
| Position move FSM | ✅ | `opening` / `closing` states with interruptible setpoints. |
|
||||||
|
| Startup / shutdown sequences | ✅ | Pre-shutdown ramps to position 0 when operational. |
|
||||||
|
| Emergency-stop sequence | ✅ | Aliased `cmd.estop` → state-machine `emergencystop`. |
|
||||||
|
| Fluid-contract aggregation | ✅ | Tracks upstream service type via registered sources. |
|
||||||
|
| Gas-choke detection | ⚠️ | Capped at `gasChokedRatioLimit`; surfaced in `hydraulicDiagnostics`. |
|
||||||
|
| Multi-parent registration | ⚠️ | Allowed but not exercised in production tests. |
|
||||||
|
|
||||||
|
## 4. Code map
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TB
|
||||||
|
subgraph nodeRED["nodeClass.js — adapter (BaseNodeAdapter)"]
|
||||||
|
nc["buildDomainConfig()<br/>static DomainClass, commands"]
|
||||||
|
end
|
||||||
|
subgraph domain["specificClass.js — orchestrator (BaseDomain)"]
|
||||||
|
sc["Valve.configure()<br/>wires concern modules<br/>installs FluidCompatibility registerChild"]
|
||||||
|
end
|
||||||
|
subgraph concerns["src/ concern modules"]
|
||||||
|
state["state/<br/>stateBindings → positionChange"]
|
||||||
|
fluid["fluid/<br/>FluidCompatibility"]
|
||||||
|
curve["curve/<br/>SupplierCurvePredictor"]
|
||||||
|
meas["measurement/<br/>MeasurementRouter + FORMULA_UNITS"]
|
||||||
|
flow["flow/<br/>FlowController (setpoint, sequences)"]
|
||||||
|
io["io/<br/>buildOutput + buildStatusBadge"]
|
||||||
|
hyd["hydraulicModel.js<br/>ValveHydraulicModel"]
|
||||||
|
end
|
||||||
|
nc --> sc
|
||||||
|
sc --> state
|
||||||
|
sc --> fluid
|
||||||
|
sc --> curve
|
||||||
|
sc --> meas
|
||||||
|
sc --> flow
|
||||||
|
sc --> io
|
||||||
|
sc --> hyd
|
||||||
|
```
|
||||||
|
|
||||||
|
| Module | Owns | Read first if you're changing… |
|
||||||
|
|---|---|---|
|
||||||
|
| `state/` | Bindings from state-machine `positionChange` → `updatePosition()` | Move-finished triggers. |
|
||||||
|
| `fluid/` | Service-type compatibility, contract aggregation | Gas-vs-liquid mismatch warnings. |
|
||||||
|
| `curve/` | Supplier Kv curve load + interpolation | Curve fitting, model selection. |
|
||||||
|
| `measurement/` | Pressure/flow routing + deltaP recompute | What triggers a recalc. |
|
||||||
|
| `flow/` | Sequence + setpoint execution | Startup / shutdown / move semantics. |
|
||||||
|
| `io/` | Port-0 output shape + status badge | What lands on the wire each tick. |
|
||||||
|
|
||||||
|
## 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` | Replaces the named state value with the supplied payload. |
|
||||||
|
| `cmd.startup` | _(none)_ | `any` | Triggers an action / sequence — not idempotent. |
|
||||||
|
| `cmd.shutdown` | _(none)_ | `any` | Triggers an action / sequence — not idempotent. |
|
||||||
|
| `cmd.estop` | `emergencystop`, `emergencyStop` | `any` | Triggers an action / sequence — not idempotent. |
|
||||||
|
| `execSequence` | _(none)_ | `object` | _(see handler)_ |
|
||||||
|
| `set.position` | `execMovement` | `object` | Replaces the named state value with the supplied payload. |
|
||||||
|
| `data.flow` | `updateFlow` | `object` | Pushes a value into the node's measurement stream. |
|
||||||
|
| `query.curve` | `showcurve` | `any` | Read-only query; node replies on the same msg. |
|
||||||
|
| `child.register` | `registerChild` | `string` | Parent/child plumbing — registers or unregisters a child node. |
|
||||||
|
|
||||||
|
<!-- END AUTOGEN: topic-contract -->
|
||||||
|
|
||||||
|
## 6. Child registration
|
||||||
|
|
||||||
|
valve overrides BaseDomain's default `registerChild` with `FluidCompatibility.registerChild` so upstream-source contracts feed the fluid aggregator. Measurement children attach through the generic measurement handshake.
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
subgraph kids["accepted children (softwareType)"]
|
||||||
|
src["machine / rotatingmachine /<br/>machinegroup / pumpingstation /<br/>valvegroupcontrol"]:::unit
|
||||||
|
m["measurement"]:::ctrl
|
||||||
|
end
|
||||||
|
src -->|getFluidContract| fluid[FluidCompatibility<br/>aggregates serviceType]
|
||||||
|
m -->|"<type>.measured.<position>"| router[MeasurementRouter<br/>updatePressure / updateFlow]
|
||||||
|
router --> deltaP[updateDeltaP<br/>writes pressure.predicted.delta]
|
||||||
|
fluid --> evt1[evt.fluidCompatibilityChange]
|
||||||
|
deltaP --> evt2[evt.deltaPChange]
|
||||||
|
classDef unit fill:#50a8d9,color:#000
|
||||||
|
classDef ctrl fill:#a9daee,color:#000
|
||||||
|
```
|
||||||
|
|
||||||
|
| softwareType | onRegister side-effect | Subscribed events |
|
||||||
|
|---|---|---|
|
||||||
|
| `machine` / `rotatingmachine` | Stored as upstream source; reads `getFluidContract()` or default `liquid`. | `fluidContractChange`. |
|
||||||
|
| `machinegroup` / `machinegroupcontrol` | Same; recomputes aggregate service type. | `fluidContractChange`. |
|
||||||
|
| `pumpingstation` | Same. | `fluidContractChange`. |
|
||||||
|
| `valvegroupcontrol` | Same. | `fluidContractChange`. |
|
||||||
|
| `measurement` | Routed via measurement handshake; values land in MeasurementContainer. | `<type>.measured.<position>`. |
|
||||||
|
|
||||||
|
## 7. Lifecycle — what one event does
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
participant parent as valveGroupControl
|
||||||
|
participant valve as valve
|
||||||
|
participant state as state FSM
|
||||||
|
participant hyd as hydraulicModel
|
||||||
|
participant out as Port-0
|
||||||
|
|
||||||
|
parent->>valve: set.position { setpoint: 60 }
|
||||||
|
valve->>state: moveTo(60)
|
||||||
|
state-->>valve: positionChange ticks
|
||||||
|
valve->>valve: predictKv(position)
|
||||||
|
valve->>hyd: calculateDeltaPMbar(q, kv, downP, rho, T)
|
||||||
|
hyd-->>valve: { deltaPMbar, details }
|
||||||
|
valve->>valve: write pressure.predicted.delta
|
||||||
|
valve->>parent: emitter.emit('deltaPChange', deltaP)
|
||||||
|
valve->>out: msg{topic, payload (delta-compressed)}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 8. Data model — `getOutput()`
|
||||||
|
|
||||||
|
What lands on Port 0. Composed in `io/output.buildOutput`, then delta-compressed by `outputUtils.formatMsg`.
|
||||||
|
|
||||||
|
<!-- BEGIN AUTOGEN: data-model -->
|
||||||
|
|
||||||
|
| Key | Type | Unit | Sample |
|
||||||
|
|---|---|---|---|
|
||||||
|
| `state` | string | — | `"operational"` |
|
||||||
|
| `percentageOpen` | number | % | `0` |
|
||||||
|
| `moveTimeleft` | number | s | `0` |
|
||||||
|
| `mode` | string | — | `"auto"` |
|
||||||
|
| `downstream_predicted_flow` | number | m3/h | `0` |
|
||||||
|
| `downstream_measured_flow` | number | m3/h | _(emitted when measurement child present)_ |
|
||||||
|
| `downstream_predicted_pressure` | number | mbar | _(emitted when upstream pressure present)_ |
|
||||||
|
| `downstream_measured_pressure` | number | mbar | _(emitted when measurement child present)_ |
|
||||||
|
| `delta_predicted_pressure` | number | mbar | `0` |
|
||||||
|
|
||||||
|
<!-- END AUTOGEN: data-model -->
|
||||||
|
|
||||||
|
Measurement-derived keys follow the legacy `<position>_<variant>_<type>` shape (e.g. `downstream_predicted_flow`, `delta_predicted_pressure`) and are emitted only when the container holds a finite value.
|
||||||
|
|
||||||
|
## 9. Configuration — editor form ↔ config keys
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TB
|
||||||
|
subgraph editor["Node-RED editor form"]
|
||||||
|
f1[Mode]
|
||||||
|
f2[Asset model]
|
||||||
|
f3[Service type]
|
||||||
|
f4[Diameter]
|
||||||
|
f5[Fluid density / temperature]
|
||||||
|
f6[Inline valveCurve override]
|
||||||
|
end
|
||||||
|
subgraph config["Domain config slice"]
|
||||||
|
c1[mode.current]
|
||||||
|
c2[asset.model]
|
||||||
|
c3[asset.serviceType]
|
||||||
|
c4[asset.valveDiameter]
|
||||||
|
c5[asset.fluidDensity / fluidTemperatureK]
|
||||||
|
c6[asset.valveCurve]
|
||||||
|
end
|
||||||
|
f1 --> c1
|
||||||
|
f2 --> c2
|
||||||
|
f3 --> c3
|
||||||
|
f4 --> c4
|
||||||
|
f5 --> c5
|
||||||
|
f6 --> c6
|
||||||
|
```
|
||||||
|
|
||||||
|
| Form field | Config key | Default | Range | Where used |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| Mode | `mode.current` | per schema | enum | `setMode`, `flowController` |
|
||||||
|
| Asset model | `asset.model` | `null` | string | `SupplierCurvePredictor` |
|
||||||
|
| Service type | `asset.serviceType` | per asset | `gas` / `liquid` | `ValveHydraulicModel` |
|
||||||
|
| Diameter | `asset.valveDiameter` | per asset | > 0 (m) | curve key selection |
|
||||||
|
| Fluid density | `asset.fluidDensity` | model default | > 0 (kg/m³) | hydraulic formula |
|
||||||
|
| Fluid temperature | `asset.fluidTemperatureK` | model default | > 0 (K) | hydraulic formula |
|
||||||
|
| Choked-flow cap | `asset.gasChokedRatioLimit` | per asset | 0–1 | gas formula clamp |
|
||||||
|
|
||||||
|
## 10. State chart
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
stateDiagram-v2
|
||||||
|
[*] --> off
|
||||||
|
off --> idle: cmd.startup
|
||||||
|
idle --> opening: set.position > 0
|
||||||
|
opening --> operational: position reached
|
||||||
|
operational --> opening: set.position changed
|
||||||
|
operational --> closing: set.position < current
|
||||||
|
closing --> closed: position == 0
|
||||||
|
closed --> opening: set.position > 0
|
||||||
|
operational --> stopping: cmd.shutdown (ramps to 0)
|
||||||
|
stopping --> idle: cooldown elapsed
|
||||||
|
operational --> emergencystop: cmd.estop
|
||||||
|
emergencystop --> off: cmd.reset
|
||||||
|
```
|
||||||
|
|
||||||
|
The `opening` / `closing` states cover the move-in-progress window; `positionChange` ticks fire until the setpoint is reached, then the FSM lands on `operational`. Pre-shutdown ramp to 0 is enforced by `FlowController.executeSequence('shutdown')`.
|
||||||
|
|
||||||
|
## 11. Examples
|
||||||
|
|
||||||
|
| Tier | File | What it shows | Mandatory? |
|
||||||
|
|---|---|---|---|
|
||||||
|
| Basic | `examples/01-Basic.flow.json` | Inject `set.position` + dashboard, no parent | ✅ |
|
||||||
|
| Integration | `examples/02-Integration.flow.json` | valve + VGC + upstream source | ✅ |
|
||||||
|
| Dashboard | `examples/03-Dashboard.flow.json` | Live FlowFuse charts (position, ΔP, flow) | ⭕ |
|
||||||
|
|
||||||
|
Screenshots under `wiki/_partial-screenshots/valve/` when produced. Docker compose snippet under `examples/README.md`.
|
||||||
|
|
||||||
|
## 12. Debug recipes
|
||||||
|
|
||||||
|
| Symptom | First thing to check | Where to look |
|
||||||
|
|---|---|---|
|
||||||
|
| Status badge shows `⚠ no input` | Did any pressure / flow measurement register? Watch Port 2. | Editor debug tap on Port 2 |
|
||||||
|
| `delta_predicted_pressure` stuck at zero | Is `kv > 0`? FSM may be in `off` / `closed`. | `state.getCurrentState()` |
|
||||||
|
| Gas mismatch warning on status badge | `fluidCompatibility.status` is `mismatch` / `conflict`. | `getFluidCompatibility()` |
|
||||||
|
| `query.curve` returns empty curve | Asset model not found by `loadModel`; fallback to `config.asset.valveCurve`. | `SupplierCurvePredictor.snapshot()` |
|
||||||
|
| deltaP non-finite | Downstream gauge pressure absolute term ≤ 0, or choked ratio reached. | `hydraulicDiagnostics` |
|
||||||
|
|
||||||
|
> 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
|
||||||
|
|
||||||
|
- Use valve for a **throttling element** with a known Kv curve. For a fixed-restriction orifice with no actuator, model the deltaP externally.
|
||||||
|
- Don't use valve to model a non-return / check valve — no position control or curve fitting is exposed.
|
||||||
|
- Skip valve when an upstream source provides flow directly and no pressure-drop estimate is needed; just wire the source straight to the parent.
|
||||||
|
|
||||||
|
## 14. Known limitations / current issues
|
||||||
|
|
||||||
|
| # | Issue | Tracked in |
|
||||||
|
|---|---|---|
|
||||||
|
| 1 | Gas-choke detection is a hard cap, not a smooth transition — chart traces show a step at the choked-ratio limit. | `hydraulicModel.js` |
|
||||||
|
| 2 | Multi-parent registration is allowed but not exercised in production tests. | CONTRACT.md `## Children registered by this node` |
|
||||||
|
| 3 | `set.position` move sequences are interruptible but tests cover happy-path only. | P10 test-suite refactor |
|
||||||
11
wiki/_partial-datamodel.md.template
Normal file
11
wiki/_partial-datamodel.md.template
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
| Key | Type | Unit | Sample |
|
||||||
|
|---|---|---|---|
|
||||||
|
| `state` | string | — | `"operational"` |
|
||||||
|
| `percentageOpen` | number | % | `0` |
|
||||||
|
| `moveTimeleft` | number | s | `0` |
|
||||||
|
| `mode` | string | — | `"auto"` |
|
||||||
|
| `downstream_predicted_flow` | number | m3/h | `0` |
|
||||||
|
| `downstream_measured_flow` | number | m3/h | _(emitted when measurement child present)_ |
|
||||||
|
| `downstream_predicted_pressure` | number | mbar | _(emitted when upstream pressure present)_ |
|
||||||
|
| `downstream_measured_pressure` | number | mbar | _(emitted when measurement child present)_ |
|
||||||
|
| `delta_predicted_pressure` | number | mbar | `0` |
|
||||||
Reference in New Issue
Block a user