diff --git a/examples/01-Basic.json b/examples/01-Basic.json index 4bb75bd..cc8eba7 100644 --- a/examples/01-Basic.json +++ b/examples/01-Basic.json @@ -1,479 +1,360 @@ [ - { - "id": "77f00aef1c966167", - "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_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" + }, + { + "p": "unit", + "v": "m3/h", + "vt": "str" + } + ], + "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 m3/h", + "props": [ + { + "p": "topic", + "vt": "str" + }, + { + "p": "payload", + "v": "40", + "vt": "num" + }, + { + "p": "unit", + "v": "m3/h", + "vt": "str" + } + ], + "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" + }, + { + "p": "unit", + "v": "m3", + "vt": "str" + } + ], + "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" + }, + { + "p": "unit", + "v": "m", + "vt": "str" + } + ], + "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" }, - { - "id": "aa3381b896eb2cfb", - "type": "group", - "z": "77f00aef1c966167", - "name": "Pumping Station (Process Cell)", - "style": { - "label": true, - "stroke": "#000000", - "fill": "#0c99d9", - "fill-opacity": "0.10" - }, - "nodes": [ - "8e78b6607deb33a7" - ], - "x": 534, - "y": 351.5, - "w": 232, - "h": 97 - }, - { - "id": "4996420d47442fad", - "type": "group", - "z": "77f00aef1c966167", - "name": "1. Control mode", - "style": { - "stroke": "#666666", - "fill": "#ffdf7f", - "fill-opacity": "0.15", - "label": true, - "color": "#333333" - }, - "nodes": [ - "1155bbbde7c65363", - "e9bea0f95b557f5d" - ], - "x": 94, - "y": 119, - "w": 272, - "h": 122 - }, - { - "id": "a9f9b38b0e00c1d7", - "type": "group", - "z": "77f00aef1c966167", - "name": "2. Flow signals (inflow / outflow)", - "style": { - "stroke": "#666666", - "fill": "#ffdf7f", - "fill-opacity": "0.15", - "label": true, - "color": "#333333" - }, - "nodes": [ - "7b2b5eb919b1ab15", - "3350187815774b95" - ], - "x": 94, - "y": 279, - "w": 262, - "h": 122 - }, - { - "id": "42bf82c87d05f498", - "type": "group", - "z": "77f00aef1c966167", - "name": "3. Operator demand (manual mode only)", - "style": { - "stroke": "#666666", - "fill": "#ffdf7f", - "fill-opacity": "0.15", - "label": true, - "color": "#333333" - }, - "nodes": [ - "48c2262c345c46b9" - ], - "x": 94, - "y": 479, - "w": 261, - "h": 82 - }, - { - "id": "234bdce20170061a", - "type": "group", - "z": "77f00aef1c966167", - "name": "4. Calibration", - "style": { - "stroke": "#666666", - "fill": "#ffdf7f", - "fill-opacity": "0.15", - "label": true, - "color": "#333333" - }, - "nodes": [ - "463eefdd54df89a5", - "2e0642275899fc79" - ], - "x": 94, - "y": 599, - "w": 272, - "h": 122 - }, - { - "id": "f4ba4542514ed853", - "type": "group", - "z": "77f00aef1c966167", - "name": "Expected outputs", - "style": { - "stroke": "#666666", - "fill": "#d1d1d1", - "fill-opacity": "0.2", - "label": true, - "color": "#333333" - }, - "nodes": [ - "b2450e5ee2eebfaa", - "386af1ad8aa8ed12", - "c27c2655f199b530" - ], - "x": 874, - "y": 299, - "w": 252, - "h": 202 - }, - { - "id": "b30af582f935bcb7", - "type": "comment", - "z": "77f00aef1c966167", - "name": "PumpingStation — Basic (Tier 1)", - "info": "Single pumpingStation node driven by inject buttons. Shows the canonical msg.topic command surface.\n\nDefault controlMode = levelbased. Switch to manual to honour set.demand.\n\nHOW TO USE\n1. Deploy the flow.\n2. (optional) Click \"set.mode = manual\" if you want set.demand to forward; otherwise leave it on levelbased and the ramp drives demand from level.\n3. Click \"set.inflow = 60 m³/h\" to push wastewater into the basin.\n4. Watch the basin fill on Port 0 (level, volume rise) and Port 1 (InfluxDB-shaped payload).\n5. In manual mode: click \"set.demand = 40\" — the value surfaces as `manualDemand` on Port 0/1 and in the node status badge.\n6. Click \"calibrate volume 25 m³\" or \"calibrate level 1.5 m\" to snap the predicted-volume integrator.\n\nPORTS\n- Port 0: process output (changed fields only)\n- Port 1: InfluxDB-shaped {measurement, fields, tags, timestamp}\n- Port 2: parent registration (child handshake)", - "x": 650, - "y": 300, - "wires": [] - }, - { - "id": "1155bbbde7c65363", - "type": "inject", - "z": "77f00aef1c966167", - "g": "4996420d47442fad", - "name": "set.mode = manual", - "props": [ - { - "p": "topic", - "vt": "str" - }, - { - "p": "payload", - "v": "manual", - "vt": "str" - } - ], - "repeat": "", - "crontab": "", - "once": false, - "onceDelay": "", - "topic": "set.mode", - "x": 230, - "y": 160, - "wires": [ - [ - "8e78b6607deb33a7" - ] - ] - }, - { - "id": "e9bea0f95b557f5d", - "type": "inject", - "z": "77f00aef1c966167", - "g": "4996420d47442fad", - "name": "set.mode = levelbased", - "props": [ - { - "p": "topic", - "vt": "str" - }, - { - "p": "payload", - "v": "levelbased", - "vt": "str" - } - ], - "repeat": "", - "crontab": "", - "once": false, - "onceDelay": "", - "topic": "set.mode", - "x": 240, - "y": 200, - "wires": [ - [ - "8e78b6607deb33a7" - ] - ] - }, - { - "id": "7b2b5eb919b1ab15", - "type": "inject", - "z": "77f00aef1c966167", - "g": "a9f9b38b0e00c1d7", - "name": "set.inflow = 60 m3/h", - "props": [ - { - "p": "topic", - "vt": "str" - }, - { - "p": "payload", - "v": "60", - "vt": "num" - } - ], - "repeat": "", - "crontab": "", - "once": false, - "onceDelay": "", - "topic": "set.inflow", - "x": 240, - "y": 360, - "wires": [ - [ - "8e78b6607deb33a7" - ] - ] - }, - { - "id": "48c2262c345c46b9", - "type": "inject", - "z": "77f00aef1c966167", - "g": "42bf82c87d05f498", - "name": "set.demand = 40 %", - "props": [ - { - "p": "topic", - "vt": "str" - }, - { - "p": "payload", - "v": "40", - "vt": "num" - } - ], - "repeat": "", - "crontab": "", - "once": false, - "onceDelay": "", - "topic": "set.demand", - "x": 230, - "y": 520, - "wires": [ - [ - "8e78b6607deb33a7" - ] - ] - }, - { - "id": "463eefdd54df89a5", - "type": "inject", - "z": "77f00aef1c966167", - "g": "234bdce20170061a", - "name": "calibrate volume 25 m3", - "props": [ - { - "p": "topic", - "vt": "str" - }, - { - "p": "payload", - "v": "25", - "vt": "num" - } - ], - "repeat": "", - "crontab": "", - "once": false, - "onceDelay": "", - "topic": "cmd.calibrate.volume", - "x": 240, - "y": 640, - "wires": [ - [ - "8e78b6607deb33a7" - ] - ] - }, - { - "id": "2e0642275899fc79", - "type": "inject", - "z": "77f00aef1c966167", - "g": "234bdce20170061a", - "name": "calibrate level 1.5 m", - "props": [ - { - "p": "topic", - "vt": "str" - }, - { - "p": "payload", - "v": "1.5", - "vt": "num" - } - ], - "repeat": "", - "crontab": "", - "once": false, - "onceDelay": "", - "topic": "cmd.calibrate.level", - "x": 240, - "y": 680, - "wires": [ - [ - "8e78b6607deb33a7" - ] - ] - }, - { - "id": "b2450e5ee2eebfaa", - "type": "debug", - "z": "77f00aef1c966167", - "g": "f4ba4542514ed853", - "name": "Port 0: Process", - "active": true, - "tosidebar": true, - "console": false, - "tostatus": false, - "complete": "payload", - "targetType": "msg", - "x": 980, - "y": 340, - "wires": [] - }, - { - "id": "386af1ad8aa8ed12", - "type": "debug", - "z": "77f00aef1c966167", - "g": "f4ba4542514ed853", - "name": "Port 1: InfluxDB", - "active": true, - "tosidebar": true, - "console": false, - "tostatus": false, - "complete": "true", - "targetType": "full", - "x": 980, - "y": 400, - "wires": [] - }, - { - "id": "c27c2655f199b530", - "type": "debug", - "z": "77f00aef1c966167", - "g": "f4ba4542514ed853", - "name": "Port 2: Parent reg", - "active": true, - "tosidebar": true, - "console": false, - "tostatus": false, - "complete": "true", - "targetType": "full", - "x": 990, - "y": 460, - "wires": [] - }, - { - "id": "8e78b6607deb33a7", - "type": "pumpingStation", - "z": "77f00aef1c966167", - "g": "aa3381b896eb2cfb", - "name": "", - "simulator": false, - "basinVolume": 50, - "basinHeight": 4, - "inflowLevel": 1.5, - "outflowLevel": 0.2, - "overflowLevel": 3.8, - "defaultFluid": "wastewater", - "inletPipeDiameter": 0.3, - "outletPipeDiameter": 0.3, - "pipelineLength": 80, - "maxDischargeHead": 24, - "staticHead": 12, - "maxInflowRate": 200, - "temperatureReferenceDegC": 15, - "timeleftToFullOrEmptyThresholdSeconds": 0, - "enableDryRunProtection": true, - "enableHighVolumeSafety": true, - "enableOverfillProtection": true, - "dryRunThresholdPercent": 2, - "highVolumeSafetyThresholdPercent": 98, - "overfillThresholdPercent": 98, - "minHeightBasedOn": "outlet", - "processOutputFormat": "process", - "dbaseOutputFormat": "influxdb", - "refHeight": "NAP", - "basinBottomRef": 1, - "uuid": "", - "supplier": "", - "category": "", - "assetType": "", - "model": "", - "unit": "", - "enableLog": false, - "logLevel": "error", - "positionVsParent": "atEquipment", - "positionIcon": "⊥", - "hasDistance": false, - "distance": "", - "controlMode": "levelbased", - "levelCurveType": "linear", - "logCurveFactor": 9, - "enableShiftedRamp": false, - "shiftLevel": 0, - "shiftArmPercent": 95, - "startLevel": 1, - "stopLevel": 0.5, - "minLevel": 0.20400000000000001, - "maxLevel": 3.8, - "flowSetpoint": null, - "flowDeadband": null, - "x": 650, - "y": 400, - "wires": [ - [ - "b2450e5ee2eebfaa" - ], - [ - "386af1ad8aa8ed12" - ], - [ - "c27c2655f199b530" - ] - ] - }, - { - "id": "3350187815774b95", - "type": "inject", - "z": "77f00aef1c966167", - "g": "a9f9b38b0e00c1d7", - "name": "set.outflow= 80 m3/h", - "props": [ - { - "p": "topic", - "vt": "str" - }, - { - "p": "payload" - } - ], - "repeat": "", - "crontab": "", - "once": false, - "onceDelay": "", - "topic": "set.outflow", - "payload": "80", - "payloadType": "num", - "x": 230, - "y": 320, - "wires": [ - [ - "8e78b6607deb33a7" - ] - ] - }, - { - "id": "ef77c1819422a098", - "type": "global-config", - "env": [], - "modules": { - "EVOLV": "1.0.29" - } - } -] \ No newline at end of file + "nodes": [ + "ps_basic_node", + "ps_basic_format" + ], + "x": 1290, + "y": 230, + "w": 500, + "h": 140 + } +] diff --git a/tools/build-examples.js b/tools/build-examples.js index 74e7ca4..a009206 100644 --- a/tools/build-examples.js +++ b/tools/build-examples.js @@ -67,12 +67,18 @@ function linkIn(id, z, name, x, y, links, downstream) { function inject(id, z, name, topic, payload, payloadType, x, y, wires, opts) { const o = opts || {}; + const props = [ + { p: 'topic', vt: 'str' }, + { p: 'payload', v: String(payload), vt: payloadType }, + ]; + // Command envelope: declare the unit alongside the value so the example + // documents what unit the number is in (the registry converts to the + // descriptor unit). origin = control authority (parent|GUI|fysical). + if (o.unit) props.push({ p: 'unit', v: o.unit, vt: 'str' }); + if (o.origin) props.push({ p: 'origin', v: o.origin, vt: 'str' }); return { id, type: 'inject', z, name, - props: [ - { p: 'topic', vt: 'str' }, - { p: 'payload', v: String(payload), vt: payloadType }, - ], + props, topic, repeat: o.repeat || '', crontab: '', @@ -425,10 +431,10 @@ function buildBasic() { // Lane 2..3: inject nodes (we keep them in lane 1 for proximity). const injectMode = inject('ps_basic_inj_mode', Z, 'set.mode = manual', 'set.mode', 'manual', 'str', 200, 160, ['ps_basic_node']); const injectModeLvl = inject('ps_basic_inj_mode_lvl',Z, 'set.mode = levelbased','set.mode', 'levelbased', 'str', 220, 200, ['ps_basic_node']); - const injectInflow = inject('ps_basic_inj_inflow', Z, 'set.inflow = 60 m3/h', 'set.inflow', '60', 'num', 200, 260, ['ps_basic_node']); - const injectDemand = inject('ps_basic_inj_demand', Z, 'set.demand = 40 %', 'set.demand', '40', 'num', 200, 300, ['ps_basic_node']); - const injectCalVol = inject('ps_basic_inj_calvol', Z, 'calibrate volume 25 m3','cmd.calibrate.volume','25','num', 220, 360, ['ps_basic_node']); - const injectCalLvl = inject('ps_basic_inj_callvl', Z, 'calibrate level 1.5 m','cmd.calibrate.level','1.5','num', 220, 400, ['ps_basic_node']); + const injectInflow = inject('ps_basic_inj_inflow', Z, 'set.inflow = 60 m3/h', 'set.inflow', '60', 'num', 200, 260, ['ps_basic_node'], { unit: 'm3/h' }); + const injectDemand = inject('ps_basic_inj_demand', Z, 'set.demand = 40 m3/h', 'set.demand', '40', 'num', 200, 300, ['ps_basic_node'], { unit: 'm3/h' }); + const injectCalVol = inject('ps_basic_inj_calvol', Z, 'calibrate volume 25 m3','cmd.calibrate.volume','25','num', 220, 360, ['ps_basic_node'], { unit: 'm3' }); + const injectCalLvl = inject('ps_basic_inj_callvl', Z, 'calibrate level 1.5 m','cmd.calibrate.level','1.5','num', 220, 400, ['ps_basic_node'], { unit: 'm' }); nodes.push(injectMode, injectModeLvl, injectInflow, injectDemand, injectCalVol, injectCalLvl); // Lane 5 (PC): the pumpingStation itself. @@ -585,7 +591,7 @@ function buildIntegration() { const setMode = inject('setup_inj_mode', TAB_SETUP, 'set.mode = levelbased', 'set.mode', 'levelbased', 'str', LANE_X[0], 160, ['lout_setup_mode'], { once: true, onceDelay: '0.5' }); const setMgc = inject('setup_inj_mgcmode', TAB_SETUP, 'MGC set.mode = auto', 'set.mode', 'auto', 'str', LANE_X[0], 220, ['lout_setup_mgcmode'],{ once: true, onceDelay: '0.5' }); - const setInflow = inject('setup_inj_inflow', TAB_SETUP, 'seed inflow 60 m3/h', 'set.inflow', '60', 'num', LANE_X[0], 280, ['lout_setup_inflow'], { once: true, onceDelay: '1.0' }); + const setInflow = inject('setup_inj_inflow', TAB_SETUP, 'seed inflow 60 m3/h', 'set.inflow', '60', 'num', LANE_X[0], 280, ['lout_setup_inflow'], { once: true, onceDelay: '1.0', unit: 'm3/h' }); nodes.push(setMode, setMgc, setInflow); const loutMode = linkOut('lout_setup_mode', TAB_SETUP, 'setup:to-ps-mode', LANE_X[7], 160, ['lin_setup_mode']); @@ -809,7 +815,7 @@ function buildDashboard() { nodes.push(inject('ps_dash_setup_mode', TAB_SETUP, 'set.mode = levelbased', 'set.mode', 'levelbased', 'str', LANE_X[0], 160, ['ps_dash_lout_setup_mode'], { once: true, onceDelay: '0.5' })); nodes.push(inject('ps_dash_setup_inflow', TAB_SETUP, 'seed inflow 60 m3/h', 'set.inflow', '60', 'num', - LANE_X[0], 220, ['ps_dash_lout_setup_inflow'], { once: true, onceDelay: '1.0' })); + LANE_X[0], 220, ['ps_dash_lout_setup_inflow'], { once: true, onceDelay: '1.0', unit: 'm3/h' })); nodes.push(linkOut('ps_dash_lout_setup_mode', TAB_SETUP, 'setup:to-ps-mode', LANE_X[7], 160, ['lin_proc_setupmode'])); nodes.push(linkOut('ps_dash_lout_setup_inflow', TAB_SETUP, 'setup:to-ps-inflow', LANE_X[7], 220, ['lin_proc_setupinflow']));