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>
8.9 KiB
Reference — Examples
Note
Every example flow shipped under
nodes/valve/examples/, plus how to load them, what they show, and the debug recipes that go with them. Live source:nodes/valve/examples/.Pending full node review (2026-05). The shipped flows are currently minimal stubs; tiered demos (matching the
rotatingMachinetemplate) are on the backlog.
Shipped examples
| File | Tier | Dependencies | What it shows |
|---|---|---|---|
basic.flow.json |
1 (stub) | EVOLV only | Minimal: one inject → one valve → one debug. Sanity check that the node loads. |
integration.flow.json |
2 (stub) | EVOLV only | Same shape as basic; placeholder for VGC + measurement integration. |
edge.flow.json |
3 (stub) | EVOLV only | Placeholder for edge cases (gas-choke, e-stop, invalid setpoints). |
Important
Tiered example flows TODO. Replace the three stubs with
01 - Basic Manual Control.json/02 - Integration with Valve Group.json/03 - Dashboard Visualization.jsonfollowing therotatingMachinetemplate. Track in.agents/improvements/IMPROVEMENTS_BACKLOG.mdand validate live against Docker-stack Node-RED. Screenshots / GIFs land underwiki/_partial-screenshots/valve/andwiki/_partial-gifs/valve/.
Loading a flow
Via the editor
- Open the Node-RED editor at
http://localhost:1880. - Menu → Import → drag the JSON file.
- Click Deploy.
Via the Admin API
curl -X POST -H 'Content-Type: application/json' \
--data @"nodes/valve/examples/basic.flow.json" \
http://localhost:1880/flow
Use POST /flow (single tab, full replace) or POST /flows (full deploy) depending on whether other tabs are already loaded.
Driving the basic flow manually
The shipped basic.flow.json has a single inject wired to the valve. To exercise the FSM + hydraulic model, send the following sequence by hand (e.g. via additional inject nodes you wire in, or the Admin API):
set.mode— payload"virtualControl"— lets the GUI source drive the valve.cmd.startup— payload{}. FSM walksidle → starting → warmingup → operational. Watchstateon Port 0.set.position— payload{"setpoint": 60}. FSM goesoperational → accelerating → operational;percentageOpenramps 0 → 60 atmovement.speed%/s.data.flow— payload{"variant": "measured", "value": 25, "position": "downstream", "unit": "m3/h"}. Flow lands in MeasurementContainer;MeasurementRouter.updateFlowrecomputes deltaP.delta_predicted_pressureappears on Port 0;evt.deltaPChangefires upward.data.flow— payload{"variant": "measured", "value": 0, "position": "downstream", "unit": "mbar"}to push downstream pressure as well (needed for the gas-flow path).cmd.shutdown— payload{}. Because the valve isoperational, the controller first rampspercentageOpento 0, thenstatetransitionsstopping → coolingdown → idle.
Important
GIF needed. Demo recording of steps 1–6 + status badge progression. Save as
wiki/_partial-gifs/valve/01-basic-demo.gif, target ≤ 1 MB aftergifsicle -O3 --lossy=80.
Try the position-residue handler
After the valve reaches operational at 60 %:
- Send
set.position = {setpoint: 20}. State goesoperational → decelerating → .... - While
decelerating, sendset.position = {setpoint: 80}. state.moveTorecognises the residue state, transitions back tooperationalsynchronously, then ramps up to 80. No setpoint is lost.
This is the same residue mechanism rotatingMachine uses for fast retargets.
Try the e-stop sequence
From operational, send cmd.estop. The valve runs the emergencystop sequence ([emergencystop, off]). Allowed transitions out of emergencystop are idle / off / maintenance. To restart, drop to idle first (cmd.shutdown from off may not work depending on the state graph — TODO: confirm).
Integration with valveGroupControl
Important
TODO: Tier-2 example. A proper integration flow with
valveGroupControl+ 2×valve children + an upstreamrotatingMachine/pumpingStationfor fluid-contract tracking is on the backlog. Screenshot underwiki/_partial-screenshots/valve/02-integration.png.
When built, the integration flow will demonstrate:
- Auto-registration via Port 2 at deploy — each valve's
child.registerreaches the VGC; no manual wiring needed. - Upstream-source registration — a
rotatingMachineregistered as a child of the valve feedsgetFluidContract()intoFluidCompatibility. Status flips frompending/compatible/mismatchbased onserviceTypeagreement. evt.deltaPChangepropagation from each valve to the VGC for group-level deltaP aggregation.
Dashboard visualization
Important
TODO: Tier-3 example. A FlowFuse Dashboard 2.0 page (
@flowfuse/node-red-dashboard) with control buttons (mode, startup, shutdown, e-stop, position slider), live status (state badge, position %, deltaP, flow), and trend charts (deltaP, position) is on the backlog. Save as03 - Dashboard Visualization.json.
Docker compose snippet
To bring up Node-RED + InfluxDB with EVOLV nodes pre-loaded:
# docker-compose.yml (extract)
services:
nodered:
build: ./docker/nodered
ports: ['1880:1880']
volumes:
- ./docker/nodered/data:/data/evolv
influxdb:
image: influxdb:2.7
ports: ['8086:8086']
Full file: EVOLV/docker-compose.yml.
Debug recipes
| Symptom | First thing to check | Where to look |
|---|---|---|
Editor throws legacy asset field(s) [supplier, ...] on deploy |
Flow predates the AssetResolver refactor. Re-open the node, pick the model from the asset menu, save. The registry derives supplier / category / type. | src/nodeClass.js _rejectLegacyAssetFields. |
Status badge shows ⚠ <message> (yellow ring) |
getFluidCompatibility().status is mismatch or conflict. An upstream source advertised a service type that doesn't match this valve's expected type. |
src/fluid/fluidCompatibility.js, getFluidCompatibility(). |
delta_predicted_pressure stuck at 0 or missing |
kv is 0 (valve closed), the FSM isn't in operational / accelerating / decelerating, or no flow has landed. For gas flow, also needs a finite downstream_measured_pressure. |
state.getCurrentState(), percentageOpen, MeasurementRouter.updateDeltaP. |
set.position has no effect |
Source not in mode.allowedSources[currentMode]. Watch for Source '...' is not valid for mode '...' in the warn log. |
src/flow/flowController.js isValidSourceForMode. |
data.flow payloads aren't reflected on Port 0 |
Payload shape: {variant: 'measured'|'predicted', value: <number>, position: <string>, unit?: 'm3/h'}. Missing variant warns Unrecognized variant '...' for flow update. Missing value warns Received null or undefined value for flow update. |
src/measurement/measurementRouter.js updateFlow. |
| Gas-flow deltaP saturates at a ceiling | The choked-flow cap fired (isChoked: true in hydraulicDiagnostics). Increase gasChokedRatioLimit or revise downstream pressure. |
src/hydraulicModel.js _calculateGasDeltaP. |
query.curve returns empty valveCurve |
asset.model not found by assetResolver; the predictor falls back to inline asset.valveCurve — check that exists. |
src/curve/supplierCurve.js SupplierCurvePredictor.snapshot(). |
FSM stuck in accelerating / decelerating |
A move was aborted with returnToOperationalOnAbort = false. Send a new set.position — the residue handler in state.moveTo transitions back to operational first. |
generalFunctions/src/state/state.js moveTo residue branch. |
| Per-valve Port 0 key names differ from what your dashboard expects | valve uses <position>_<variant>_<type> (e.g. delta_predicted_pressure, downstream_measured_flow). rotatingMachine uses <type>.<variant>.<position>.<childId>. Don't mix them. |
src/io/output.js buildOutput. |
Never ship
enableLog: 'debug'in a demo — fills the container log within seconds and obscures real errors.
Related pages
| Page | Why |
|---|---|
| Home | Intuitive overview |
| Reference — Contracts | Topic + config + child filters |
| Reference — Architecture | Code map, FSM, hydraulic-model pipeline |
| Reference — Limitations | Known issues and open questions |
| valveGroupControl — Examples | Group-control demo flows |
| EVOLV — Topology Patterns | Where valve fits in a larger plant |