Files
valve/wiki/Home.md
znetsixe 8aa5b5e23e 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>
2026-05-11 15:17:38 +02:00

12 KiB
Raw Blame History

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

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

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 positionChangeupdatePosition() 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.

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.

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.

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   -->|"&lt;type&gt;.measured.&lt;position&gt;"| 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

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.

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

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

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 01 gas formula clamp

10. State chart

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