Files
pumpingStation/examples/01-Basic.json
znetsixe ed22f01932 P9.3 + examples: fresh 3-tier flows + pilot wiki Home.md
examples/ (new — was empty except standalone-demo.js):
  01-Basic.json         14 nodes, inject + dashboard, no parent
  02-Integration.json   32 nodes, 2 tabs, measurement + MGC + 2 pumps,
                        link-out/link-in channels per node-red-flow-layout.md
  03-Dashboard.json     63 nodes, 3 tabs (process + UI + setup),
                        FlowFuse charts + sliders, trend-split pattern
  README.md             load instructions
  tools/build-examples.js  regenerator

All canonical topic names only (set.*, cmd.*, data.*, child.*). No
legacy aliases. Every ui-* widget has x/y. Every chart has the full
mandatory key set from node-red-flow-layout.md §4.

wiki/Home.md (new) — pilot page for the 14-section visual-first template.
Sections 5 (topic-contract) + 9 (data-model) are auto-generated via the
new npm run wiki:* scripts; everything else hand-written following
.claude/refactor/WIKI_TEMPLATE.md.

package.json — added wiki:contract / wiki:datamodel / wiki:all scripts
wired to ../generalFunctions/scripts/wikiGen.js.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 14:50:45 +02:00

341 lines
8.3 KiB
JSON

