docs(wiki): full 5-page wiki matching the rotatingMachine reference format
Replaces the prior stub/partial wiki with a Home + Reference-{Architecture,
Contracts,Examples,Limitations} + _Sidebar structure. Topic-contract and
data-model sections wrapped in AUTOGEN markers for the future wiki-gen tool.
Source-vs-spec contradictions surfaced and flagged inline (not silently
fixed). Pending-review notes mark sections that need a full node review.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
379
wiki/Home.md
379
wiki/Home.md
@@ -1,313 +1,134 @@
|
||||
# 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
|
||||
A `valve` models a single actuated throttling valve. It loads a supplier Kv-vs-position characteristic curve, drives a position FSM (`accelerating` / `decelerating` through `operational`), and recomputes pressure drop from flow + Kv via a hydraulic model that picks a liquid or gas formula by `serviceType`. Used standalone, or as a child of `valveGroupControl`, downstream of a `rotatingMachine` / `machineGroupControl` / `pumpingStation`.
|
||||
|
||||
**valve** models a single actuated throttling valve at the S88 Equipment Module level. It loads a supplier Kv-vs-position characteristic curve, drives an FSM for open/close move sequences (using `accelerating`/`decelerating` states shared with `rotatingMachine`), and recomputes pressure drop from flow + Kv via a hydraulic model. Used standalone or as a child of `valveGroupControl`.
|
||||
> [!NOTE]
|
||||
> Pending full node review (2026-05). Content reflects `CONTRACT.md` and current source only.
|
||||
|
||||
## 2. Position in the platform
|
||||
---
|
||||
|
||||
## At a glance
|
||||
|
||||
| Thing | Value |
|
||||
|:---|:---|
|
||||
| What it represents | One actuated throttling valve — supplier Kv curve, position FSM, deltaP estimate |
|
||||
| S88 level | Equipment Module |
|
||||
| Use it when | You need a position-controlled valve whose deltaP depends on flow, Kv(position), and service-type (gas / liquid) |
|
||||
| Don't use it for | Fixed-restriction orifices, non-return / check valves, or curveless throttling devices (no fallback model) |
|
||||
| Children it accepts | Upstream sources (`rotatingmachine`, `machinegroup` / `machinegroupcontrol`, `pumpingstation`, `valvegroupcontrol`) for fluid-contract tracking; `measurement` for pressure / flow |
|
||||
| Parents it talks to | `valveGroupControl` (typical) or any node that issues `set.position` / `cmd.startup` / `cmd.shutdown` |
|
||||
|
||||
---
|
||||
|
||||
## How it fits
|
||||
|
||||
```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 Port 2| vgc
|
||||
this -->|evt.deltaPChange| vgc
|
||||
this -->|evt.fluidCompatibilityChange| vgc
|
||||
parent[valveGroupControl]:::unit -->|set.position<br/>cmd.startup / shutdown| v[valve<br/>Equipment]:::equip
|
||||
src["rotatingMachine /<br/>MGC / pumpingStation"]:::unit -->|child.register<br/>(fluid contract)| v
|
||||
m_p[measurement<br/>pressure]:::ctrl -.measured.-> v
|
||||
m_f[measurement<br/>flow]:::ctrl -.measured.-> v
|
||||
v -->|child.register| parent
|
||||
v -.->|evt.deltaPChange<br/>evt.fluidCompatibilityChange<br/>evt.fluidContractChange| parent
|
||||
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: `.claude/rules/node-red-flow-layout.md`.
|
||||
S88 colours are anchored in `.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 `valveCurve` override supported. |
|
||||
| Position move FSM | ✅ | `accelerating` / `decelerating` states with interruptible setpoints; `moveTo` uses shared state machine. |
|
||||
| Startup / shutdown sequences | ✅ | Pre-shutdown ramps valve to position 0 before executing stop sequence. |
|
||||
| Emergency-stop sequence | ✅ | `cmd.estop` → `emergencystop → off` sequence. |
|
||||
| Fluid-contract aggregation | ✅ | Tracks upstream service type via registered sources through `FluidCompatibility`. |
|
||||
| Gas-choke detection | ⚠️ | Hard cap at `gasChokedRatioLimit`; surfaced in `hydraulicDiagnostics`. |
|
||||
| Multi-parent registration | ⚠️ | Allowed but not exercised in production tests. |
|
||||
## Try it — 3-minute demo
|
||||
|
||||
## 4. Code map
|
||||
Import the basic example flow, deploy, and drive a single valve through a position move.
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
subgraph nodeRED["nodeClass.js — adapter (BaseNodeAdapter)"]
|
||||
nc["buildDomainConfig()<br/>static DomainClass=Valve, commands<br/>tickInterval=null (event-driven)"]
|
||||
end
|
||||
subgraph domain["specificClass.js — orchestrator (BaseDomain)"]
|
||||
sc["Valve.configure()<br/>wires concern modules<br/>overrides registerChild → FluidCompatibility"]
|
||||
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
|
||||
```bash
|
||||
curl -X POST -H 'Content-Type: application/json' \
|
||||
--data @nodes/valve/examples/basic.flow.json \
|
||||
http://localhost:1880/flow
|
||||
```
|
||||
|
||||
| Module | Owns | Read first if you're changing… |
|
||||
|---|---|---|
|
||||
| `state/` | Bindings from state-machine `positionChange` → `updatePosition()` | Move-finished triggers, position callbacks. |
|
||||
| `fluid/` | Service-type compatibility, contract aggregation | Gas-vs-liquid mismatch warnings, upstream fluid tracking. |
|
||||
| `curve/` | Supplier Kv curve load + interpolation | Curve fitting, model selection, density keys. |
|
||||
| `measurement/` | Pressure/flow routing + deltaP recompute | What triggers a recalc, measurement container writes. |
|
||||
| `flow/` | Sequence + setpoint execution, mode validation | Startup / shutdown / move semantics, allowed-source checks. |
|
||||
| `io/` | Port-0 output shape + status badge | What lands on the wire each tick. |
|
||||
| `hydraulicModel.js` | Liquid + gas deltaP formulas, choke detection | Hydraulic calculation errors, choke ratio behaviour. |
|
||||
> [!NOTE]
|
||||
> The shipped `examples/{basic,integration,edge}.flow.json` files are minimal stubs (one inject → valve → debug). A tiered `01 - Basic Manual Control.json` / `02 - Integration with Valve Group.json` / `03 - Dashboard Visualization.json` set, matching the `rotatingMachine` template, is on the backlog. Until then, drive the node directly with injects.
|
||||
|
||||
## 5. Topic contract
|
||||
What to send after deploy (the topics map one-to-one to entries in [Reference — Contracts](Reference-Contracts#topic-contract)):
|
||||
|
||||
> **Auto-generated** from `src/commands/index.js`. Do NOT hand-edit between the markers. Re-run `npm run wiki:contract`.
|
||||
1. `set.mode = virtualControl` — lets the GUI source drive the valve (parent path is for grouped use).
|
||||
2. `cmd.startup` — FSM runs `idle → starting → warmingup → operational`.
|
||||
3. `set.position = {setpoint: 60}` (position %) — valve ramps from 0 to 60; state goes `operational → accelerating → operational`. Each position tick fires a Kv lookup + deltaP recompute.
|
||||
4. `data.flow = {variant: 'measured', value: 25, position: 'downstream', unit: 'm3/h'}` — push flow so the hydraulic model has something to chew on. `delta_predicted_pressure` updates and `evt.deltaPChange` fires upward.
|
||||
5. `cmd.shutdown` — if currently `operational`, the controller first ramps to position 0, then transitions `stopping → coolingdown → idle`.
|
||||
|
||||
<!-- BEGIN AUTOGEN: topic-contract -->
|
||||
> [!IMPORTANT]
|
||||
> **GIF needed.** Demo recording of steps 1–5 with the live status badge. Save as `wiki/_partial-gifs/valve/01-basic-demo.gif`, target ≤ 1 MB after `gifsicle -O3 --lossy=80`.
|
||||
|
||||
| Canonical topic | Aliases | Payload | Unit | Effect |
|
||||
|---|---|---|---|---|
|
||||
| `set.mode` | `setMode` | `string` | — | Switch the valve between auto / manual control modes. |
|
||||
| `cmd.startup` | _(none)_ | `any` | — | Initiate the valve startup sequence. |
|
||||
| `cmd.shutdown` | _(none)_ | `any` | — | Initiate the valve shutdown sequence. |
|
||||
| `cmd.estop` | `emergencystop`, `emergencyStop` | `any` | — | Trigger an emergency stop on the valve. |
|
||||
| `execSequence` | _(none)_ | `object` | — | Legacy umbrella that demuxes payload.action to startup / shutdown / estop. |
|
||||
| `set.position` | `execMovement` | `object` | — | Move the valve to a control-% position via execMovement. |
|
||||
| `data.flow` | `updateFlow` | `object` | — | Push a measured flow into the valve (variant + position + unit). |
|
||||
| `query.curve` | `showcurve` | `any` | — | Return the valve characteristic curve on the reply port. |
|
||||
| `child.register` | `registerChild` | `string` | — | Register a child measurement with this valve. |
|
||||
---
|
||||
|
||||
<!-- END AUTOGEN: topic-contract -->
|
||||
## The seven things you'll send
|
||||
|
||||
## 6. Child registration
|
||||
| Topic | Aliases | Payload | What it does |
|
||||
|:---|:---|:---|:---|
|
||||
| `set.mode` | `setMode` | `"auto"` \| `"virtualControl"` \| `"fysicalControl"` \| `"maintenance"` | Switch operational mode. Source allow-list per mode (defaults from `valve.json`). |
|
||||
| `cmd.startup` | — | `{ source?: string }` | Run the configured `startup` sequence (default `[starting, warmingup, operational]`). |
|
||||
| `cmd.shutdown` | — | `{ source?: string }` | Run `shutdown`. If currently `operational`, first ramps the valve to position 0, then transitions `stopping → coolingdown → idle`. |
|
||||
| `cmd.estop` | `emergencystop`, `emergencyStop` | `{ source?: string, action?: string }` | Trigger an emergency stop — runs the `emergencystop` sequence (default `[emergencystop, off]`). |
|
||||
| `set.position` | `execMovement` | `{ source?: string, action?: string, setpoint: number }` | Move the valve to a position (control-%, `0..100`). Setpoint is coerced to `Number`. |
|
||||
| `data.flow` | `updateFlow` | `{ variant, value, position, unit? }` — `variant ∈ {'measured','predicted'}` | Push a flow measurement; triggers a Kv lookup + deltaP recompute via the hydraulic model. |
|
||||
| `query.curve` | `showcurve` | any | Reply on Port 0 with `{ topic: 'Showing curve', payload: <curve snapshot> }`. |
|
||||
|
||||
valve overrides `BaseDomain.registerChild` with `FluidCompatibility.registerChild`. Upstream sources feed the fluid-contract aggregator; measurement children attach through the standard measurement handshake and land in `MeasurementRouter`.
|
||||
Plus the registration topic emitted upward at startup and accepted from real `measurement` children:
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
subgraph kids["accepted children (softwareType)"]
|
||||
src["machine / rotatingmachine<br/>machinegroup / pumpingstation<br/>valvegroupcontrol"]:::unit
|
||||
m["measurement<br/>type=pressure or flow"]:::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<br/>evt.fluidContractChange"]
|
||||
deltaP --> evt2[evt.deltaPChange]
|
||||
classDef unit fill:#50a8d9,color:#000
|
||||
classDef ctrl fill:#a9daee,color:#000
|
||||
| Topic | Aliases | Payload |
|
||||
|:---|:---|:---|
|
||||
| `child.register` | `registerChild` | child Node-RED id (string); `msg.positionVsParent` carries the position label |
|
||||
|
||||
The legacy umbrella `execSequence` (`{action: 'startup' \| 'shutdown' \| 'emergencystop'}`) is still accepted — it forwards to the canonical `cmd.*` handler and logs a one-time deprecation warning. Scheduled for removal in Phase 7.
|
||||
|
||||
---
|
||||
|
||||
## What you'll see come out
|
||||
|
||||
Sample Port 0 message (delta-compressed, while operational at ~60 % open with a 25 m³/h flow):
|
||||
|
||||
```json
|
||||
{
|
||||
"topic": "valve#valve_a",
|
||||
"payload": {
|
||||
"state": "operational",
|
||||
"percentageOpen": 60,
|
||||
"moveTimeleft": 0,
|
||||
"mode": "auto",
|
||||
"downstream_measured_flow": 25,
|
||||
"downstream_predicted_flow": 0,
|
||||
"delta_predicted_pressure": 84
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
| softwareType | onRegister side-effect | Subscribed events |
|
||||
|---|---|---|
|
||||
| `machine` / `rotatingmachine` | Stored as upstream source; reads `getFluidContract()` or defaults to `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>` |
|
||||
Key shape: **`<position>_<variant>_<type>`** — the legacy three-segment shape. Position labels are lowercase (`downstream`, `delta`, `upstream`). `valve` does **not** use the four-segment `<type>.<variant>.<position>.<childId>` shape that `rotatingMachine` emits.
|
||||
|
||||
## 7. Lifecycle — what one event does
|
||||
| Field | Meaning |
|
||||
|:---|:---|
|
||||
| `state` | Current FSM state. See [Architecture — FSM](Reference-Architecture#fsm). |
|
||||
| `percentageOpen` | Current position (`0..100`). 0 = closed, 100 = fully open. |
|
||||
| `moveTimeleft` | Seconds remaining on the current position move (0 when stationary). |
|
||||
| `mode` | One of `auto` / `virtualControl` / `fysicalControl` / `maintenance`. |
|
||||
| `delta_predicted_pressure` | Predicted deltaP across the valve (output unit `mbar`). |
|
||||
| `downstream_predicted_flow` / `_measured_flow` | Last flow pushed via `data.flow` (output unit `m3/h`). |
|
||||
| `downstream_measured_pressure` / `_predicted_pressure` | Pressure measurements pushed via the `MeasurementRouter`. |
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant parent as valveGroupControl
|
||||
participant valve as valve
|
||||
participant fsm as state FSM
|
||||
participant hyd as hydraulicModel
|
||||
participant out as Port-0
|
||||
---
|
||||
|
||||
parent->>valve: set.position { setpoint: 60 }
|
||||
valve->>fsm: moveTo(60)
|
||||
fsm-->>fsm: operational → accelerating
|
||||
fsm-->>valve: positionChange ticks (stateBindings)
|
||||
valve->>valve: predictKv(position)
|
||||
valve->>hyd: calculateDeltaPMbar(q, kv, downP, rho, T)
|
||||
hyd-->>valve: { deltaPMbar, diagnostics }
|
||||
valve->>valve: write pressure.predicted.delta
|
||||
valve->>parent: emitter.emit('deltaPChange', deltaP)
|
||||
valve->>out: msg { topic, payload (delta-compressed) }
|
||||
fsm-->>fsm: accelerating → operational (setpoint reached)
|
||||
```
|
||||
## Need more?
|
||||
|
||||
## 8. Data model — `getOutput()`
|
||||
| Page | What you'll find |
|
||||
|:---|:---|
|
||||
| [Reference — Contracts](Reference-Contracts) | Full topic contract, config schema, child registration filters |
|
||||
| [Reference — Architecture](Reference-Architecture) | Code map, FSM, hydraulic model, lifecycle |
|
||||
| [Reference — Examples](Reference-Examples) | Shipped example flows + debug recipes |
|
||||
| [Reference — Limitations](Reference-Limitations) | When not to use, known limitations, open questions |
|
||||
|
||||
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 keys follow the legacy `<position>_<variant>_<type>` shape (e.g. `downstream_predicted_flow`, `delta_predicted_pressure`). Only keys with finite values are emitted — consumers must cache and merge (delta-compression is active).
|
||||
|
||||
## 9. Configuration — editor form ↔ config keys
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
subgraph editor["Node-RED editor form"]
|
||||
f1[Reaction Speed]
|
||||
f2[Asset model / supplier / category]
|
||||
f3[Service type]
|
||||
f4[Fluid density / temperature K]
|
||||
f5[Gas choke ratio limit]
|
||||
f6[Startup / warmup / shutdown / cooldown times]
|
||||
f7[Log level / enableLog]
|
||||
f8[positionVsParent]
|
||||
end
|
||||
subgraph config["Domain config slice"]
|
||||
c1["movement.speed (stateConfig)"]
|
||||
c2[asset.model]
|
||||
c3["runtimeOptions.serviceType → hydraulicModel"]
|
||||
c4["runtimeOptions.fluidDensity / fluidTemperatureK"]
|
||||
c5["runtimeOptions.gasChokedRatioLimit"]
|
||||
c6["stateConfig.time.starting / warmingup / stopping / coolingdown"]
|
||||
c7["general.logging.enabled / logLevel"]
|
||||
c8["functionality.positionVsParent → Port-2 registration"]
|
||||
end
|
||||
f1 --> c1
|
||||
f2 --> c2
|
||||
f3 --> c3
|
||||
f4 --> c4
|
||||
f5 --> c5
|
||||
f6 --> c6
|
||||
f7 --> c7
|
||||
f8 --> c8
|
||||
```
|
||||
|
||||
| Form field | Config path | Default | Range / type | Where used |
|
||||
|---|---|---|---|---|
|
||||
| Reaction Speed | `movement.speed` (stateConfig) | `1` | > 0 (%/s) | `MovementManager` — sets rate of position change |
|
||||
| Asset model | `asset.model` | `'Unknown'` | string | `SupplierCurvePredictor` — selects Kv curve dataset |
|
||||
| Service type | `runtimeOptions.serviceType` | `null` (from asset) | `'gas'` / `'liquid'` | `ValveHydraulicModel` formula selection |
|
||||
| Fluid density | `runtimeOptions.fluidDensity` | model default | > 0 (kg/m³) | liquid hydraulic formula |
|
||||
| Fluid temperature | `runtimeOptions.fluidTemperatureK` | model default | > 0 (K) | gas hydraulic formula |
|
||||
| Gas choke limit | `runtimeOptions.gasChokedRatioLimit` | per asset | 0–1 | gas choke cap in `ValveHydraulicModel` |
|
||||
| Startup time | `stateConfig.time.starting` | `10` s | > 0 (s) | `StateManager` transition timer |
|
||||
| Warmup time | `stateConfig.time.warmingup` | `5` s | > 0 (s) | `StateManager` protected transition |
|
||||
| Shutdown time | `stateConfig.time.stopping` | `5` s | > 0 (s) | `StateManager` transition timer |
|
||||
| Cooldown time | `stateConfig.time.coolingdown` | `10` s | > 0 (s) | `StateManager` transition timer |
|
||||
| Mode | `mode.current` | `'auto'` | `auto` / `virtualControl` / `fysicalControl` / `maintenance` | `FlowController.isValidSourceForMode` |
|
||||
| Log level | `general.logging.logLevel` | `'info'` | enum | structured logger |
|
||||
| positionVsParent | `functionality.positionVsParent` | `'atEquipment'` | enum | Port-2 registration message to parent |
|
||||
|
||||
## 10. State chart
|
||||
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
[*] --> off
|
||||
off --> idle : cmd.startup (boot sequence)
|
||||
off --> emergencystop : cmd.estop
|
||||
off --> maintenance : set.mode=maintenance
|
||||
|
||||
idle --> starting : cmd.startup
|
||||
idle --> off : (direct transition)
|
||||
idle --> emergencystop : cmd.estop
|
||||
idle --> maintenance : set.mode=maintenance
|
||||
|
||||
starting --> warmingup : timed (starting duration)
|
||||
starting --> emergencystop : cmd.estop
|
||||
|
||||
warmingup --> operational : timed (warmup duration) [protected — cannot abort]
|
||||
warmingup --> emergencystop : cmd.estop
|
||||
|
||||
operational --> accelerating : set.position > current
|
||||
operational --> decelerating : set.position < current
|
||||
operational --> stopping : cmd.shutdown
|
||||
operational --> emergencystop : cmd.estop
|
||||
|
||||
accelerating --> operational : setpoint reached
|
||||
accelerating --> emergencystop : cmd.estop
|
||||
|
||||
decelerating --> operational : setpoint reached
|
||||
decelerating --> emergencystop : cmd.estop
|
||||
|
||||
stopping --> coolingdown : timed (stopping duration)
|
||||
stopping --> idle : (direct)
|
||||
stopping --> emergencystop : cmd.estop
|
||||
|
||||
coolingdown --> idle : timed (cooldown duration) [protected — cannot abort]
|
||||
coolingdown --> off : (direct)
|
||||
coolingdown --> emergencystop : cmd.estop
|
||||
|
||||
emergencystop --> idle : cmd.reset / sequence
|
||||
emergencystop --> off : cmd.reset / sequence
|
||||
emergencystop --> maintenance : (allowed)
|
||||
|
||||
maintenance --> idle : manual reset
|
||||
maintenance --> off : manual reset
|
||||
```
|
||||
|
||||
**Key valve-specific behaviours:**
|
||||
|
||||
- `accelerating` = position moving up; `decelerating` = position moving down. Both fire `positionChange` ticks. The valve's `stateBindings` hooks these to `updatePosition()` → Kv lookup → deltaP recompute.
|
||||
- `warmingup` and `coolingdown` are **protected** — the abort signal is disabled; these phases cannot be interrupted.
|
||||
- `cmd.shutdown` from `operational` first ramps the valve to position 0 (via `FlowController.executeSequence('shutdown')`), then transitions `stopping → coolingdown → idle`.
|
||||
- `cmd.estop` triggers `emergencystop → off` regardless of current state (except from within protected transitions).
|
||||
- Default sequences: `startup` = `[starting, warmingup, operational]`; `boot` = `[idle, starting, warmingup, operational]`; `emergencystop` = `[emergencystop, off]`.
|
||||
|
||||
## 11. Examples
|
||||
|
||||
| Tier | File | What it shows | Mandatory? |
|
||||
|---|---|---|---|
|
||||
| Basic | `examples/basic.flow.json` | Inject `set.position` + minimal wiring, no parent | ✅ |
|
||||
| Integration | `examples/integration.flow.json` | valve + VGC + upstream measurement source | ✅ |
|
||||
| Edge | `examples/edge.flow.json` | Edge-case inputs (gas, choke, estop, bad setpoints) | ⭕ optional |
|
||||
|
||||
Renamed example files (`01-Basic.flow.json`, `02-Integration.flow.json`, `03-Dashboard.flow.json`) will replace the above when produced. Screenshots under `wiki/_partial-screenshots/valve/`. 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. | Debug tap on Port 2 |
|
||||
| `delta_predicted_pressure` stuck at `0` | Is `kv > 0`? FSM may be in `off` / `idle` — valve is closed. | `state.getCurrentState()`, `percentageOpen` |
|
||||
| Gas mismatch warning on status badge | `fluidCompatibility.status` is `mismatch` or `conflict`. | `getFluidCompatibility()` |
|
||||
| `query.curve` returns empty curve | `asset.model` not found by `loadModel`; check `SupplierCurvePredictor.snapshot()`. | `SupplierCurvePredictor.snapshot()` |
|
||||
| deltaP non-finite | Downstream gauge pressure absolute term ≤ 0, or choked ratio reached. | `hydraulicDiagnostics` in output |
|
||||
| `set.position` has no effect | Check `currentMode` — source may not be in `mode.allowedSources[mode]`. | `FlowController.isValidSourceForMode` |
|
||||
| FSM stuck in `accelerating` / `decelerating` | Movement was aborted but `_returnToOperationalOnAbort` was false. Send a new `set.position`. | `state.js` abort logic |
|
||||
|
||||
> 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 actuator** with a known Kv curve. For a fixed-restriction orifice (no actuator, no curve), model the deltaP externally.
|
||||
- Don't use valve to model a **non-return / check valve** — no position control or FSM-driven actuation is exposed.
|
||||
- Skip valve when an upstream source already provides flow directly and **no pressure-drop estimate is needed** — wire the source straight to the parent without inserting a valve.
|
||||
|
||||
## 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; abort-deadlock edge case documented separately. | `state.js` abort logic + `test/integration/` |
|
||||
| 4 | `execSequence` (legacy umbrella topic) will be removed in Phase 7 — callers must migrate to `cmd.startup` / `cmd.shutdown` / `cmd.estop`. | `CONTRACT.md` — execSequence demux |
|
||||
[EVOLV master wiki](https://gitea.wbd-rd.nl/RnD/EVOLV/wiki/Home) · [Topology Patterns](https://gitea.wbd-rd.nl/RnD/EVOLV/wiki/Topology-Patterns) · [Topic Conventions](https://gitea.wbd-rd.nl/RnD/EVOLV/wiki/Topic-Conventions)
|
||||
|
||||
Reference in New Issue
Block a user