Files
valveGroupControl/wiki/Reference-Examples.md
znetsixe 9552e4fba9 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>
2026-05-19 09:42:12 +02:00

9.7 KiB

Reference — Examples

code-ref

Note

Pending full node review (2026-05). Content reflects CONTRACT.md and current source only.

Every example flow shipped under nodes/valveGroupControl/examples/, plus how to load them, what they show, and the debug recipes that go with them. Live source: nodes/valveGroupControl/examples/.


Shipped examples

File Tier Dependencies What it shows
basic.flow.json 1 EVOLV only Inject data.totalFlow + 2 valve children registered as the group. No parent. Verifies Kv-share split and residual reconciliation.
integration.flow.json 2 EVOLV only VGC + 2 valves + one upstream rotatingMachine source. Source's flow.predicted.* events drive the group; fluid-contract aggregation resolves to liquid.
edge.flow.json 3 EVOLV only Edge cases: no valves available, residual non-convergence, cascaded VGC as upstream source. Currently a structural placeholder — TODO consult examples/edge.flow.json for actual scenarios.

Important

The Tier-1 / Tier-2 / Tier-3 naming convention (01 - <name>.json) used by rotatingMachine has not yet been applied to VGC. Filenames are still the legacy basic/integration/edge triad. Migration tracked in .agents/improvements/IMPROVEMENTS_BACKLOG.md. When renaming, update examples/README.md in the same commit.


Loading a flow

Via the editor

  1. Open the Node-RED editor at http://localhost:1880.
  2. Menu → Import → drag the JSON file.
  3. Click Deploy.

Via the Admin API

curl -X POST -H 'Content-Type: application/json' \
  --data @"nodes/valveGroupControl/examples/basic.flow.json" \
  http://localhost:1880/flows

Example: basic.flow.json

Important

TODO: not yet validated against live Node-RED. Steps below are inferred from CONTRACT.md + the topic registry. Consult examples/basic.flow.json directly for exact node ids and inject payloads. Screenshot needed once validated — save under wiki/_partial-screenshots/valveGroupControl/01-basic-editor.png.

Single VGC with 2 valve children. Demonstrates:

  1. Wire each valve's Port 2 to VGC's input so child.register arrives automatically on deploy.
  2. Inject data.totalFlow with payload { value: 80, unit: "m3/h", position: "atEquipment", variant: "measured" }. VGC runs calcValveFlows:
    • Splits 80 m³/h by Kv share across the 2 valves.
    • Each valve's updateFlow('predicted', share, 'downstream') push.
    • Re-reads each valve's accepted flow.predicted.downstream.
    • Residual loop runs up to maxPasses: 2 until |residual| < 0.001.
  3. Watch Port 0 debug: atEquipment_predicted_flow settles at the assigned total; deltaMax_predicted_pressure updates as each valve emits deltaPChange.
  4. Toggle one valve to off — the next data.totalFlow (or tick) routes 100% to the remaining valve.
  5. Set the offlined valve back to operational — the next tick re-includes it in the split.

Try the residual pass

After the group settles at 80 m³/h:

  1. Inject data.totalFlow = 200 (a value beyond aggregate Kv capacity). Watch lastFlowSolve.residual — it stays large because the valves cap at their accept limits. flow.predicted.atEquipment will read the sum of caps, not 200.
  2. Inject data.totalFlow = 0. Every valve receives updateFlow('predicted', 0, 'downstream'); maxDeltaP collapses to 0.

Example: integration.flow.json

Important

TODO: not yet validated. Screenshot needed once validated — save under wiki/_partial-screenshots/valveGroupControl/02-integration-editor.png.

VGC + 2 valves + 1 upstream rotatingMachine. Demonstrates:

  • Source registration: the rotatingMachine's child.register (softwareType rotatingmachine → canonical machine) lands on VGC; _registerSource subscribes to flow.predicted.downstream, flow.measured.downstream, etc.
  • Source-driven flow: a pump-state change emits flow.predicted.downstream; VGC's handler converts to updateFlow('predicted', value, 'atEquipment', unit) and re-runs calcValveFlows.
  • Fluid-contract resolution: the pump's getFluidContract() (or fallback DEFAULT_SOURCE_SERVICE_TYPE.rotatingmachine = 'liquid') produces serviceType: 'liquid'; fluidContract resolves to {status: 'resolved', serviceType: 'liquid'}.