[
{
"id": "ps_basic_tab",
"type": "tab",
"label": "PumpingStation - Basic",
"disabled": false,
"info": "Tier 1: single pumpingStation node driven by inject nodes only. Demonstrates the canonical Phase-2 topic API: set.mode, set.inflow, set.demand."
},
{
"id": "ps_basic_title",
"type": "comment",
"z": "ps_basic_tab",
"name": "PumpingStation - Basic\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\nA 50 m³ basin (3.5 m tall, inflow at 3.0 m, outflow at 0.2 m,\noverflow at 3.2 m). controlMode = levelbased, manual demand allowed\nonly when set.mode = manual.\n\nHOW TO USE:\n 1. Deploy the flow.\n 2. Click \"set.mode = manual\" so set.demand is honoured.\n 3. Click \"set.inflow = 60 m3/h\" to push wastewater into the basin.\n 4. Watch the basin fill on Port 0 (level, volume, percControl rise).\n 5. Click \"calibrate volume 25 m3\" to jump straight to half-full.\n\nAliases (changemode, q_in, Qd, …) still work but log a deprecation\nwarning - fresh flows use the canonical names.",
"info": "",
"x": 600,
"y": 40,
"wires": []
},
{
"id": "ps_basic_inj_mode",
"type": "inject",
"z": "ps_basic_tab",
"name": "set.mode = manual",
"props": [
{
"p": "topic",
"vt": "str"
},
{
"p": "payload",
"v": "manual",
"vt": "str"
}
],
"topic": "set.mode",
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": "",
"x": 200,
"y": 160,
"wires": [
[
"ps_basic_node"
]
]
},
{
"id": "ps_basic_inj_mode_lvl",
"type": "inject",
"z": "ps_basic_tab",
"name": "set.mode = levelbased",
"props": [
{
"p": "topic",
"vt": "str"
},
{
"p": "payload",
"v": "levelbased",
"vt": "str"
}
],
"topic": "set.mode",
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": "",
"x": 220,
"y": 200,
"wires": [
[
"ps_basic_node"
]
]
},
{
"id": "ps_basic_inj_inflow",
"type": "inject",
"z": "ps_basic_tab",
"name": "set.inflow = 60 m3/h",
"props": [
{
"p": "topic",
"vt": "str"
},
{
"p": "payload",
"v": "60",
"vt": "num"
}
],
"topic": "set.inflow",
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": "",
"x": 200,
"y": 260,
"wires": [
[
"ps_basic_node"
]
]
},
{
"id": "ps_basic_inj_demand",
"type": "inject",
"z": "ps_basic_tab",
"name": "set.demand = 40 %",
"props": [
{
"p": "topic",
"vt": "str"
},
{
"p": "payload",
"v": "40",
"vt": "num"
}
],
"topic": "set.demand",
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": "",
"x": 200,
"y": 300,
"wires": [
[
"ps_basic_node"
]
]
},
{
"id": "ps_basic_inj_calvol",
"type": "inject",
"z": "ps_basic_tab",
"name": "calibrate volume 25 m3",
"props": [
{
"p": "topic",
"vt": "str"
},
{
"p": "payload",
"v": "25",
"vt": "num"
}
],
"topic": "cmd.calibrate.volume",
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": "",
"x": 220,
"y": 360,
"wires": [
[
"ps_basic_node"
]
]
},
{
"id": "ps_basic_inj_callvl",
"type": "inject",
"z": "ps_basic_tab",
"name": "calibrate level 1.5 m",
"props": [
{
"p": "topic",
"vt": "str"
},
{
"p": "payload",
"v": "1.5",
"vt": "num"
}
],
"topic": "cmd.calibrate.level",
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": "",
"x": 220,
"y": 400,
"wires": [
[
"ps_basic_node"
]
]
},
{
"id": "ps_basic_node",
"type": "pumpingStation",
"z": "ps_basic_tab",
"name": "Pumping Station",
"simulator": false,
"basinVolume": 50,
"basinHeight": 3.5,
"inflowLevel": 3,
"outflowLevel": 0.2,
"overflowLevel": 3.2,
"defaultFluid": "wastewater",
"inletPipeDiameter": 0.3,
"outletPipeDiameter": 0.3,
"pipelineLength": 80,
"maxDischargeHead": 24,
"staticHead": 12,
"maxInflowRate": 200,
"temperatureReferenceDegC": 15,
"timeleftToFullOrEmptyThresholdSeconds": 0,
"enableDryRunProtection": true,
"enableOverfillProtection": true,
"dryRunThresholdPercent": 2,
"overfillThresholdPercent": 98,
"minHeightBasedOn": "outlet",
"processOutputFormat": "process",
"dbaseOutputFormat": "influxdb",
"refHeight": "NAP",
"basinBottomRef": 1,
"uuid": "example-ps-001",
"supplier": "WBD-RD",
"category": "station",
"assetType": "pumpingstation",
"model": "demo-50m3",
"unit": "m3/h",
"enableLog": true,
"logLevel": "info",
"positionVsParent": "atEquipment",
"positionIcon": "",
"hasDistance": false,
"distance": "",
"distanceUnit": "m",
"distanceDescription": "",
"controlMode": "levelbased",
"startLevel": 1.2,
"minLevel": 0.4,
"maxLevel": 2.8,
"flowSetpoint": null,
"flowDeadband": null,
"x": 1320,
"y": 300,
"wires": [
[
"ps_basic_format"
],
[
"ps_basic_dbg_influx"
],
[
"ps_basic_dbg_parent"
]
]
},
{
"id": "ps_basic_format",
"type": "function",
"z": "ps_basic_tab",
"name": "Merge deltas + format",
"func": "const p = (msg && msg.payload && typeof msg.payload === 'object') ? msg.payload : {};\nconst cache = context.get('c') || {};\nObject.assign(cache, p);\ncontext.set('c', cache);\nfunction pick(prefix) {\n for (const k of Object.keys(cache)) if (k === prefix || k.indexOf(prefix + '.') === 0) {\n const v = Number(cache[k]); if (Number.isFinite(v)) return v;\n } return null;\n}\nconst vol = pick('volume.predicted.atequipment');\nconst lvl = pick('level.predicted.atequipment');\nconst flIn = pick('flow.predicted.in');\nmsg.payload = {\n state: cache.state || 'unknown',\n controlMode: cache.controlMode || cache.mode || 'n/a',\n direction: cache.direction || 'n/a',\n percControl: cache.percControl != null ? Number(cache.percControl).toFixed(1) + ' %' : 'n/a',\n volume: vol != null ? vol.toFixed(2) + ' m3' : 'n/a',\n volumePercent: cache.volumePercent != null ? Number(cache.volumePercent).toFixed(1) + ' %' : 'n/a',\n level: lvl != null ? lvl.toFixed(3) + ' m' : 'n/a',\n inflow: flIn != null ? (flIn * 3600).toFixed(1) + ' m3/h' : 'n/a',\n timeToFull: cache.timeToFull != null ? Number(cache.timeToFull).toFixed(0) + ' s' : 'n/a',\n timeToEmpty: cache.timeToEmpty != null ? Number(cache.timeToEmpty).toFixed(0) + ' s' : 'n/a'\n};\nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 1560,
"y": 280,
"wires": [
[
"ps_basic_dbg_process"
]
]
},
{
"id": "ps_basic_dbg_process",
"type": "debug",
"z": "ps_basic_tab",
"name": "Port 0: Process",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "payload",
"targetType": "msg",
"x": 1800,
"y": 240,
"wires": []
},
{
"id": "ps_basic_dbg_influx",
"type": "debug",
"z": "ps_basic_tab",
"name": "Port 1: InfluxDB",
"active": false,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "true",
"targetType": "full",
"x": 1800,
"y": 320,
"wires": []
},
{
"id": "ps_basic_dbg_parent",
"type": "debug",
"z": "ps_basic_tab",
"name": "Port 2: Parent reg",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "true",
"targetType": "full",
"x": 1800,
"y": 380,
"wires": []
},
{
"id": "grp_ps_basic",
"type": "group",
"z": "ps_basic_tab",
"name": "Pumping Station (PC)",
"style": {
"label": true,
"stroke": "#000000",
"fill": "#0c99d9",
"fill-opacity": "0.10"
},
"nodes": [
"ps_basic_node",
"ps_basic_format"
],
"x": 1290,
"y": 230,
"w": 500,
"h": 140
}
]