[ { "id": "tab_process", "type": "tab", "label": "\ud83c\udfed Process Plant", "disabled": false, "info": "EVOLV plant model: pumpingStation + machineGroupControl + 3 rotatingMachines, each with upstream and downstream pressure measurements.\n\nReceives commands via link-in nodes from the Dashboard / Demo Drivers tabs. Emits per-pump status via link-out per pump.\n\nNo UI, no demo drivers, no one-shot setup logic on this tab \u2014 those live on their own tabs so this layer can be lifted into production unchanged." }, { "id": "c_process_title", "type": "comment", "z": "tab_process", "name": "\ud83c\udfed PROCESS PLANT \u2014 EVOLV nodes only", "info": "Per pump: 2 measurement sensors \u2192 rotatingMachine \u2192 output formatter \u2192 link-out to dashboard.\nMGC orchestrates 3 pumps. PS observes basin (manual mode for the demo).\nAll cross-tab wires are link-in / link-out by named channel.", "x": 640, "y": 20, "wires": [] }, { "id": "c_pump_a", "type": "comment", "z": "tab_process", "name": "\u2500\u2500 Pump A \u2500\u2500", "info": "Up + Dn pressure sensors register as children. rotatingMachine emits state on port 0 (formatted then link-out to UI). Port 2 emits registerChild \u2192 MGC.", "x": 640, "y": 100, "wires": [] }, { "id": "meas_pump_a_u", "type": "measurement", "z": "tab_process", "name": "PT-A-Up", "mode": "analog", "channels": "[]", "scaling": false, "i_min": 0, "i_max": 1, "i_offset": 0, "o_min": 50, "o_max": 400, "simulator": true, "smooth_method": "mean", "count": "5", "processOutputFormat": "process", "dbaseOutputFormat": "influxdb", "uuid": "sensor-pump_a-upstream", "supplier": "vega", "category": "sensor", "assetType": "pressure", "model": "vega-pressure-10", "unit": "mbar", "assetTagNumber": "PT-1-U", "enableLog": false, "logLevel": "warn", "positionVsParent": "upstream", "positionIcon": "", "hasDistance": false, "distance": 0, "distanceUnit": "m", "distanceDescription": "", "x": 380, "y": 140, "wires": [ [], [], [ "pump_a" ] ] }, { "id": "meas_pump_a_d", "type": "measurement", "z": "tab_process", "name": "PT-A-Dn", "mode": "analog", "channels": "[]", "scaling": false, "i_min": 0, "i_max": 1, "i_offset": 0, "o_min": 800, "o_max": 2200, "simulator": true, "smooth_method": "mean", "count": "5", "processOutputFormat": "process", "dbaseOutputFormat": "influxdb", "uuid": "sensor-pump_a-downstream", "supplier": "vega", "category": "sensor", "assetType": "pressure", "model": "vega-pressure-10", "unit": "mbar", "assetTagNumber": "PT-1-D", "enableLog": false, "logLevel": "warn", "positionVsParent": "downstream", "positionIcon": "", "hasDistance": false, "distance": 0, "distanceUnit": "m", "distanceDescription": "", "x": 380, "y": 190, "wires": [ [], [], [ "pump_a" ] ] }, { "id": "lin_setpoint_pump_a", "type": "link in", "z": "tab_process", "name": "cmd:setpoint-A", "links": [ "lout_setpoint_pump_a_dash" ], "x": 120, "y": 160, "wires": [ [ "build_setpoint_pump_a" ] ] }, { "id": "build_setpoint_pump_a", "type": "function", "z": "tab_process", "name": "build setpoint cmd (Pump A)", "func": "msg.topic = 'execMovement';\nmsg.payload = { source: 'GUI', action: 'execMovement', setpoint: Number(msg.payload) };\nreturn msg;", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 600, "y": 160, "wires": [ [ "pump_a" ] ] }, { "id": "lin_seq_pump_a", "type": "link in", "z": "tab_process", "name": "cmd:pump-A-seq", "links": [ "lout_seq_pump_a_dash" ], "x": 120, "y": 210, "wires": [ [ "pump_a" ] ] }, { "id": "pump_a", "type": "rotatingMachine", "z": "tab_process", "name": "Pump A", "speed": "10", "startup": "2", "warmup": "1", "shutdown": "2", "cooldown": "1", "movementMode": "staticspeed", "machineCurve": "", "uuid": "pump-pump_a", "supplier": "hidrostal", "category": "pump", "assetType": "pump-centrifugal", "model": "hidrostal-H05K-S03R", "unit": "m3/h", "curvePressureUnit": "mbar", "curveFlowUnit": "m3/h", "curvePowerUnit": "kW", "curveControlUnit": "%", "enableLog": false, "logLevel": "warn", "positionVsParent": "atEquipment", "positionIcon": "", "hasDistance": false, "distance": 0, "distanceUnit": "m", "distanceDescription": "", "x": 900, "y": 180, "wires": [ [ "format_pump_a" ], [], [ "mgc_pumps" ] ] }, { "id": "format_pump_a", "type": "function", "z": "tab_process", "name": "format Pump A port 0", "func": "const p = msg.payload || {};\nconst c = context.get('c') || {};\nObject.assign(c, p);\ncontext.set('c', c);\nfunction find(prefix) {\n for (const k in c) { if (k.indexOf(prefix) === 0) return c[k]; }\n return null;\n}\nconst flow = find('flow.predicted.downstream.');\nconst power = find('power.predicted.atequipment.');\nconst pU = find('pressure.measured.upstream.');\nconst pD = find('pressure.measured.downstream.');\nmsg.payload = {\n state: c.state || 'idle',\n mode: c.mode || 'auto',\n ctrl: c.ctrl != null ? Number(c.ctrl).toFixed(1) + '%' : 'n/a',\n flow: flow != null ? Number(flow).toFixed(1) + ' m\u00b3/h' : 'n/a',\n power: power != null ? Number(power).toFixed(2) + ' kW' : 'n/a',\n pUp: pU != null ? Number(pU).toFixed(0) + ' mbar' : 'n/a',\n pDn: pD != null ? Number(pD).toFixed(0) + ' mbar' : 'n/a',\n flowNum: flow != null ? Number(flow) : null,\n powerNum: power != null ? Number(power) : null,\n};\nreturn msg;", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 1160, "y": 180, "wires": [ [ "lout_evt_pump_a" ] ] }, { "id": "lout_evt_pump_a", "type": "link out", "z": "tab_process", "name": "evt:pump-A", "mode": "link", "links": [ "lin_evt_pump_a_dash" ], "x": 1420, "y": 180, "wires": [] }, { "id": "c_pump_b", "type": "comment", "z": "tab_process", "name": "\u2500\u2500 Pump B \u2500\u2500", "info": "Up + Dn pressure sensors register as children. rotatingMachine emits state on port 0 (formatted then link-out to UI). Port 2 emits registerChild \u2192 MGC.", "x": 640, "y": 300, "wires": [] }, { "id": "meas_pump_b_u", "type": "measurement", "z": "tab_process", "name": "PT-B-Up", "mode": "analog", "channels": "[]", "scaling": false, "i_min": 0, "i_max": 1, "i_offset": 0, "o_min": 50, "o_max": 400, "simulator": true, "smooth_method": "mean", "count": "5", "processOutputFormat": "process", "dbaseOutputFormat": "influxdb", "uuid": "sensor-pump_b-upstream", "supplier": "vega", "category": "sensor", "assetType": "pressure", "model": "vega-pressure-10", "unit": "mbar", "assetTagNumber": "PT-2-U", "enableLog": false, "logLevel": "warn", "positionVsParent": "upstream", "positionIcon": "", "hasDistance": false, "distance": 0, "distanceUnit": "m", "distanceDescription": "", "x": 380, "y": 340, "wires": [ [], [], [ "pump_b" ] ] }, { "id": "meas_pump_b_d", "type": "measurement", "z": "tab_process", "name": "PT-B-Dn", "mode": "analog", "channels": "[]", "scaling": false, "i_min": 0, "i_max": 1, "i_offset": 0, "o_min": 800, "o_max": 2200, "simulator": true, "smooth_method": "mean", "count": "5", "processOutputFormat": "process", "dbaseOutputFormat": "influxdb", "uuid": "sensor-pump_b-downstream", "supplier": "vega", "category": "sensor", "assetType": "pressure", "model": "vega-pressure-10", "unit": "mbar", "assetTagNumber": "PT-2-D", "enableLog": false, "logLevel": "warn", "positionVsParent": "downstream", "positionIcon": "", "hasDistance": false, "distance": 0, "distanceUnit": "m", "distanceDescription": "", "x": 380, "y": 390, "wires": [ [], [], [ "pump_b" ] ] }, { "id": "lin_setpoint_pump_b", "type": "link in", "z": "tab_process", "name": "cmd:setpoint-B", "links": [ "lout_setpoint_pump_b_dash" ], "x": 120, "y": 360, "wires": [ [ "build_setpoint_pump_b" ] ] }, { "id": "build_setpoint_pump_b", "type": "function", "z": "tab_process", "name": "build setpoint cmd (Pump B)", "func": "msg.topic = 'execMovement';\nmsg.payload = { source: 'GUI', action: 'execMovement', setpoint: Number(msg.payload) };\nreturn msg;", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 600, "y": 360, "wires": [ [ "pump_b" ] ] }, { "id": "lin_seq_pump_b", "type": "link in", "z": "tab_process", "name": "cmd:pump-B-seq", "links": [ "lout_seq_pump_b_dash" ], "x": 120, "y": 410, "wires": [ [ "pump_b" ] ] }, { "id": "pump_b", "type": "rotatingMachine", "z": "tab_process", "name": "Pump B", "speed": "10", "startup": "2", "warmup": "1", "shutdown": "2", "cooldown": "1", "movementMode": "staticspeed", "machineCurve": "", "uuid": "pump-pump_b", "supplier": "hidrostal", "category": "pump", "assetType": "pump-centrifugal", "model": "hidrostal-H05K-S03R", "unit": "m3/h", "curvePressureUnit": "mbar", "curveFlowUnit": "m3/h", "curvePowerUnit": "kW", "curveControlUnit": "%", "enableLog": false, "logLevel": "warn", "positionVsParent": "atEquipment", "positionIcon": "", "hasDistance": false, "distance": 0, "distanceUnit": "m", "distanceDescription": "", "x": 900, "y": 380, "wires": [ [ "format_pump_b" ], [], [ "mgc_pumps" ] ] }, { "id": "format_pump_b", "type": "function", "z": "tab_process", "name": "format Pump B port 0", "func": "const p = msg.payload || {};\nconst c = context.get('c') || {};\nObject.assign(c, p);\ncontext.set('c', c);\nfunction find(prefix) {\n for (const k in c) { if (k.indexOf(prefix) === 0) return c[k]; }\n return null;\n}\nconst flow = find('flow.predicted.downstream.');\nconst power = find('power.predicted.atequipment.');\nconst pU = find('pressure.measured.upstream.');\nconst pD = find('pressure.measured.downstream.');\nmsg.payload = {\n state: c.state || 'idle',\n mode: c.mode || 'auto',\n ctrl: c.ctrl != null ? Number(c.ctrl).toFixed(1) + '%' : 'n/a',\n flow: flow != null ? Number(flow).toFixed(1) + ' m\u00b3/h' : 'n/a',\n power: power != null ? Number(power).toFixed(2) + ' kW' : 'n/a',\n pUp: pU != null ? Number(pU).toFixed(0) + ' mbar' : 'n/a',\n pDn: pD != null ? Number(pD).toFixed(0) + ' mbar' : 'n/a',\n flowNum: flow != null ? Number(flow) : null,\n powerNum: power != null ? Number(power) : null,\n};\nreturn msg;", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 1160, "y": 380, "wires": [ [ "lout_evt_pump_b" ] ] }, { "id": "lout_evt_pump_b", "type": "link out", "z": "tab_process", "name": "evt:pump-B", "mode": "link", "links": [ "lin_evt_pump_b_dash" ], "x": 1420, "y": 380, "wires": [] }, { "id": "c_pump_c", "type": "comment", "z": "tab_process", "name": "\u2500\u2500 Pump C \u2500\u2500", "info": "Up + Dn pressure sensors register as children. rotatingMachine emits state on port 0 (formatted then link-out to UI). Port 2 emits registerChild \u2192 MGC.", "x": 640, "y": 500, "wires": [] }, { "id": "meas_pump_c_u", "type": "measurement", "z": "tab_process", "name": "PT-C-Up", "mode": "analog", "channels": "[]", "scaling": false, "i_min": 0, "i_max": 1, "i_offset": 0, "o_min": 50, "o_max": 400, "simulator": true, "smooth_method": "mean", "count": "5", "processOutputFormat": "process", "dbaseOutputFormat": "influxdb", "uuid": "sensor-pump_c-upstream", "supplier": "vega", "category": "sensor", "assetType": "pressure", "model": "vega-pressure-10", "unit": "mbar", "assetTagNumber": "PT-3-U", "enableLog": false, "logLevel": "warn", "positionVsParent": "upstream", "positionIcon": "", "hasDistance": false, "distance": 0, "distanceUnit": "m", "distanceDescription": "", "x": 380, "y": 540, "wires": [ [], [], [ "pump_c" ] ] }, { "id": "meas_pump_c_d", "type": "measurement", "z": "tab_process", "name": "PT-C-Dn", "mode": "analog", "channels": "[]", "scaling": false, "i_min": 0, "i_max": 1, "i_offset": 0, "o_min": 800, "o_max": 2200, "simulator": true, "smooth_method": "mean", "count": "5", "processOutputFormat": "process", "dbaseOutputFormat": "influxdb", "uuid": "sensor-pump_c-downstream", "supplier": "vega", "category": "sensor", "assetType": "pressure", "model": "vega-pressure-10", "unit": "mbar", "assetTagNumber": "PT-3-D", "enableLog": false, "logLevel": "warn", "positionVsParent": "downstream", "positionIcon": "", "hasDistance": false, "distance": 0, "distanceUnit": "m", "distanceDescription": "", "x": 380, "y": 590, "wires": [ [], [], [ "pump_c" ] ] }, { "id": "lin_setpoint_pump_c", "type": "link in", "z": "tab_process", "name": "cmd:setpoint-C", "links": [ "lout_setpoint_pump_c_dash" ], "x": 120, "y": 560, "wires": [ [ "build_setpoint_pump_c" ] ] }, { "id": "build_setpoint_pump_c", "type": "function", "z": "tab_process", "name": "build setpoint cmd (Pump C)", "func": "msg.topic = 'execMovement';\nmsg.payload = { source: 'GUI', action: 'execMovement', setpoint: Number(msg.payload) };\nreturn msg;", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 600, "y": 560, "wires": [ [ "pump_c" ] ] }, { "id": "lin_seq_pump_c", "type": "link in", "z": "tab_process", "name": "cmd:pump-C-seq", "links": [ "lout_seq_pump_c_dash" ], "x": 120, "y": 610, "wires": [ [ "pump_c" ] ] }, { "id": "pump_c", "type": "rotatingMachine", "z": "tab_process", "name": "Pump C", "speed": "10", "startup": "2", "warmup": "1", "shutdown": "2", "cooldown": "1", "movementMode": "staticspeed", "machineCurve": "", "uuid": "pump-pump_c", "supplier": "hidrostal", "category": "pump", "assetType": "pump-centrifugal", "model": "hidrostal-H05K-S03R", "unit": "m3/h", "curvePressureUnit": "mbar", "curveFlowUnit": "m3/h", "curvePowerUnit": "kW", "curveControlUnit": "%", "enableLog": false, "logLevel": "warn", "positionVsParent": "atEquipment", "positionIcon": "", "hasDistance": false, "distance": 0, "distanceUnit": "m", "distanceDescription": "", "x": 900, "y": 580, "wires": [ [ "format_pump_c" ], [], [ "mgc_pumps" ] ] }, { "id": "format_pump_c", "type": "function", "z": "tab_process", "name": "format Pump C port 0", "func": "const p = msg.payload || {};\nconst c = context.get('c') || {};\nObject.assign(c, p);\ncontext.set('c', c);\nfunction find(prefix) {\n for (const k in c) { if (k.indexOf(prefix) === 0) return c[k]; }\n return null;\n}\nconst flow = find('flow.predicted.downstream.');\nconst power = find('power.predicted.atequipment.');\nconst pU = find('pressure.measured.upstream.');\nconst pD = find('pressure.measured.downstream.');\nmsg.payload = {\n state: c.state || 'idle',\n mode: c.mode || 'auto',\n ctrl: c.ctrl != null ? Number(c.ctrl).toFixed(1) + '%' : 'n/a',\n flow: flow != null ? Number(flow).toFixed(1) + ' m\u00b3/h' : 'n/a',\n power: power != null ? Number(power).toFixed(2) + ' kW' : 'n/a',\n pUp: pU != null ? Number(pU).toFixed(0) + ' mbar' : 'n/a',\n pDn: pD != null ? Number(pD).toFixed(0) + ' mbar' : 'n/a',\n flowNum: flow != null ? Number(flow) : null,\n powerNum: power != null ? Number(power) : null,\n};\nreturn msg;", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 1160, "y": 580, "wires": [ [ "lout_evt_pump_c" ] ] }, { "id": "lout_evt_pump_c", "type": "link out", "z": "tab_process", "name": "evt:pump-C", "mode": "link", "links": [ "lin_evt_pump_c_dash" ], "x": 1420, "y": 580, "wires": [] }, { "id": "c_mgc", "type": "comment", "z": "tab_process", "name": "\u2500\u2500 MGC \u2500\u2500 (orchestrates the 3 pumps via optimalcontrol)", "info": "Receives Qd from cmd:demand link-in. Distributes flow across pumps.", "x": 640, "y": 700, "wires": [] }, { "id": "mgc_pumps", "type": "machineGroupControl", "z": "tab_process", "name": "MGC \u2014 Pump Group", "uuid": "mgc-pump-group", "category": "controller", "assetType": "machinegroupcontrol", "model": "default", "unit": "m3/h", "supplier": "evolv", "enableLog": false, "logLevel": "warn", "positionVsParent": "atEquipment", "positionIcon": "", "hasDistance": false, "distance": 0, "distanceUnit": "m", "distanceDescription": "", "processOutputFormat": "process", "dbaseOutputFormat": "influxdb", "x": 900, "y": 780, "wires": [ [ "format_mgc" ], [], [ "ps_basin" ] ] }, { "id": "format_mgc", "type": "function", "z": "tab_process", "name": "format MGC port 0", "func": "const p = msg.payload || {};\nconst c = context.get('c') || {};\nObject.assign(c, p);\ncontext.set('c', c);\nfunction find(prefix) {\n for (const k in c) { if (k.indexOf(prefix) === 0) return c[k]; }\n return null;\n}\nconst totalFlow = find('flow.predicted.atequipment.') ?? find('downstream_predicted_flow');\nconst totalPower = find('power.predicted.atequipment.') ?? find('atEquipment_predicted_power');\nconst eff = find('efficiency.predicted.atequipment.');\nmsg.payload = {\n totalFlow: totalFlow != null ? Number(totalFlow).toFixed(1) + ' m\u00b3/h' : 'n/a',\n totalPower: totalPower != null ? Number(totalPower).toFixed(2) + ' kW' : 'n/a',\n efficiency: eff != null ? Number(eff).toFixed(3) : 'n/a',\n totalFlowNum: totalFlow != null ? Number(totalFlow) : null,\n totalPowerNum: totalPower != null ? Number(totalPower) : null,\n};\nreturn msg;", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 1160, "y": 780, "wires": [ [ "lout_evt_mgc" ] ] }, { "id": "lout_evt_mgc", "type": "link out", "z": "tab_process", "name": "evt:mgc", "mode": "link", "links": [ "lin_evt_mgc_dash" ], "x": 1420, "y": 780, "wires": [] }, { "id": "c_ps", "type": "comment", "z": "tab_process", "name": "\u2500\u2500 Pumping Station \u2500\u2500 (basin model, levelbased control)", "info": "Receives q_in (simulated inflow) from Demo Drivers tab.\nLevel-based control starts/stops pumps via MGC when level crosses start/stop thresholds.", "x": 640, "y": 900, "wires": [] }, { "id": "lin_qin_at_ps", "type": "link in", "z": "tab_process", "name": "cmd:q_in", "links": [ "lout_qin_drivers" ], "x": 120, "y": 940, "wires": [ [ "ps_basin" ] ] }, { "id": "lin_qd_at_ps", "type": "link in", "z": "tab_process", "name": "cmd:Qd", "links": [ "lout_demand_dash" ], "x": 120, "y": 980, "wires": [ [ "qd_to_ps_wrap" ] ] }, { "id": "qd_to_ps_wrap", "type": "function", "z": "tab_process", "name": "wrap slider \u2192 PS Qd", "func": "msg.topic = 'Qd';\nreturn msg;", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 380, "y": 980, "wires": [ [ "ps_basin" ] ] }, { "id": "lin_ps_mode_at_ps", "type": "link in", "z": "tab_process", "name": "cmd:ps-mode", "links": [ "lout_ps_mode_dash" ], "x": 120, "y": 1020, "wires": [ [ "ps_basin" ] ] }, { "id": "ps_basin", "type": "pumpingStation", "z": "tab_process", "name": "Pumping Station", "uuid": "ps-basin-1", "category": "station", "assetType": "pumpingstation", "model": "default", "unit": "m3/s", "supplier": "evolv", "enableLog": false, "logLevel": "warn", "positionVsParent": "atEquipment", "positionIcon": "", "hasDistance": false, "distance": 0, "distanceUnit": "m", "distanceDescription": "", "processOutputFormat": "process", "dbaseOutputFormat": "influxdb", "controlMode": "levelbased", "basinVolume": 10, "basinHeight": 3, "startLevel": 1.2, "stopLevel": 0.6, "minFlowLevel": 0.6, "maxFlowLevel": 2.8, "heightInlet": 2.5, "heightOutlet": 0.2, "heightOverflow": 2.8, "enableDryRunProtection": true, "enableOverfillProtection": true, "dryRunThresholdPercent": 5, "overfillThresholdPercent": 95, "timeleftToFullOrEmptyThresholdSeconds": 0, "x": 900, "y": 980, "wires": [ [ "format_ps" ], [] ] }, { "id": "format_ps", "type": "function", "z": "tab_process", "name": "format PS port 0", "func": "const p = msg.payload || {};\nconst c = context.get('c') || {};\nObject.assign(c, p);\ncontext.set('c', c);\nfunction find(prefix) {\n for (const k in c) { if (k.indexOf(prefix) === 0) return c[k]; }\n return null;\n}\nconst lvl = find('level.predicted.');\nconst vol = find('volume.predicted.');\nconst qIn = find('flow.measured.upstream.') || find('flow.measured.in.');\nconst qOut = find('flow.measured.downstream.') || find('flow.measured.out.');\n// Compute derived metrics\nconst maxVol = 9.33; // must match basinVolume * basinHeight / basinHeight = basinVolume / surfaceArea * height\nconst fillPct = vol != null ? Math.round(Number(vol) / maxVol * 100) : null;\nconst netM3h = (c.netFlow != null) ? Number(c.netFlow) * 3600 : null;\nconst seconds = (c.seconds != null && Number.isFinite(Number(c.seconds))) ? Number(c.seconds) : null;\nconst timeStr = seconds != null ? (seconds > 60 ? Math.round(seconds/60) + ' min' : Math.round(seconds) + ' s') : 'n/a';\nmsg.payload = {\n direction: c.direction || 'steady',\n level: lvl != null ? Number(lvl).toFixed(2) + ' m' : 'n/a',\n volume: vol != null ? Number(vol).toFixed(1) + ' m\u00b3' : 'n/a',\n fillPct: fillPct != null ? fillPct + '%' : 'n/a',\n netFlow: netM3h != null ? netM3h.toFixed(0) + ' m\u00b3/h' : 'n/a',\n timeLeft: timeStr,\n qIn: qIn != null ? (Number(qIn) * 3600).toFixed(0) + ' m\u00b3/h' : 'n/a',\n qOut: qOut != null ? (Number(qOut) * 3600).toFixed(0) + ' m\u00b3/h' : 'n/a',\n // Numerics for trends\n levelNum: lvl != null ? Number(lvl) : null,\n volumeNum: vol != null ? Number(vol) : null,\n fillPctNum: fillPct,\n netFlowNum: netM3h,\n};\nreturn msg;", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 1160, "y": 980, "wires": [ [ "lout_evt_ps" ] ] }, { "id": "lout_evt_ps", "type": "link out", "z": "tab_process", "name": "evt:ps", "mode": "link", "links": [ "lin_evt_ps_dash" ], "x": 1420, "y": 980, "wires": [] }, { "id": "c_mode_bcast", "type": "comment", "z": "tab_process", "name": "\u2500\u2500 Mode broadcast \u2500\u2500", "info": "Single 'auto' / 'virtualControl' value fans out as setMode to all 3 pumps.", "x": 640, "y": 1100, "wires": [] }, { "id": "lin_mode", "type": "link in", "z": "tab_process", "name": "cmd:mode", "links": [ "lout_mode_dash" ], "x": 120, "y": 1160, "wires": [ [ "fanout_mode" ] ] }, { "id": "fanout_mode", "type": "function", "z": "tab_process", "name": "fan setMode \u2192 3 pumps", "func": "msg.topic = 'setMode';\nreturn [msg, msg, msg];", "outputs": 3, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 600, "y": 1160, "wires": [ [ "pump_a" ], [ "pump_b" ], [ "pump_c" ] ] }, { "id": "c_station_cmds", "type": "comment", "z": "tab_process", "name": "\u2500\u2500 Station-wide commands \u2500\u2500 (Start All / Stop All / Emergency)", "info": "Each link-in carries a fully-built msg ready for handleInput; we just fan out 3-way.", "x": 640, "y": 1300, "wires": [] }, { "id": "lin_station_start", "type": "link in", "z": "tab_process", "name": "cmd:station-startup", "links": [ "lout_cmd_station_startup_dash" ], "x": 120, "y": 1360, "wires": [ [ "fan_station_start" ] ] }, { "id": "fan_station_start", "type": "function", "z": "tab_process", "name": "fan startup \u2192 3 pumps", "func": "return [msg, msg, msg];", "outputs": 3, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 600, "y": 1360, "wires": [ [ "pump_a" ], [ "pump_b" ], [ "pump_c" ] ] }, { "id": "lin_station_stop", "type": "link in", "z": "tab_process", "name": "cmd:station-shutdown", "links": [ "lout_cmd_station_shutdown_dash" ], "x": 120, "y": 1420, "wires": [ [ "fan_station_stop" ] ] }, { "id": "fan_station_stop", "type": "function", "z": "tab_process", "name": "fan shutdown \u2192 3 pumps", "func": "return [msg, msg, msg];", "outputs": 3, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 600, "y": 1420, "wires": [ [ "pump_a" ], [ "pump_b" ], [ "pump_c" ] ] }, { "id": "lin_station_estop", "type": "link in", "z": "tab_process", "name": "cmd:station-estop", "links": [ "lout_cmd_station_estop_dash" ], "x": 120, "y": 1480, "wires": [ [ "fan_station_estop" ] ] }, { "id": "fan_station_estop", "type": "function", "z": "tab_process", "name": "fan emergency stop \u2192 3 pumps", "func": "return [msg, msg, msg];", "outputs": 3, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 600, "y": 1480, "wires": [ [ "pump_a" ], [ "pump_b" ], [ "pump_c" ] ] }, { "id": "c_setup_at_mgc", "type": "comment", "z": "tab_process", "name": "\u2500\u2500 Setup feeders \u2500\u2500", "info": "Cross-tab link from Setup tab \u2192 MGC scaling/mode init.", "x": 640, "y": 1500, "wires": [] }, { "id": "lin_setup_at_mgc", "type": "link in", "z": "tab_process", "name": "setup:to-mgc", "links": [ "lout_setup_to_mgc" ], "x": 120, "y": 1560, "wires": [ [ "mgc_pumps" ] ] }, { "id": "tab_ui", "type": "tab", "label": "\ud83d\udcca Dashboard UI", "disabled": false, "info": "Every ui-* widget lives here. Inputs (sliders/switches/buttons) emit via link-out; status text + charts receive via link-in. No business logic on this tab." }, { "id": "ui_base_ps_demo", "type": "ui-base", "name": "EVOLV Demo", "path": "/dashboard", "appIcon": "", "includeClientData": true, "acceptsClientConfig": [ "ui-notification", "ui-control" ], "showPathInSidebar": true, "headerContent": "page", "navigationStyle": "default", "titleBarStyle": "default" }, { "id": "ui_theme_ps_demo", "type": "ui-theme", "name": "EVOLV Theme", "colors": { "surface": "#ffffff", "primary": "#0f52a5", "bgPage": "#f4f6fa", "groupBg": "#ffffff", "groupOutline": "#cccccc" }, "sizes": { "density": "default", "pagePadding": "12px", "groupGap": "12px", "groupBorderRadius": "6px", "widgetGap": "8px" } }, { "id": "ui_page_control", "type": "ui-page", "name": "Control", "ui": "ui_base_ps_demo", "path": "/pumping-station-demo", "icon": "water_pump", "layout": "grid", "theme": "ui_theme_ps_demo", "breakpoints": [ { "name": "Default", "px": "0", "cols": "12" } ], "order": 1, "className": "" }, { "id": "ui_page_short_trends", "type": "ui-page", "name": "Trends \u2014 10 min", "ui": "ui_base_ps_demo", "path": "/pumping-station-demo/trends-short", "icon": "show_chart", "layout": "grid", "theme": "ui_theme_ps_demo", "breakpoints": [ { "name": "Default", "px": "0", "cols": "12" } ], "order": 2, "className": "" }, { "id": "ui_page_long_trends", "type": "ui-page", "name": "Trends \u2014 1 hour", "ui": "ui_base_ps_demo", "path": "/pumping-station-demo/trends-long", "icon": "timeline", "layout": "grid", "theme": "ui_theme_ps_demo", "breakpoints": [ { "name": "Default", "px": "0", "cols": "12" } ], "order": 3, "className": "" }, { "id": "ui_grp_demand", "type": "ui-group", "name": "1. Process Demand", "page": "ui_page_control", "width": "12", "height": "1", "order": 1, "showTitle": true, "className": "", "groupType": "default", "disabled": false, "visible": true }, { "id": "ui_grp_station", "type": "ui-group", "name": "2. Station Controls", "page": "ui_page_control", "width": "12", "height": "1", "order": 2, "showTitle": true, "className": "", "groupType": "default", "disabled": false, "visible": true }, { "id": "ui_grp_mgc", "type": "ui-group", "name": "3a. MGC Status", "page": "ui_page_control", "width": "6", "height": "1", "order": 3, "showTitle": true, "className": "", "groupType": "default", "disabled": false, "visible": true }, { "id": "ui_grp_ps", "type": "ui-group", "name": "3b. Basin Status", "page": "ui_page_control", "width": "6", "height": "1", "order": 4, "showTitle": true, "className": "", "groupType": "default", "disabled": false, "visible": true }, { "id": "ui_grp_pump_a", "type": "ui-group", "name": "4a. Pump A", "page": "ui_page_control", "width": "4", "height": "1", "order": 5, "showTitle": true, "className": "", "groupType": "default", "disabled": false, "visible": true }, { "id": "ui_grp_pump_b", "type": "ui-group", "name": "4b. Pump B", "page": "ui_page_control", "width": "4", "height": "1", "order": 6, "showTitle": true, "className": "", "groupType": "default", "disabled": false, "visible": true }, { "id": "ui_grp_pump_c", "type": "ui-group", "name": "4c. Pump C", "page": "ui_page_control", "width": "4", "height": "1", "order": 7, "showTitle": true, "className": "", "groupType": "default", "disabled": false, "visible": true }, { "id": "ui_grp_trend_short_flow", "type": "ui-group", "name": "Flow (10 min)", "page": "ui_page_short_trends", "width": "12", "height": "1", "order": 1, "showTitle": true, "className": "", "groupType": "default", "disabled": false, "visible": true }, { "id": "ui_grp_trend_short_power", "type": "ui-group", "name": "Power (10 min)", "page": "ui_page_short_trends", "width": "12", "height": "1", "order": 2, "showTitle": true, "className": "", "groupType": "default", "disabled": false, "visible": true }, { "id": "ui_grp_trend_short_basin", "type": "ui-group", "name": "Basin (10 min)", "page": "ui_page_short_trends", "width": "12", "height": "1", "order": 3, "showTitle": true, "className": "", "groupType": "default", "disabled": false, "visible": true }, { "id": "ui_grp_trend_long_flow", "type": "ui-group", "name": "Flow (1 hour)", "page": "ui_page_long_trends", "width": "12", "height": "1", "order": 1, "showTitle": true, "className": "", "groupType": "default", "disabled": false, "visible": true }, { "id": "ui_grp_trend_long_power", "type": "ui-group", "name": "Power (1 hour)", "page": "ui_page_long_trends", "width": "12", "height": "1", "order": 2, "showTitle": true, "className": "", "groupType": "default", "disabled": false, "visible": true }, { "id": "ui_grp_trend_long_basin", "type": "ui-group", "name": "Basin (1 hour)", "page": "ui_page_long_trends", "width": "12", "height": "1", "order": 3, "showTitle": true, "className": "", "groupType": "default", "disabled": false, "visible": true }, { "id": "c_ui_title", "type": "comment", "z": "tab_ui", "name": "\ud83d\udcca DASHBOARD UI \u2014 only ui-* widgets here", "info": "Layout: column 1 = inputs (sliders/switches/buttons) \u2192 link-outs.\nColumn 2 = link-ins from process \u2192 routed to text/gauge/chart widgets.", "x": 640, "y": 20, "wires": [] }, { "id": "c_ui_demand", "type": "comment", "z": "tab_ui", "name": "\u2500\u2500 Process Demand \u2500\u2500", "info": "", "x": 640, "y": 100, "wires": [] }, { "id": "ui_demand_slider", "type": "ui-slider", "z": "tab_ui", "group": "ui_grp_demand", "name": "Manual demand (manual mode only)", "label": "Manual demand (m\u00b3/h) \u2014 active in manual mode only", "tooltip": "", "order": 1, "width": "0", "height": "0", "passthru": true, "outs": "end", "topic": "manualDemand", "topicType": "str", "min": "0", "max": "100", "step": "5.0", "showLabel": true, "showValue": true, "labelPosition": "top", "valuePosition": "left", "thumbLabel": false, "iconStart": "", "iconEnd": "", "x": 120, "y": 140, "wires": [ [ "lout_demand_dash" ] ] }, { "id": "lout_demand_dash", "type": "link out", "z": "tab_ui", "name": "cmd:Qd", "mode": "link", "links": [ "lin_qd_at_ps" ], "x": 380, "y": 140, "wires": [] }, { "id": "ui_demand_text", "type": "ui-text", "z": "tab_ui", "group": "ui_grp_demand", "order": 1, "width": "0", "height": "0", "name": "Manual demand (active in manual mode)", "label": "Manual demand", "format": "{{msg.payload}} m\u00b3/h", "layout": "row-left", "style": false, "font": "", "fontSize": 14, "color": "#000000", "x": 900, "y": 140, "wires": [] }, { "id": "c_ui_station", "type": "comment", "z": "tab_ui", "name": "\u2500\u2500 Mode + Station-wide buttons \u2500\u2500", "info": "", "x": 640, "y": 320, "wires": [] }, { "id": "ui_mode_toggle", "type": "ui-switch", "z": "tab_ui", "group": "ui_grp_station", "name": "Station mode", "label": "Station mode (Auto = level-based control \u00b7 Manual = slider demand)", "tooltip": "", "order": 1, "width": "0", "height": "0", "passthru": true, "decouple": "false", "topic": "changemode", "topicType": "str", "style": "", "className": "", "evaluate": "true", "onvalue": "levelbased", "onvalueType": "str", "onicon": "auto_mode", "oncolor": "#0f52a5", "offvalue": "manual", "offvalueType": "str", "officon": "back_hand", "offcolor": "#888888", "x": 120, "y": 360, "wires": [ [ "lout_ps_mode_dash" ] ] }, { "id": "lout_ps_mode_dash", "type": "link out", "z": "tab_ui", "name": "cmd:ps-mode", "mode": "link", "links": [ "lin_ps_mode_at_ps" ], "x": 380, "y": 360, "wires": [] }, { "id": "btn_station_0", "type": "ui-button", "z": "tab_ui", "group": "ui_grp_station", "name": "Start all pumps", "label": "Start all pumps", "order": 1, "width": "0", "height": "0", "tooltip": "", "color": "#ffffff", "bgcolor": "#16a34a", "className": "", "icon": "play_arrow", "iconPosition": "left", "payload": "fired", "payloadType": "str", "topic": "station_0", "topicType": "str", "buttonType": "default", "x": 120, "y": 420, "wires": [ [ "wrap_station_0" ] ] }, { "id": "wrap_station_0", "type": "function", "z": "tab_ui", "name": "build cmd (Start all pumps)", "func": "msg.topic = 'execSequence';\nmsg.payload = { source:'GUI', action:'execSequence', parameter:'startup' };\nreturn msg;", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 480, "y": 420, "wires": [ [ "lout_cmd_station_startup_dash" ] ] }, { "id": "lout_cmd_station_startup_dash", "type": "link out", "z": "tab_ui", "name": "cmd:station-startup", "mode": "link", "links": [ "lin_station_start" ], "x": 640, "y": 420, "wires": [] }, { "id": "btn_station_1", "type": "ui-button", "z": "tab_ui", "group": "ui_grp_station", "name": "Stop all pumps", "label": "Stop all pumps", "order": 1, "width": "0", "height": "0", "tooltip": "", "color": "#ffffff", "bgcolor": "#ea580c", "className": "", "icon": "stop", "iconPosition": "left", "payload": "fired", "payloadType": "str", "topic": "station_1", "topicType": "str", "buttonType": "default", "x": 120, "y": 480, "wires": [ [ "wrap_station_1" ] ] }, { "id": "wrap_station_1", "type": "function", "z": "tab_ui", "name": "build cmd (Stop all pumps)", "func": "msg.topic = 'execSequence';\nmsg.payload = { source:'GUI', action:'execSequence', parameter:'shutdown' };\nreturn msg;", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 480, "y": 480, "wires": [ [ "lout_cmd_station_shutdown_dash" ] ] }, { "id": "lout_cmd_station_shutdown_dash", "type": "link out", "z": "tab_ui", "name": "cmd:station-shutdown", "mode": "link", "links": [ "lin_station_stop" ], "x": 640, "y": 480, "wires": [] }, { "id": "btn_station_2", "type": "ui-button", "z": "tab_ui", "group": "ui_grp_station", "name": "EMERGENCY STOP", "label": "EMERGENCY STOP", "order": 1, "width": "0", "height": "0", "tooltip": "", "color": "#ffffff", "bgcolor": "#dc2626", "className": "", "icon": "stop_circle", "iconPosition": "left", "payload": "fired", "payloadType": "str", "topic": "station_2", "topicType": "str", "buttonType": "default", "x": 120, "y": 540, "wires": [ [ "wrap_station_2" ] ] }, { "id": "wrap_station_2", "type": "function", "z": "tab_ui", "name": "build cmd (EMERGENCY STOP)", "func": "msg.topic = 'emergencystop';\nmsg.payload = { source:'GUI', action:'emergencystop' };\nreturn msg;", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 480, "y": 540, "wires": [ [ "lout_cmd_station_estop_dash" ] ] }, { "id": "lout_cmd_station_estop_dash", "type": "link out", "z": "tab_ui", "name": "cmd:station-estop", "mode": "link", "links": [ "lin_station_estop" ], "x": 640, "y": 540, "wires": [] }, { "id": "c_ui_mgc_ps", "type": "comment", "z": "tab_ui", "name": "\u2500\u2500 MGC + Basin overview \u2500\u2500", "info": "", "x": 640, "y": 600, "wires": [] }, { "id": "lin_evt_mgc_dash", "type": "link in", "z": "tab_ui", "name": "evt:mgc", "links": [ "lout_evt_mgc" ], "x": 120, "y": 640, "wires": [ [ "dispatch_mgc" ] ] }, { "id": "dispatch_mgc", "type": "function", "z": "tab_ui", "name": "dispatch MGC", "func": "const p = msg.payload || {};\nreturn [\n {payload: String(p.totalFlow || 'n/a')},\n {payload: String(p.totalPower || 'n/a')},\n {payload: String(p.efficiency || 'n/a')},\n];", "outputs": 3, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 380, "y": 640, "wires": [ [ "ui_mgc_total_flow" ], [ "ui_mgc_total_power" ], [ "ui_mgc_eff" ] ] }, { "id": "ui_mgc_total_flow", "type": "ui-text", "z": "tab_ui", "group": "ui_grp_mgc", "order": 1, "width": "0", "height": "0", "name": "MGC total flow", "label": "Total flow", "format": "{{msg.payload}}", "layout": "row-left", "style": false, "font": "", "fontSize": 14, "color": "#000000", "x": 640, "y": 640, "wires": [] }, { "id": "ui_mgc_total_power", "type": "ui-text", "z": "tab_ui", "group": "ui_grp_mgc", "order": 1, "width": "0", "height": "0", "name": "MGC total power", "label": "Total power", "format": "{{msg.payload}}", "layout": "row-left", "style": false, "font": "", "fontSize": 14, "color": "#000000", "x": 640, "y": 670, "wires": [] }, { "id": "ui_mgc_eff", "type": "ui-text", "z": "tab_ui", "group": "ui_grp_mgc", "order": 1, "width": "0", "height": "0", "name": "MGC efficiency", "label": "Group efficiency", "format": "{{msg.payload}}", "layout": "row-left", "style": false, "font": "", "fontSize": 14, "color": "#000000", "x": 640, "y": 700, "wires": [] }, { "id": "lin_evt_ps_dash", "type": "link in", "z": "tab_ui", "name": "evt:ps", "links": [ "lout_evt_ps" ], "x": 120, "y": 760, "wires": [ [ "dispatch_ps" ] ] }, { "id": "dispatch_ps", "type": "function", "z": "tab_ui", "name": "dispatch PS", "func": "const p = msg.payload || {};\nconst ts = Date.now();\nreturn [\n {payload: String(p.direction || 'steady')},\n {payload: String(p.level || 'n/a')},\n {payload: String(p.volume || 'n/a')},\n {payload: String(p.fillPct || 'n/a')},\n {payload: String(p.netFlow || 'n/a')},\n {payload: String(p.timeLeft || 'n/a')},\n {payload: String(p.qIn || 'n/a')},\n // Trend numerics\n p.fillPctNum != null ? {topic: 'Basin fill', payload: p.fillPctNum, timestamp: ts} : null,\n p.levelNum != null ? {topic: 'Basin level', payload: p.levelNum, timestamp: ts} : null,\n p.netFlowNum != null ? {topic: 'Net flow', payload: p.netFlowNum,timestamp: ts} : null,\n];", "outputs": 10, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 380, "y": 760, "wires": [ [ "ui_ps_direction" ], [ "ui_ps_level" ], [ "ui_ps_volume" ], [ "ui_ps_fill" ], [ "ui_ps_netflow" ], [ "ui_ps_timeleft" ], [ "ui_ps_qin" ], [ "trend_short_basin", "trend_long_basin", "gauge_ps_fill", "gauge_ps_fill_long" ], [ "trend_short_basin", "trend_long_basin", "gauge_ps_level", "gauge_ps_level_long" ], [ "trend_short_basin", "trend_long_basin" ] ] }, { "id": "ui_ps_direction", "type": "ui-text", "z": "tab_ui", "group": "ui_grp_ps", "order": 1, "width": "0", "height": "0", "name": "PS direction", "label": "Direction", "format": "{{msg.payload}}", "layout": "row-left", "style": false, "font": "", "fontSize": 14, "color": "#000000", "x": 640, "y": 760, "wires": [] }, { "id": "ui_ps_level", "type": "ui-text", "z": "tab_ui", "group": "ui_grp_ps", "order": 1, "width": "0", "height": "0", "name": "PS level", "label": "Basin level", "format": "{{msg.payload}}", "layout": "row-left", "style": false, "font": "", "fontSize": 14, "color": "#000000", "x": 640, "y": 800, "wires": [] }, { "id": "ui_ps_volume", "type": "ui-text", "z": "tab_ui", "group": "ui_grp_ps", "order": 1, "width": "0", "height": "0", "name": "PS volume", "label": "Basin volume", "format": "{{msg.payload}}", "layout": "row-left", "style": false, "font": "", "fontSize": 14, "color": "#000000", "x": 640, "y": 840, "wires": [] }, { "id": "ui_ps_fill", "type": "ui-text", "z": "tab_ui", "group": "ui_grp_ps", "order": 1, "width": "0", "height": "0", "name": "PS fill %", "label": "Fill level", "format": "{{msg.payload}}", "layout": "row-left", "style": false, "font": "", "fontSize": 14, "color": "#000000", "x": 640, "y": 880, "wires": [] }, { "id": "ui_ps_netflow", "type": "ui-text", "z": "tab_ui", "group": "ui_grp_ps", "order": 1, "width": "0", "height": "0", "name": "PS net flow", "label": "Net flow", "format": "{{msg.payload}}", "layout": "row-left", "style": false, "font": "", "fontSize": 14, "color": "#000000", "x": 640, "y": 920, "wires": [] }, { "id": "ui_ps_timeleft", "type": "ui-text", "z": "tab_ui", "group": "ui_grp_ps", "order": 1, "width": "0", "height": "0", "name": "PS time left", "label": "Time to full/empty", "format": "{{msg.payload}}", "layout": "row-left", "style": false, "font": "", "fontSize": 14, "color": "#000000", "x": 640, "y": 960, "wires": [] }, { "id": "ui_ps_qin", "type": "ui-text", "z": "tab_ui", "group": "ui_grp_ps", "order": 1, "width": "0", "height": "0", "name": "PS Qin", "label": "Inflow", "format": "{{msg.payload}}", "layout": "row-left", "style": false, "font": "", "fontSize": 14, "color": "#000000", "x": 640, "y": 1000, "wires": [] }, { "id": "c_ui_pump_a", "type": "comment", "z": "tab_ui", "name": "\u2500\u2500 Pump A \u2500\u2500", "info": "", "x": 640, "y": 1000, "wires": [] }, { "id": "lin_evt_pump_a_dash", "type": "link in", "z": "tab_ui", "name": "evt:pump-A", "links": [ "lout_evt_pump_a" ], "x": 120, "y": 1040, "wires": [ [ "dispatch_pump_a" ] ] }, { "id": "dispatch_pump_a", "type": "function", "z": "tab_ui", "name": "dispatch Pump A", "func": "const p = msg.payload || {};\nconst ts = Date.now();\nreturn [\n {payload: String(p.state || 'idle')},\n {payload: String(p.mode || 'auto')},\n {payload: String(p.ctrl || 'n/a')},\n {payload: String(p.flow || 'n/a')},\n {payload: String(p.power || 'n/a')},\n {payload: String(p.pUp || 'n/a')},\n {payload: String(p.pDn || 'n/a')},\n p.flowNum != null ? {topic: 'Pump A', payload: p.flowNum, timestamp: ts} : null,\n p.powerNum != null ? {topic: 'Pump A', payload: p.powerNum, timestamp: ts} : null,\n];", "outputs": 9, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 380, "y": 1040, "wires": [ [ "ui_pump_a_state" ], [ "ui_pump_a_mode" ], [ "ui_pump_a_ctrl" ], [ "ui_pump_a_flow" ], [ "ui_pump_a_power" ], [ "ui_pump_a_pUp" ], [ "ui_pump_a_pDn" ], [ "trend_short_flow", "trend_long_flow" ], [ "trend_short_power", "trend_long_power" ] ] }, { "id": "ui_pump_a_state", "type": "ui-text", "z": "tab_ui", "group": "ui_grp_pump_a", "order": 1, "width": "0", "height": "0", "name": "Pump A State", "label": "State", "format": "{{msg.payload}}", "layout": "row-left", "style": false, "font": "", "fontSize": 14, "color": "#000000", "x": 640, "y": 1040, "wires": [] }, { "id": "ui_pump_a_mode", "type": "ui-text", "z": "tab_ui", "group": "ui_grp_pump_a", "order": 1, "width": "0", "height": "0", "name": "Pump A Mode", "label": "Mode", "format": "{{msg.payload}}", "layout": "row-left", "style": false, "font": "", "fontSize": 14, "color": "#000000", "x": 640, "y": 1080, "wires": [] }, { "id": "ui_pump_a_ctrl", "type": "ui-text", "z": "tab_ui", "group": "ui_grp_pump_a", "order": 1, "width": "0", "height": "0", "name": "Pump A Controller %", "label": "Controller %", "format": "{{msg.payload}}", "layout": "row-left", "style": false, "font": "", "fontSize": 14, "color": "#000000", "x": 640, "y": 1120, "wires": [] }, { "id": "ui_pump_a_flow", "type": "ui-text", "z": "tab_ui", "group": "ui_grp_pump_a", "order": 1, "width": "0", "height": "0", "name": "Pump A Flow", "label": "Flow", "format": "{{msg.payload}}", "layout": "row-left", "style": false, "font": "", "fontSize": 14, "color": "#000000", "x": 640, "y": 1160, "wires": [] }, { "id": "ui_pump_a_power", "type": "ui-text", "z": "tab_ui", "group": "ui_grp_pump_a", "order": 1, "width": "0", "height": "0", "name": "Pump A Power", "label": "Power", "format": "{{msg.payload}}", "layout": "row-left", "style": false, "font": "", "fontSize": 14, "color": "#000000", "x": 640, "y": 1200, "wires": [] }, { "id": "ui_pump_a_pUp", "type": "ui-text", "z": "tab_ui", "group": "ui_grp_pump_a", "order": 1, "width": "0", "height": "0", "name": "Pump A p Upstream", "label": "p Upstream", "format": "{{msg.payload}}", "layout": "row-left", "style": false, "font": "", "fontSize": 14, "color": "#000000", "x": 640, "y": 1240, "wires": [] }, { "id": "ui_pump_a_pDn", "type": "ui-text", "z": "tab_ui", "group": "ui_grp_pump_a", "order": 1, "width": "0", "height": "0", "name": "Pump A p Downstream", "label": "p Downstream", "format": "{{msg.payload}}", "layout": "row-left", "style": false, "font": "", "fontSize": 14, "color": "#000000", "x": 640, "y": 1280, "wires": [] }, { "id": "ui_pump_a_setpoint", "type": "ui-slider", "z": "tab_ui", "group": "ui_grp_pump_a", "name": "Pump A setpoint", "label": "Setpoint % (manual mode)", "tooltip": "", "order": 1, "width": "0", "height": "0", "passthru": true, "outs": "end", "topic": "setpoint_pump_a", "topicType": "str", "min": "0", "max": "100", "step": "5.0", "showLabel": true, "showValue": true, "labelPosition": "top", "valuePosition": "left", "thumbLabel": false, "iconStart": "", "iconEnd": "", "x": 120, "y": 1280, "wires": [ [ "lout_setpoint_pump_a_dash" ] ] }, { "id": "lout_setpoint_pump_a_dash", "type": "link out", "z": "tab_ui", "name": "cmd:setpoint-A", "mode": "link", "links": [ "lin_setpoint_pump_a" ], "x": 380, "y": 1280, "wires": [] }, { "id": "btn_pump_a_start", "type": "ui-button", "z": "tab_ui", "group": "ui_grp_pump_a", "name": "Pump A startup", "label": "Startup", "order": 1, "width": "0", "height": "0", "tooltip": "", "color": "#ffffff", "bgcolor": "#16a34a", "className": "", "icon": "play_arrow", "iconPosition": "left", "payload": "fired", "payloadType": "str", "topic": "start_pump_a", "topicType": "str", "buttonType": "default", "x": 120, "y": 1330, "wires": [ [ "wrap_pump_a_start" ] ] }, { "id": "wrap_pump_a_start", "type": "function", "z": "tab_ui", "name": "build start (Pump A)", "func": "msg.topic = 'execSequence';\nmsg.payload = { source:'GUI', action:'execSequence', parameter:'startup' };\nreturn msg;", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 480, "y": 1330, "wires": [ [ "lout_seq_pump_a_dash" ] ] }, { "id": "btn_pump_a_stop", "type": "ui-button", "z": "tab_ui", "group": "ui_grp_pump_a", "name": "Pump A shutdown", "label": "Shutdown", "order": 1, "width": "0", "height": "0", "tooltip": "", "color": "#ffffff", "bgcolor": "#ea580c", "className": "", "icon": "stop", "iconPosition": "left", "payload": "fired", "payloadType": "str", "topic": "stop_pump_a", "topicType": "str", "buttonType": "default", "x": 120, "y": 1380, "wires": [ [ "wrap_pump_a_stop" ] ] }, { "id": "wrap_pump_a_stop", "type": "function", "z": "tab_ui", "name": "build stop (Pump A)", "func": "msg.topic = 'execSequence';\nmsg.payload = { source:'GUI', action:'execSequence', parameter:'shutdown' };\nreturn msg;", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 480, "y": 1380, "wires": [ [ "lout_seq_pump_a_dash" ] ] }, { "id": "lout_seq_pump_a_dash", "type": "link out", "z": "tab_ui", "name": "cmd:pump-A-seq", "mode": "link", "links": [ "lin_seq_pump_a" ], "x": 640, "y": 1355, "wires": [] }, { "id": "c_ui_pump_b", "type": "comment", "z": "tab_ui", "name": "\u2500\u2500 Pump B \u2500\u2500", "info": "", "x": 640, "y": 1400, "wires": [] }, { "id": "lin_evt_pump_b_dash", "type": "link in", "z": "tab_ui", "name": "evt:pump-B", "links": [ "lout_evt_pump_b" ], "x": 120, "y": 1440, "wires": [ [ "dispatch_pump_b" ] ] }, { "id": "dispatch_pump_b", "type": "function", "z": "tab_ui", "name": "dispatch Pump B", "func": "const p = msg.payload || {};\nconst ts = Date.now();\nreturn [\n {payload: String(p.state || 'idle')},\n {payload: String(p.mode || 'auto')},\n {payload: String(p.ctrl || 'n/a')},\n {payload: String(p.flow || 'n/a')},\n {payload: String(p.power || 'n/a')},\n {payload: String(p.pUp || 'n/a')},\n {payload: String(p.pDn || 'n/a')},\n p.flowNum != null ? {topic: 'Pump B', payload: p.flowNum, timestamp: ts} : null,\n p.powerNum != null ? {topic: 'Pump B', payload: p.powerNum, timestamp: ts} : null,\n];", "outputs": 9, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 380, "y": 1440, "wires": [ [ "ui_pump_b_state" ], [ "ui_pump_b_mode" ], [ "ui_pump_b_ctrl" ], [ "ui_pump_b_flow" ], [ "ui_pump_b_power" ], [ "ui_pump_b_pUp" ], [ "ui_pump_b_pDn" ], [ "trend_short_flow", "trend_long_flow" ], [ "trend_short_power", "trend_long_power" ] ] }, { "id": "ui_pump_b_state", "type": "ui-text", "z": "tab_ui", "group": "ui_grp_pump_b", "order": 1, "width": "0", "height": "0", "name": "Pump B State", "label": "State", "format": "{{msg.payload}}", "layout": "row-left", "style": false, "font": "", "fontSize": 14, "color": "#000000", "x": 640, "y": 1440, "wires": [] }, { "id": "ui_pump_b_mode", "type": "ui-text", "z": "tab_ui", "group": "ui_grp_pump_b", "order": 1, "width": "0", "height": "0", "name": "Pump B Mode", "label": "Mode", "format": "{{msg.payload}}", "layout": "row-left", "style": false, "font": "", "fontSize": 14, "color": "#000000", "x": 640, "y": 1480, "wires": [] }, { "id": "ui_pump_b_ctrl", "type": "ui-text", "z": "tab_ui", "group": "ui_grp_pump_b", "order": 1, "width": "0", "height": "0", "name": "Pump B Controller %", "label": "Controller %", "format": "{{msg.payload}}", "layout": "row-left", "style": false, "font": "", "fontSize": 14, "color": "#000000", "x": 640, "y": 1520, "wires": [] }, { "id": "ui_pump_b_flow", "type": "ui-text", "z": "tab_ui", "group": "ui_grp_pump_b", "order": 1, "width": "0", "height": "0", "name": "Pump B Flow", "label": "Flow", "format": "{{msg.payload}}", "layout": "row-left", "style": false, "font": "", "fontSize": 14, "color": "#000000", "x": 640, "y": 1560, "wires": [] }, { "id": "ui_pump_b_power", "type": "ui-text", "z": "tab_ui", "group": "ui_grp_pump_b", "order": 1, "width": "0", "height": "0", "name": "Pump B Power", "label": "Power", "format": "{{msg.payload}}", "layout": "row-left", "style": false, "font": "", "fontSize": 14, "color": "#000000", "x": 640, "y": 1600, "wires": [] }, { "id": "ui_pump_b_pUp", "type": "ui-text", "z": "tab_ui", "group": "ui_grp_pump_b", "order": 1, "width": "0", "height": "0", "name": "Pump B p Upstream", "label": "p Upstream", "format": "{{msg.payload}}", "layout": "row-left", "style": false, "font": "", "fontSize": 14, "color": "#000000", "x": 640, "y": 1640, "wires": [] }, { "id": "ui_pump_b_pDn", "type": "ui-text", "z": "tab_ui", "group": "ui_grp_pump_b", "order": 1, "width": "0", "height": "0", "name": "Pump B p Downstream", "label": "p Downstream", "format": "{{msg.payload}}", "layout": "row-left", "style": false, "font": "", "fontSize": 14, "color": "#000000", "x": 640, "y": 1680, "wires": [] }, { "id": "ui_pump_b_setpoint", "type": "ui-slider", "z": "tab_ui", "group": "ui_grp_pump_b", "name": "Pump B setpoint", "label": "Setpoint % (manual mode)", "tooltip": "", "order": 1, "width": "0", "height": "0", "passthru": true, "outs": "end", "topic": "setpoint_pump_b", "topicType": "str", "min": "0", "max": "100", "step": "5.0", "showLabel": true, "showValue": true, "labelPosition": "top", "valuePosition": "left", "thumbLabel": false, "iconStart": "", "iconEnd": "", "x": 120, "y": 1680, "wires": [ [ "lout_setpoint_pump_b_dash" ] ] }, { "id": "lout_setpoint_pump_b_dash", "type": "link out", "z": "tab_ui", "name": "cmd:setpoint-B", "mode": "link", "links": [ "lin_setpoint_pump_b" ], "x": 380, "y": 1680, "wires": [] }, { "id": "btn_pump_b_start", "type": "ui-button", "z": "tab_ui", "group": "ui_grp_pump_b", "name": "Pump B startup", "label": "Startup", "order": 1, "width": "0", "height": "0", "tooltip": "", "color": "#ffffff", "bgcolor": "#16a34a", "className": "", "icon": "play_arrow", "iconPosition": "left", "payload": "fired", "payloadType": "str", "topic": "start_pump_b", "topicType": "str", "buttonType": "default", "x": 120, "y": 1730, "wires": [ [ "wrap_pump_b_start" ] ] }, { "id": "wrap_pump_b_start", "type": "function", "z": "tab_ui", "name": "build start (Pump B)", "func": "msg.topic = 'execSequence';\nmsg.payload = { source:'GUI', action:'execSequence', parameter:'startup' };\nreturn msg;", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 480, "y": 1730, "wires": [ [ "lout_seq_pump_b_dash" ] ] }, { "id": "btn_pump_b_stop", "type": "ui-button", "z": "tab_ui", "group": "ui_grp_pump_b", "name": "Pump B shutdown", "label": "Shutdown", "order": 1, "width": "0", "height": "0", "tooltip": "", "color": "#ffffff", "bgcolor": "#ea580c", "className": "", "icon": "stop", "iconPosition": "left", "payload": "fired", "payloadType": "str", "topic": "stop_pump_b", "topicType": "str", "buttonType": "default", "x": 120, "y": 1780, "wires": [ [ "wrap_pump_b_stop" ] ] }, { "id": "wrap_pump_b_stop", "type": "function", "z": "tab_ui", "name": "build stop (Pump B)", "func": "msg.topic = 'execSequence';\nmsg.payload = { source:'GUI', action:'execSequence', parameter:'shutdown' };\nreturn msg;", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 480, "y": 1780, "wires": [ [ "lout_seq_pump_b_dash" ] ] }, { "id": "lout_seq_pump_b_dash", "type": "link out", "z": "tab_ui", "name": "cmd:pump-B-seq", "mode": "link", "links": [ "lin_seq_pump_b" ], "x": 640, "y": 1755, "wires": [] }, { "id": "c_ui_pump_c", "type": "comment", "z": "tab_ui", "name": "\u2500\u2500 Pump C \u2500\u2500", "info": "", "x": 640, "y": 1800, "wires": [] }, { "id": "lin_evt_pump_c_dash", "type": "link in", "z": "tab_ui", "name": "evt:pump-C", "links": [ "lout_evt_pump_c" ], "x": 120, "y": 1840, "wires": [ [ "dispatch_pump_c" ] ] }, { "id": "dispatch_pump_c", "type": "function", "z": "tab_ui", "name": "dispatch Pump C", "func": "const p = msg.payload || {};\nconst ts = Date.now();\nreturn [\n {payload: String(p.state || 'idle')},\n {payload: String(p.mode || 'auto')},\n {payload: String(p.ctrl || 'n/a')},\n {payload: String(p.flow || 'n/a')},\n {payload: String(p.power || 'n/a')},\n {payload: String(p.pUp || 'n/a')},\n {payload: String(p.pDn || 'n/a')},\n p.flowNum != null ? {topic: 'Pump C', payload: p.flowNum, timestamp: ts} : null,\n p.powerNum != null ? {topic: 'Pump C', payload: p.powerNum, timestamp: ts} : null,\n];", "outputs": 9, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 380, "y": 1840, "wires": [ [ "ui_pump_c_state" ], [ "ui_pump_c_mode" ], [ "ui_pump_c_ctrl" ], [ "ui_pump_c_flow" ], [ "ui_pump_c_power" ], [ "ui_pump_c_pUp" ], [ "ui_pump_c_pDn" ], [ "trend_short_flow", "trend_long_flow" ], [ "trend_short_power", "trend_long_power" ] ] }, { "id": "ui_pump_c_state", "type": "ui-text", "z": "tab_ui", "group": "ui_grp_pump_c", "order": 1, "width": "0", "height": "0", "name": "Pump C State", "label": "State", "format": "{{msg.payload}}", "layout": "row-left", "style": false, "font": "", "fontSize": 14, "color": "#000000", "x": 640, "y": 1840, "wires": [] }, { "id": "ui_pump_c_mode", "type": "ui-text", "z": "tab_ui", "group": "ui_grp_pump_c", "order": 1, "width": "0", "height": "0", "name": "Pump C Mode", "label": "Mode", "format": "{{msg.payload}}", "layout": "row-left", "style": false, "font": "", "fontSize": 14, "color": "#000000", "x": 640, "y": 1880, "wires": [] }, { "id": "ui_pump_c_ctrl", "type": "ui-text", "z": "tab_ui", "group": "ui_grp_pump_c", "order": 1, "width": "0", "height": "0", "name": "Pump C Controller %", "label": "Controller %", "format": "{{msg.payload}}", "layout": "row-left", "style": false, "font": "", "fontSize": 14, "color": "#000000", "x": 640, "y": 1920, "wires": [] }, { "id": "ui_pump_c_flow", "type": "ui-text", "z": "tab_ui", "group": "ui_grp_pump_c", "order": 1, "width": "0", "height": "0", "name": "Pump C Flow", "label": "Flow", "format": "{{msg.payload}}", "layout": "row-left", "style": false, "font": "", "fontSize": 14, "color": "#000000", "x": 640, "y": 1960, "wires": [] }, { "id": "ui_pump_c_power", "type": "ui-text", "z": "tab_ui", "group": "ui_grp_pump_c", "order": 1, "width": "0", "height": "0", "name": "Pump C Power", "label": "Power", "format": "{{msg.payload}}", "layout": "row-left", "style": false, "font": "", "fontSize": 14, "color": "#000000", "x": 640, "y": 2000, "wires": [] }, { "id": "ui_pump_c_pUp", "type": "ui-text", "z": "tab_ui", "group": "ui_grp_pump_c", "order": 1, "width": "0", "height": "0", "name": "Pump C p Upstream", "label": "p Upstream", "format": "{{msg.payload}}", "layout": "row-left", "style": false, "font": "", "fontSize": 14, "color": "#000000", "x": 640, "y": 2040, "wires": [] }, { "id": "ui_pump_c_pDn", "type": "ui-text", "z": "tab_ui", "group": "ui_grp_pump_c", "order": 1, "width": "0", "height": "0", "name": "Pump C p Downstream", "label": "p Downstream", "format": "{{msg.payload}}", "layout": "row-left", "style": false, "font": "", "fontSize": 14, "color": "#000000", "x": 640, "y": 2080, "wires": [] }, { "id": "ui_pump_c_setpoint", "type": "ui-slider", "z": "tab_ui", "group": "ui_grp_pump_c", "name": "Pump C setpoint", "label": "Setpoint % (manual mode)", "tooltip": "", "order": 1, "width": "0", "height": "0", "passthru": true, "outs": "end", "topic": "setpoint_pump_c", "topicType": "str", "min": "0", "max": "100", "step": "5.0", "showLabel": true, "showValue": true, "labelPosition": "top", "valuePosition": "left", "thumbLabel": false, "iconStart": "", "iconEnd": "", "x": 120, "y": 2080, "wires": [ [ "lout_setpoint_pump_c_dash" ] ] }, { "id": "lout_setpoint_pump_c_dash", "type": "link out", "z": "tab_ui", "name": "cmd:setpoint-C", "mode": "link", "links": [ "lin_setpoint_pump_c" ], "x": 380, "y": 2080, "wires": [] }, { "id": "btn_pump_c_start", "type": "ui-button", "z": "tab_ui", "group": "ui_grp_pump_c", "name": "Pump C startup", "label": "Startup", "order": 1, "width": "0", "height": "0", "tooltip": "", "color": "#ffffff", "bgcolor": "#16a34a", "className": "", "icon": "play_arrow", "iconPosition": "left", "payload": "fired", "payloadType": "str", "topic": "start_pump_c", "topicType": "str", "buttonType": "default", "x": 120, "y": 2130, "wires": [ [ "wrap_pump_c_start" ] ] }, { "id": "wrap_pump_c_start", "type": "function", "z": "tab_ui", "name": "build start (Pump C)", "func": "msg.topic = 'execSequence';\nmsg.payload = { source:'GUI', action:'execSequence', parameter:'startup' };\nreturn msg;", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 480, "y": 2130, "wires": [ [ "lout_seq_pump_c_dash" ] ] }, { "id": "btn_pump_c_stop", "type": "ui-button", "z": "tab_ui", "group": "ui_grp_pump_c", "name": "Pump C shutdown", "label": "Shutdown", "order": 1, "width": "0", "height": "0", "tooltip": "", "color": "#ffffff", "bgcolor": "#ea580c", "className": "", "icon": "stop", "iconPosition": "left", "payload": "fired", "payloadType": "str", "topic": "stop_pump_c", "topicType": "str", "buttonType": "default", "x": 120, "y": 2180, "wires": [ [ "wrap_pump_c_stop" ] ] }, { "id": "wrap_pump_c_stop", "type": "function", "z": "tab_ui", "name": "build stop (Pump C)", "func": "msg.topic = 'execSequence';\nmsg.payload = { source:'GUI', action:'execSequence', parameter:'shutdown' };\nreturn msg;", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 480, "y": 2180, "wires": [ [ "lout_seq_pump_c_dash" ] ] }, { "id": "lout_seq_pump_c_dash", "type": "link out", "z": "tab_ui", "name": "cmd:pump-C-seq", "mode": "link", "links": [ "lin_seq_pump_c" ], "x": 640, "y": 2155, "wires": [] }, { "id": "c_ui_trends", "type": "comment", "z": "tab_ui", "name": "\u2500\u2500 Trend charts \u2500\u2500 (feed to 4 charts on 2 pages)", "info": "Short-term (10 min) and long-term (1 h) trends share the same feed.\nEach chart on its own page.", "x": 640, "y": 2280, "wires": [] }, { "id": "trend_short_flow", "type": "ui-chart", "z": "tab_ui", "group": "ui_grp_trend_short_flow", "name": "Flow per pump \u2014 10 min", "label": "Flow per pump (m\u00b3/h)", "order": 1, "chartType": "line", "interpolation": "linear", "category": "topic", "categoryType": "msg", "xAxisLabel": "", "xAxisType": "time", "xAxisProperty": "", "xAxisPropertyType": "timestamp", "xAxisFormat": "", "xAxisFormatType": "auto", "xmin": "", "xmax": "", "yAxisLabel": "", "yAxisProperty": "payload", "yAxisPropertyType": "msg", "ymin": "", "ymax": "", "removeOlder": "10", "removeOlderUnit": "60", "removeOlderPoints": "300", "action": "append", "stackSeries": false, "pointShape": "circle", "pointRadius": 4, "showLegend": true, "bins": 10, "colors": [ "#0095FF", "#FF0000", "#FF7F0E", "#2CA02C", "#A347E1", "#D62728", "#FF9896", "#9467BD", "#C5B0D5" ], "textColor": [ "#666666" ], "textColorDefault": true, "gridColor": [ "#e5e5e5" ], "gridColorDefault": true, "width": 12, "height": 8, "className": "", "x": 900, "y": 2320, "wires": [ [] ] }, { "id": "trend_short_power", "type": "ui-chart", "z": "tab_ui", "group": "ui_grp_trend_short_power", "name": "Power per pump \u2014 10 min", "label": "Power per pump (kW)", "order": 1, "chartType": "line", "interpolation": "linear", "category": "topic", "categoryType": "msg", "xAxisLabel": "", "xAxisType": "time", "xAxisProperty": "", "xAxisPropertyType": "timestamp", "xAxisFormat": "", "xAxisFormatType": "auto", "xmin": "", "xmax": "", "yAxisLabel": "", "yAxisProperty": "payload", "yAxisPropertyType": "msg", "ymin": "", "ymax": "", "removeOlder": "10", "removeOlderUnit": "60", "removeOlderPoints": "300", "action": "append", "stackSeries": false, "pointShape": "circle", "pointRadius": 4, "showLegend": true, "bins": 10, "colors": [ "#0095FF", "#FF0000", "#FF7F0E", "#2CA02C", "#A347E1", "#D62728", "#FF9896", "#9467BD", "#C5B0D5" ], "textColor": [ "#666666" ], "textColorDefault": true, "gridColor": [ "#e5e5e5" ], "gridColorDefault": true, "width": 12, "height": 8, "className": "", "x": 900, "y": 2400, "wires": [ [] ] }, { "id": "trend_long_flow", "type": "ui-chart", "z": "tab_ui", "group": "ui_grp_trend_long_flow", "name": "Flow per pump \u2014 1 hour", "label": "Flow per pump (m\u00b3/h)", "order": 1, "chartType": "line", "interpolation": "linear", "category": "topic", "categoryType": "msg", "xAxisLabel": "", "xAxisType": "time", "xAxisProperty": "", "xAxisPropertyType": "timestamp", "xAxisFormat": "", "xAxisFormatType": "auto", "xmin": "", "xmax": "", "yAxisLabel": "", "yAxisProperty": "payload", "yAxisPropertyType": "msg", "ymin": "", "ymax": "", "removeOlder": "60", "removeOlderUnit": "60", "removeOlderPoints": "1800", "action": "append", "stackSeries": false, "pointShape": "circle", "pointRadius": 4, "showLegend": true, "bins": 10, "colors": [ "#0095FF", "#FF0000", "#FF7F0E", "#2CA02C", "#A347E1", "#D62728", "#FF9896", "#9467BD", "#C5B0D5" ], "textColor": [ "#666666" ], "textColorDefault": true, "gridColor": [ "#e5e5e5" ], "gridColorDefault": true, "width": 12, "height": 8, "className": "", "x": 900, "y": 2480, "wires": [ [] ] }, { "id": "trend_long_power", "type": "ui-chart", "z": "tab_ui", "group": "ui_grp_trend_long_power", "name": "Power per pump \u2014 1 hour", "label": "Power per pump (kW)", "order": 1, "chartType": "line", "interpolation": "linear", "category": "topic", "categoryType": "msg", "xAxisLabel": "", "xAxisType": "time", "xAxisProperty": "", "xAxisPropertyType": "timestamp", "xAxisFormat": "", "xAxisFormatType": "auto", "xmin": "", "xmax": "", "yAxisLabel": "", "yAxisProperty": "payload", "yAxisPropertyType": "msg", "ymin": "", "ymax": "", "removeOlder": "60", "removeOlderUnit": "60", "removeOlderPoints": "1800", "action": "append", "stackSeries": false, "pointShape": "circle", "pointRadius": 4, "showLegend": true, "bins": 10, "colors": [ "#0095FF", "#FF0000", "#FF7F0E", "#2CA02C", "#A347E1", "#D62728", "#FF9896", "#9467BD", "#C5B0D5" ], "textColor": [ "#666666" ], "textColorDefault": true, "gridColor": [ "#e5e5e5" ], "gridColorDefault": true, "width": 12, "height": 8, "className": "", "x": 900, "y": 2560, "wires": [ [] ] }, { "id": "trend_short_basin", "type": "ui-chart", "z": "tab_ui", "group": "ui_grp_trend_short_basin", "name": "Basin \u2014 10 min", "label": "Basin metrics", "order": 1, "chartType": "line", "interpolation": "linear", "category": "topic", "categoryType": "msg", "xAxisLabel": "", "xAxisType": "time", "xAxisProperty": "", "xAxisPropertyType": "timestamp", "xAxisFormat": "", "xAxisFormatType": "auto", "xmin": "", "xmax": "", "yAxisLabel": "", "yAxisProperty": "payload", "yAxisPropertyType": "msg", "ymin": "", "ymax": "", "removeOlder": "10", "removeOlderUnit": "60", "removeOlderPoints": "300", "action": "append", "stackSeries": false, "pointShape": "circle", "pointRadius": 4, "showLegend": true, "bins": 10, "colors": [ "#0095FF", "#FF0000", "#FF7F0E", "#2CA02C", "#A347E1", "#D62728", "#FF9896", "#9467BD", "#C5B0D5" ], "textColor": [ "#666666" ], "textColorDefault": true, "gridColor": [ "#e5e5e5" ], "gridColorDefault": true, "width": 8, "height": 8, "className": "", "x": 900, "y": 2640, "wires": [ [] ] }, { "id": "gauge_ps_level", "type": "ui-gauge", "z": "tab_ui", "group": "ui_grp_trend_short_basin", "name": "Basin level gauge (short)", "gtype": "gauge-tank", "gstyle": "Rounded", "title": "Level", "units": "m", "prefix": "", "suffix": " m", "min": 0, "max": 3, "segments": [ { "color": "#f44336", "from": 0 }, { "color": "#ff9800", "from": 0.6 }, { "color": "#2196f3", "from": 1.2 }, { "color": "#ff9800", "from": 2.5 }, { "color": "#f44336", "from": 2.8 } ], "width": 2, "height": 5, "order": 2, "icon": "", "sizeGauge": 20, "sizeGap": 2, "sizeSegments": 10, "x": 1160, "y": 2640, "wires": [] }, { "id": "gauge_ps_fill", "type": "ui-gauge", "z": "tab_ui", "group": "ui_grp_trend_short_basin", "name": "Basin fill gauge (short)", "gtype": "gauge-34", "gstyle": "Rounded", "title": "Fill", "units": "%", "prefix": "", "suffix": "%", "min": 0, "max": 100, "segments": [ { "color": "#f44336", "from": 0 }, { "color": "#ff9800", "from": 10 }, { "color": "#4caf50", "from": 30 }, { "color": "#ff9800", "from": 80 }, { "color": "#f44336", "from": 95 } ], "width": 2, "height": 4, "order": 3, "icon": "water_drop", "sizeGauge": 20, "sizeGap": 2, "sizeSegments": 10, "x": 1420, "y": 2640, "wires": [] }, { "id": "trend_long_basin", "type": "ui-chart", "z": "tab_ui", "group": "ui_grp_trend_long_basin", "name": "Basin \u2014 1 hour", "label": "Basin metrics", "order": 1, "chartType": "line", "interpolation": "linear", "category": "topic", "categoryType": "msg", "xAxisLabel": "", "xAxisType": "time", "xAxisProperty": "", "xAxisPropertyType": "timestamp", "xAxisFormat": "", "xAxisFormatType": "auto", "xmin": "", "xmax": "", "yAxisLabel": "", "yAxisProperty": "payload", "yAxisPropertyType": "msg", "ymin": "", "ymax": "", "removeOlder": "60", "removeOlderUnit": "60", "removeOlderPoints": "1800", "action": "append", "stackSeries": false, "pointShape": "circle", "pointRadius": 4, "showLegend": true, "bins": 10, "colors": [ "#0095FF", "#FF0000", "#FF7F0E", "#2CA02C", "#A347E1", "#D62728", "#FF9896", "#9467BD", "#C5B0D5" ], "textColor": [ "#666666" ], "textColorDefault": true, "gridColor": [ "#e5e5e5" ], "gridColorDefault": true, "width": 8, "height": 8, "className": "", "x": 900, "y": 2820, "wires": [ [] ] }, { "id": "gauge_ps_level_long", "type": "ui-gauge", "z": "tab_ui", "group": "ui_grp_trend_long_basin", "name": "Basin level gauge (long)", "gtype": "gauge-tank", "gstyle": "Rounded", "title": "Level", "units": "m", "prefix": "", "suffix": " m", "min": 0, "max": 3, "segments": [ { "color": "#f44336", "from": 0 }, { "color": "#ff9800", "from": 0.6 }, { "color": "#2196f3", "from": 1.2 }, { "color": "#ff9800", "from": 2.5 }, { "color": "#f44336", "from": 2.8 } ], "width": 2, "height": 5, "order": 2, "icon": "", "sizeGauge": 20, "sizeGap": 2, "sizeSegments": 10, "x": 1160, "y": 2820, "wires": [] }, { "id": "gauge_ps_fill_long", "type": "ui-gauge", "z": "tab_ui", "group": "ui_grp_trend_long_basin", "name": "Basin fill gauge (long)", "gtype": "gauge-34", "gstyle": "Rounded", "title": "Fill", "units": "%", "prefix": "", "suffix": "%", "min": 0, "max": 100, "segments": [ { "color": "#f44336", "from": 0 }, { "color": "#ff9800", "from": 10 }, { "color": "#4caf50", "from": 30 }, { "color": "#ff9800", "from": 80 }, { "color": "#f44336", "from": 95 } ], "width": 2, "height": 4, "order": 3, "icon": "water_drop", "sizeGauge": 20, "sizeGap": 2, "sizeSegments": 10, "x": 1420, "y": 2820, "wires": [] }, { "id": "tab_drivers", "type": "tab", "label": "\ud83c\udf9b\ufe0f Demo Drivers", "disabled": false, "info": "Simulated inflow for the demo. A slow sinusoid generates inflow into the pumping station basin, which then drives the level-based pump control automatically.\n\nIn production, delete this tab \u2014 real inflow comes from upstream measurement sensors." }, { "id": "c_drv_title", "type": "comment", "z": "tab_drivers", "name": "\ud83c\udf9b\ufe0f DEMO DRIVERS \u2014 simulated basin inflow", "info": "Sinus generator \u2192 q_in to pumpingStation. Basin fills \u2192 level-based\ncontrol starts pumps \u2192 basin drains \u2192 pumps stop \u2192 cycle repeats.", "x": 640, "y": 20, "wires": [] }, { "id": "c_drv_sinus", "type": "comment", "z": "tab_drivers", "name": "\u2500\u2500 Sinusoidal inflow generator \u2500\u2500", "info": "Produces a smooth inflow curve (m\u00b3/s) and sends to pumpingStation\nvia the cmd:q_in link channel. Period = 120s.", "x": 640, "y": 100, "wires": [] }, { "id": "sinus_tick", "type": "inject", "z": "tab_drivers", "name": "tick (1s inflow)", "props": [ { "p": "topic", "vt": "str" }, { "p": "payload", "v": "", "vt": "date" } ], "topic": "sinusTick", "payload": "", "payloadType": "date", "repeat": "1", "crontab": "", "once": false, "onceDelay": "0.5", "x": 120, "y": 140, "wires": [ [ "sinus_fn" ] ] }, { "id": "sinus_fn", "type": "function", "z": "tab_drivers", "name": "sinus inflow (m\u00b3/s)", "func": "const base = 0.005; // m\u00b3/s (~18 m\u00b3/h always)\nconst amplitude = 0.03; // m\u00b3/s (~108 m\u00b3/h peak)\nconst period = 120; // seconds per full cycle\nconst t = Date.now() / 1000; // seconds since epoch\nconst q = base + amplitude * (1 + Math.sin(2 * Math.PI * t / period)) / 2;\nreturn { topic: 'q_in', payload: q, unit: 'm3/s', timestamp: Date.now() };", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 600, "y": 140, "wires": [ [ "lout_qin_drivers" ] ] }, { "id": "lout_qin_drivers", "type": "link out", "z": "tab_drivers", "name": "cmd:q_in", "mode": "link", "links": [ "lin_qin_at_ps" ], "x": 900, "y": 140, "wires": [] }, { "id": "tab_setup", "type": "tab", "label": "\u2699\ufe0f Setup & Init", "disabled": false, "info": "One-shot deploy-time injects. Sets MGC scaling/mode, broadcasts pumps mode = auto, and auto-starts the pumps + random demand." }, { "id": "c_setup_title", "type": "comment", "z": "tab_setup", "name": "\u2699\ufe0f SETUP & INIT \u2014 one-shot deploy-time injects", "info": "Disable this tab in production \u2014 the runtime should be persistent.", "x": 640, "y": 20, "wires": [] }, { "id": "setup_mgc_scaling", "type": "inject", "z": "tab_setup", "name": "MGC scaling = normalized", "props": [ { "p": "topic", "vt": "str" }, { "p": "payload", "v": "normalized", "vt": "str" } ], "topic": "setScaling", "payload": "normalized", "payloadType": "str", "repeat": "", "crontab": "", "once": true, "onceDelay": "1.5", "x": 120, "y": 100, "wires": [ [ "lout_setup_to_mgc" ] ] }, { "id": "setup_mgc_mode", "type": "inject", "z": "tab_setup", "name": "MGC mode = optimalcontrol", "props": [ { "p": "topic", "vt": "str" }, { "p": "payload", "v": "optimalcontrol", "vt": "str" } ], "topic": "setMode", "payload": "optimalcontrol", "payloadType": "str", "repeat": "", "crontab": "", "once": true, "onceDelay": "1.7", "x": 120, "y": 160, "wires": [ [ "lout_setup_to_mgc" ] ] }, { "id": "lout_setup_to_mgc", "type": "link out", "z": "tab_setup", "name": "setup:to-mgc", "mode": "link", "links": [ "lin_setup_at_mgc" ], "x": 380, "y": 130, "wires": [] }, { "id": "setup_pumps_mode", "type": "inject", "z": "tab_setup", "name": "pumps mode = auto", "props": [ { "p": "topic", "vt": "str" }, { "p": "payload", "v": "auto", "vt": "str" } ], "topic": "setMode", "payload": "auto", "payloadType": "str", "repeat": "", "crontab": "", "once": true, "onceDelay": "2.0", "x": 120, "y": 250, "wires": [ [ "lout_mode_setup" ] ] }, { "id": "lout_mode_setup", "type": "link out", "z": "tab_setup", "name": "cmd:mode", "mode": "link", "links": [ "lin_mode" ], "x": 380, "y": 250, "wires": [] }, { "id": "setup_pumps_startup", "type": "inject", "z": "tab_setup", "name": "auto-startup all pumps", "props": [ { "p": "topic", "vt": "str" }, { "p": "payload", "v": "{\"source\":\"GUI\",\"action\":\"execSequence\",\"parameter\":\"startup\"}", "vt": "json" } ], "topic": "execSequence", "payload": "{\"source\":\"GUI\",\"action\":\"execSequence\",\"parameter\":\"startup\"}", "payloadType": "json", "repeat": "", "crontab": "", "once": true, "onceDelay": "4", "x": 120, "y": 350, "wires": [ [ "lout_setup_station_start" ] ] }, { "id": "lout_setup_station_start", "type": "link out", "z": "tab_setup", "name": "cmd:station-startup", "mode": "link", "links": [ "lin_station_start" ], "x": 380, "y": 350, "wires": [] } ]