Example: edge.flow.json

Important

TODO: structural placeholder. Consult the JSON directly for the scenarios it currently exercises. The edge scenarios this node ought to test (per CONTRACT.md + source review):

  • No available valves — all in off / maintenance. Expected: status badge 'No valves' red, every valve pushed 0, lastFlowSolve.assignedTotal = 0.
  • Residual non-convergence — valve curve where Kv-share is a bad first estimate. Expected: loop exits after maxPasses: 2 with non-zero residual; assignedTotal < target; behaviour graceful.
  • Cascaded VGC — upstream source is another VGC. Expected: source registered, flow.* events bound, getFluidContract() propagated up. Not exercised in production.
  • Conflicting fluid contracts — two sources advertise liquid vs gas. Expected: fluidContract.status = 'conflict'; fluidContractChange event emitted.

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
All valves receive assigned flow = 0 getAvailableValves() returns empty list: check every valve's state (off / maintenance excludes), currentMode !== 'maintenance', and kv > 0. src/groupOps/flowDistribution.isValveAvailable
Residual never converges Pathological valve curve: Kv-share is a bad first estimate. Check vgc.lastFlowSolve.residual and .passes — if passes === maxPasses and residual is large, the loop ran out. Raise flowReconciliation.maxPasses (no editor field yet — TODO Phase 7). src/groupOps/flowDistribution.solveFlowDistribution
Group maxDeltaP stale Child deltaPChange not subscribed: valve emitter not exposed via child.emitter, or valve never registered. src/specificClass._bindValveEvents
Service-type stays unknown No upstream source registered, or all advertise unknown type. Check vgc.sources — if empty, no Port-2 child.register reached VGC. src/sources/fluidContract.refreshFluidContract
data.totalFlow silently ignored Mode rejects the source id: check mode.allowedSources for the current mode. maintenance rejects every source. Warn in log: Source '<src>' is not valid for mode '<mode>'. src/specificClass.isValidSourceForMode
set.position has no effect Known: handler is a debug-logged no-op pending Phase 7. See Limitations. src/commands/handlers.setPosition
set.reconcileInterval = 0 (or negative) silently does nothing The handler validates Number.isFinite(nextSec) && nextSec > 0. Otherwise warns and returns. src/commands/handlers.setReconcileInterval
Two emitters fight on the same flow channel (one source publishes atEquipment, another publishes atequipment) Both event names are wired; the last write wins on each updateFlow. There is no source-priority logic. src/sources/fluidContract.SOURCE_FLOW_EVENTS
Cascaded VGC not propagating flow Accepted by router, but no tests — treat as experimental. src/sources/fluidContract.SOURCE_SOFTWARE_TYPES
Output port-0 key shape differs from rotatingMachine's VGC uses <position>_<variant>_<type> (same as MGC) — the inverse of rotatingMachine's <type>.<variant>.<position>.<childId>. Don't mix. src/io/output.getOutput

Never ship enableLog: 'debug' in a demo — fills the container log within seconds and obscures real errors.


Output coverage (TODO)

Important

Per .claude/rules/output-coverage.md: every output on every layer needs a manifest entry + populated + degraded test. test/_output-manifest.md does not yet exist for VGC. Backfill tracked in .agents/improvements/IMPROVEMENTS_BACKLOG.md.

Minimum coverage to land before declaring trial-ready:

  • Port 0 / 1 keys: mode, maxDeltaP, atEquipment_predicted_flow, atEquipment_measured_flow, deltaMax_predicted_pressure. Each tested in populated AND degraded states.
  • Port 2: child.register payload shape.
  • Example flow function-node outputs: each outputs > 1 fan-out enumerated; verify no payload: null literals (lint via npm run lint:flow-outputs).

Page Why
Home Intuitive overview
Reference — Contracts Topic + config + child filters
Reference — Architecture Code map, flow-distribution loop, source aggregation
Reference — Limitations Known issues and open questions
machineGroupControl — Examples Sibling group-control demo flows
valve wiki The child node VGC coordinates
EVOLV — Topology Patterns Where valveGroupControl fits in a larger plant