Submodule bumps land the deadlock fix (state.js residue unpark + MGC optimalControl dispatch reorder) and pumpingStation stopLevel hysteresis. - Renames examples/pumpingstation-3pumps-dashboard → pumpingstation-complete-example with regenerated flow.json. New dashboard groups, demand-broadcast wiring, S88 placement rule applied, ui-chart trend-split and link-channel naming follow .claude/rules/node-red-flow-layout.md. - New cross-node test harness under test/: end-to-end-pumpingstation drives PS + MGC + 3 pumps + physics simulator end-to-end and verifies the ~5/15 min cycle. - Adds Grafana provisioning dashboards (pumping-station.json) and a helper sync-example.sh script for export/import to live Node-RED. - Docker entrypoint + settings + compose tweaks for the persistent user dir layout used by the demo. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5.6 KiB
EVOLV Examples — Team Workflow
This file is the canonical guide for working with the example flows that live under examples/. Each subfolder is a Node-RED project; the Docker stack is set up so switching between them is two clicks in the editor.
Stack at a glance
| Container | What | URL |
|---|---|---|
evolv-nodered |
Node-RED runtime + dashboard | http://localhost:1880 · dashboard at http://localhost:1880/dashboard |
evolv-influxdb |
Time-series store (port-1 telemetry) | http://localhost:8086 · evolv / evolv-dev-pw |
evolv-grafana |
Provisioned dashboards (anonymous viewer enabled) | http://localhost:3000 |
The evolv_nodered_data named volume keeps /data (flows, projects, sessions) across docker compose down && up. The examples/ directory in this repo is the source of truth; the Node-RED Projects feature operates on a copy in the volume.
Quick start
cd /path/to/EVOLV
docker compose up -d
# Node-RED: http://localhost:1880
# Dashboard: http://localhost:1880/dashboard
# Grafana: http://localhost:3000 (anonymous viewer)
The first time you start it, the entrypoint copies every examples/<name>/ into /data/projects/<name>/ and git inits each. Subsequent starts skip folders that already exist in the volume.
Switching examples
Open the editor → menu → Projects → Open Project → pick another project. The editor reloads the chosen flow.
The default active project on first boot is pumpingstation-complete-example. To change the default for fresh volumes, set DEFAULT_PROJECT=<name> on the nodered service in docker-compose.yml.
Editing a flow
You have two paths. They serve different purposes — pick based on what you're doing.
Path A — edit build_flow.py (canonical, recommended)
# 1. Edit the Python generator
vim examples/<name>/build_flow.py
# 2. Regenerate flow.json
python3 examples/<name>/build_flow.py > examples/<name>/flow.json
# 3. Push to the runtime
./scripts/sync-example.sh <name>
The Python is the source of truth. It's diff-friendly and the right place for any change you intend to commit.
Path B — edit in the Node-RED editor (experimentation)
Open editor → Make changes → Deploy
Edits go into the volume (/data/projects/<name>/flow.json). They survive docker compose down && up but are not in the EVOLV git repo. To incorporate them back:
docker cp evolv-nodered:/data/projects/<name>/flow.json examples/<name>/flow.json
Then commit examples/<name>/flow.json (and reverse-engineer the change into build_flow.py if you want it diff-friendly going forward).
Adding a new example
mkdir examples/<scenario>-<focus>
# Build a flow.json (recommended: a build_flow.py that generates it)
vim examples/<scenario>-<focus>/{build_flow.py,README.md,flow.json}
# Restart Node-RED so the entrypoint bootstraps the new project
docker compose restart nodered
The entrypoint synthesizes package.json, runs git init, and makes an initial commit so Node-RED recognises it as a project. Bootstrap is idempotent — if a /data/projects/<name>/ already exists, it's left alone.
After restart, Projects → Open Project in the editor will list the new entry.
Resetting state
| Goal | Command |
|---|---|
Push the repo's flow.json into the runtime, reload |
./scripts/sync-example.sh <name> |
| Wipe one project's volume copy and re-bootstrap | docker exec evolv-nodered rm -rf /data/projects/<name> then docker compose restart nodered |
| Wipe everything in the volume (flows, sessions, all projects, but NOT InfluxDB/Grafana) | docker compose down && docker volume rm evolv_nodered_data && docker compose up -d |
| Wipe everything including telemetry | docker compose down -v && docker compose up -d |
Debugging
| Symptom | Where to look |
|---|---|
| Flow not loading after deploy | docker logs evolv-nodered for crash backtraces |
| InfluxDB empty / not receiving | Telemetry tab in editor → status of the Count writes node. Should show N POSTs · M lines (0 err). |
Dashboard widget shows n/a |
Check the Process Plant tab → output formatter function for that node — c.<key> keys the dispatcher reads from |
| Grafana dashboard panels empty | Open InfluxDB UI (http://localhost:8086) → Data Explorer → confirm the field name the panel queries actually exists. Field names are flat dotted keys like level.predicted.atequipment.default. |
interpolation configuration: New f =... is constrained warnings |
The pump curve f-axis is out-of-range. f = downstream − upstream pressure differential, in Pa, must be inside the curve's range (e.g. 70 000 – 390 000 Pa for hidrostal-H05K-S03R). Check the per-pump physics feeder formula. |
| High CPU in Node-RED | Per-tick HTTP fan-out to InfluxDB; the pumpingstation example uses a 500 ms batch in the Telemetry tab. If CPU is still high, lower tickIntervalMs in the EVOLV node configs (currently 1000). |
File map per example
examples/<name>/
├── build_flow.py ← canonical source of flow.json (Python generator)
├── flow.json ← regenerated artefact, also tracked in Git
├── README.md ← topology, control modes, dashboard map, things to try
└── package.json ← (synthesized in volume by entrypoint, not in repo)
The repo tracks build_flow.py, flow.json, and README.md. The package.json and .git/ directory of the project live only in the named volume — they're created by the entrypoint on first bootstrap and don't leak back into the EVOLV Git history.