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>
341 lines
8.3 KiB
JSON
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
|
|
}
|
|
]
|