diff --git a/examples/pumpingstation-3pumps-dashboard/README.md b/examples/pumpingstation-3pumps-dashboard/README.md index b12bd93..74bc8e2 100644 --- a/examples/pumpingstation-3pumps-dashboard/README.md +++ b/examples/pumpingstation-3pumps-dashboard/README.md @@ -91,7 +91,7 @@ The Emergency Stop button always works regardless of mode and uses the new inter ## Notable design choices -- **PS is in `manual` control mode** (`controlMode: "manual"`). The default `levelbased` mode would auto-shut all pumps as soon as basin level dips below `stopLevel` (1 m default), which masks the demo. Manual = observation only. +- **PS is in `manual` control mode** (`controlMode: "manual"`). The default `levelbased` mode would auto-shut all pumps as soon as basin level dips below `minLevel` (1 m default), which masks the demo. Manual = observation only. - **PS safety guards (dry-run / overfill) disabled.** With no real inflow the basin will frequently look "empty" — that's expected for a demo, not a fault. In production you'd configure a real `q_in` source and leave safeties on. - **MGC scaling = `absolute`, mode = `optimalcontrol`.** Set via inject at deploy. Demand in m³/h, BEP-driven distribution. - **demand_router gates Qd ≤ 0.** A demand of 0 would shut every running pump (via MGC.turnOffAllMachines). Use the explicit Stop All button to actually take pumps down. diff --git a/examples/pumpingstation-3pumps-dashboard/build_flow.py b/examples/pumpingstation-3pumps-dashboard/build_flow.py index 30bb542..50a3b10 100644 --- a/examples/pumpingstation-3pumps-dashboard/build_flow.py +++ b/examples/pumpingstation-3pumps-dashboard/build_flow.py @@ -52,6 +52,13 @@ LANE_X = [120, 380, 640, 900, 1160, 1420] ROW = 80 # standard inter-row pitch SECTION_GAP = 200 # additional shift between major sections +# Position icon map — must match generalFunctions/src/menu/physicalPosition.js +POSITION_ICON = { + "upstream": "\u2192", # → + "downstream": "\u2190", # ← + "atEquipment": "\u22a5", # ⊥ +} + # --------------------------------------------------------------------------- # Cross-tab link channel names (the wiring contract) @@ -356,9 +363,14 @@ def build_process_tab(): )) # Two measurement sensors (upstream + downstream) + # Static pressures: upstream ~100 mbar (basin hydrostatic), + # downstream ~1300 mbar (discharge head ≈ 12 m). + # Differential = 1200 mbar = 120 kPa — mid-range on the + # hidrostal curve (700–3900 mbar). Simulator OFF so the + # value is stable and predictable. for j, pos in enumerate(("upstream", "downstream")): mid = f"meas_{pump}_{pos[0]}" - absmin, absmax = (50, 400) if pos == "upstream" else (800, 2200) + static_val = 100 if pos == "upstream" else 1300 mid_label = f"PT-{label.split()[1]}-{'Up' if pos == 'upstream' else 'Dn'}" nodes.append({ "id": mid, "type": "measurement", "z": TAB_PROCESS, @@ -366,7 +378,7 @@ def build_process_tab(): "mode": "analog", "channels": "[]", "scaling": False, "i_min": 0, "i_max": 1, "i_offset": 0, - "o_min": absmin, "o_max": absmax, + "o_min": static_val, "o_max": static_val, "simulator": True, "smooth_method": "mean", "count": "5", "processOutputFormat": "process", "dbaseOutputFormat": "influxdb", @@ -375,7 +387,7 @@ def build_process_tab(): "assetType": "pressure", "model": "vega-pressure-10", "unit": "mbar", "assetTagNumber": f"PT-{i+1}-{pos[0].upper()}", "enableLog": False, "logLevel": "warn", - "positionVsParent": pos, "positionIcon": "", + "positionVsParent": pos, "positionIcon": POSITION_ICON.get(pos, ""), "hasDistance": False, "distance": 0, "distanceUnit": "m", "distanceDescription": "", "x": LANE_X[1], "y": y_section + 40 + j * 50, @@ -424,7 +436,7 @@ def build_process_tab(): "curvePressureUnit": "mbar", "curveFlowUnit": "m3/h", "curvePowerUnit": "kW", "curveControlUnit": "%", "enableLog": False, "logLevel": "warn", - "positionVsParent": "atEquipment", "positionIcon": "", + "positionVsParent": "atEquipment", "positionIcon": POSITION_ICON["atEquipment"], "hasDistance": False, "distance": 0, "distanceUnit": "m", "distanceDescription": "", "x": LANE_X[3], "y": y_section + 80, @@ -493,7 +505,7 @@ def build_process_tab(): "assetType": "machinegroupcontrol", "model": "default", "unit": "m3/h", "supplier": "evolv", "enableLog": False, "logLevel": "warn", - "positionVsParent": "atEquipment", "positionIcon": "", + "positionVsParent": "atEquipment", "positionIcon": POSITION_ICON["atEquipment"], "hasDistance": False, "distance": 0, "distanceUnit": "m", "distanceDescription": "", "processOutputFormat": "process", "dbaseOutputFormat": "influxdb", @@ -572,7 +584,7 @@ def build_process_tab(): "category": "station", "assetType": "pumpingstation", "model": "default", "unit": "m3/s", "supplier": "evolv", "enableLog": False, "logLevel": "warn", - "positionVsParent": "atEquipment", "positionIcon": "", + "positionVsParent": "atEquipment", "positionIcon": POSITION_ICON["atEquipment"], "hasDistance": False, "distance": 0, "distanceUnit": "m", "distanceDescription": "", "processOutputFormat": "process", "dbaseOutputFormat": "influxdb", @@ -586,19 +598,18 @@ def build_process_tab(): "controlMode": "levelbased", "basinVolume": 30, "basinHeight": 4, - "heightInlet": 3.5, - "heightOutlet": 0.3, - "heightOverflow": 3.8, + "inflowLevel": 3.5, + "outflowLevel": 0.3, + "overflowLevel": 3.8, "inletPipeDiameter": 0.3, "outletPipeDiameter": 0.3, # Level-based control thresholds - "startLevel": 2.0, # pumps ON above 2.0 m (50% of height) - "stopLevel": 1.0, # pumps OFF below 1.0 m (25% of height) - "minFlowLevel": 2.0, # 0% pump demand at startLevel (must match startLevel!) - "maxFlowLevel": 3.5, # 100% pump demand at this level + "minLevel": 1.0, # pumps OFF below 1.0 m (25% of height) + "startLevel": 2.0, # 0% pump demand — ramp begins here + "maxLevel": 3.5, # 100% pump demand at this level # Hydraulics "refHeight": "NAP", - "minHeightBasedOn": "inlet", + "minHeightBasedOn": "outlet", # basin drains to outlet pipe (0.3 m), not inlet "basinBottomRef": 0, "staticHead": 12, "maxDischargeHead": 24, @@ -631,15 +642,16 @@ def build_process_tab(): "}\n" "const lvl = find('level.predicted.');\n" "const vol = find('volume.predicted.');\n" - "const qIn = find('flow.measured.upstream.') || find('flow.measured.in.');\n" - "const qOut = find('flow.measured.downstream.') || find('flow.measured.out.');\n" + "const qIn = find('flow.predicted.in.');\n" + "const qOut = find('flow.predicted.out.');\n" + "const netFlowRate = find('netFlowRate.predicted.');\n" "// Compute derived metrics\n" "// Basin capacity = basinVolume (config). Don't hardcode — read it once.\n" "if (!context.get('maxVol')) context.set('maxVol', 30.0); // basinVolume from PS config\n" "const maxVol = context.get('maxVol');\n" "const fillPct = vol != null ? Math.min(100, Math.max(0, Math.round(Number(vol) / maxVol * 100))) : null;\n" - "const netM3h = (c.netFlow != null) ? Number(c.netFlow) * 3600 : null;\n" - "const seconds = (c.seconds != null && Number.isFinite(Number(c.seconds))) ? Number(c.seconds) : null;\n" + "const netM3h = netFlowRate != null ? Number(netFlowRate) * 3600 : null;\n" + "const seconds = (c.timeleft != null && Number.isFinite(Number(c.timeleft))) ? Number(c.timeleft) : null;\n" "const timeStr = seconds != null ? (seconds > 60 ? Math.round(seconds/60) + ' min' : Math.round(seconds) + ' s') : 'n/a';\n" "msg.payload = {\n" " direction: c.direction || 'steady',\n" @@ -655,6 +667,9 @@ def build_process_tab(): " volumeNum: vol != null ? Number(vol) : null,\n" " fillPctNum: fillPct,\n" " netFlowNum: netM3h,\n" + " percControl: c.percControl != null ? Number(c.percControl) : null,\n" + " qInNum: qIn != null ? Number(qIn) * 3600 : null,\n" + " qOutNum: qOut != null ? Number(qOut) * 3600 : null,\n" "};\n" "return msg;", outputs=1, wires=[["lout_evt_ps"]], @@ -921,8 +936,11 @@ def build_ui_tab(): " 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" + " p.percControl != null ? {topic: 'PS demand', payload: p.percControl, timestamp: ts} : null,\n" + " p.qInNum != null ? {topic: 'Inflow', payload: p.qInNum, timestamp: ts} : null,\n" + " p.qOutNum != null ? {topic: 'Outflow', payload: p.qOutNum, timestamp: ts} : null,\n" "];", - outputs=10, + outputs=13, wires=[ ["ui_ps_direction"], ["ui_ps_level"], @@ -934,7 +952,10 @@ def build_ui_tab(): # Trend + gauge outputs — split level and fill to separate charts ["trend_short_fill", "trend_long_fill", "gauge_ps_fill", "gauge_ps_fill_long"], # fill % → fill chart + gauges ["trend_short_level", "trend_long_level", "gauge_ps_level", "gauge_ps_level_long"], # level → level chart + gauges - ["trend_short_level", "trend_long_level"], # net flow → level chart (shared axis) + ["trend_short_flow", "trend_long_flow"], # net flow → flow charts + ["trend_short_fill", "trend_long_fill"], # percControl (%) → fill charts (same y-axis) + ["trend_short_flow", "trend_long_flow"], # inflow m3/h → flow charts + ["trend_short_flow", "trend_long_flow"], # outflow m3/h → flow charts ], )) @@ -1118,11 +1139,11 @@ def build_ui_tab(): # ===== Basin charts + gauges (fill %, level, net flow) ===== # Gauge segment definitions (reused for both pages) TANK_SEGMENTS = [ - {"color": "#f44336", "from": 0}, # red: below stopLevel (1.0 m) + {"color": "#f44336", "from": 0}, # red: below minLevel (1.0 m) {"color": "#ff9800", "from": 1.0}, # orange: between stop and start {"color": "#2196f3", "from": 2.0}, # blue: normal operating (startLevel) {"color": "#ff9800", "from": 3.5}, # orange: approaching overflow - {"color": "#f44336", "from": 3.8}, # red: overflow zone (heightOverflow) + {"color": "#f44336", "from": 3.8}, # red: overflow zone (overflowLevel) ] FILL_SEGMENTS = [ {"color": "#f44336", "from": 0}, @@ -1314,19 +1335,8 @@ def build_setup_tab(): CH_MODE, target_in_ids=["lin_mode"] )) - y = 350 - nodes.append(inject( - "setup_pumps_startup", TAB_SETUP, LANE_X[0], y, - "auto-startup all pumps", - topic="execSequence", - payload='{"source":"GUI","action":"execSequence","parameter":"startup"}', - payload_type="json", once=True, once_delay="4", - wires=["lout_setup_station_start"] - )) - nodes.append(link_out( - "lout_setup_station_start", TAB_SETUP, LANE_X[1], y, - CH_STATION_START, target_in_ids=["lin_station_start"] - )) + # Auto-startup removed: the MGC starts pumps on demand from the PS. + # Starting pumps before the PS requests them causes flow below startLevel. # (Random demand removed — sinus inflow drives the demo automatically. # No explicit "random on" inject needed.) diff --git a/examples/pumpingstation-3pumps-dashboard/flow.json b/examples/pumpingstation-3pumps-dashboard/flow.json index fbdabe2..9c3b2cd 100644 --- a/examples/pumpingstation-3pumps-dashboard/flow.json +++ b/examples/pumpingstation-3pumps-dashboard/flow.json @@ -37,8 +37,8 @@ "i_min": 0, "i_max": 1, "i_offset": 0, - "o_min": 50, - "o_max": 400, + "o_min": 100, + "o_max": 100, "simulator": true, "smooth_method": "mean", "count": "5", @@ -54,7 +54,7 @@ "enableLog": false, "logLevel": "warn", "positionVsParent": "upstream", - "positionIcon": "", + "positionIcon": "\u2192", "hasDistance": false, "distance": 0, "distanceUnit": "m", @@ -80,8 +80,8 @@ "i_min": 0, "i_max": 1, "i_offset": 0, - "o_min": 800, - "o_max": 2200, + "o_min": 1300, + "o_max": 1300, "simulator": true, "smooth_method": "mean", "count": "5", @@ -97,7 +97,7 @@ "enableLog": false, "logLevel": "warn", "positionVsParent": "downstream", - "positionIcon": "", + "positionIcon": "\u2190", "hasDistance": false, "distance": 0, "distanceUnit": "m", @@ -188,7 +188,7 @@ "enableLog": false, "logLevel": "warn", "positionVsParent": "atEquipment", - "positionIcon": "", + "positionIcon": "\u22a5", "hasDistance": false, "distance": 0, "distanceUnit": "m", @@ -258,8 +258,8 @@ "i_min": 0, "i_max": 1, "i_offset": 0, - "o_min": 50, - "o_max": 400, + "o_min": 100, + "o_max": 100, "simulator": true, "smooth_method": "mean", "count": "5", @@ -275,7 +275,7 @@ "enableLog": false, "logLevel": "warn", "positionVsParent": "upstream", - "positionIcon": "", + "positionIcon": "\u2192", "hasDistance": false, "distance": 0, "distanceUnit": "m", @@ -301,8 +301,8 @@ "i_min": 0, "i_max": 1, "i_offset": 0, - "o_min": 800, - "o_max": 2200, + "o_min": 1300, + "o_max": 1300, "simulator": true, "smooth_method": "mean", "count": "5", @@ -318,7 +318,7 @@ "enableLog": false, "logLevel": "warn", "positionVsParent": "downstream", - "positionIcon": "", + "positionIcon": "\u2190", "hasDistance": false, "distance": 0, "distanceUnit": "m", @@ -409,7 +409,7 @@ "enableLog": false, "logLevel": "warn", "positionVsParent": "atEquipment", - "positionIcon": "", + "positionIcon": "\u22a5", "hasDistance": false, "distance": 0, "distanceUnit": "m", @@ -479,8 +479,8 @@ "i_min": 0, "i_max": 1, "i_offset": 0, - "o_min": 50, - "o_max": 400, + "o_min": 100, + "o_max": 100, "simulator": true, "smooth_method": "mean", "count": "5", @@ -496,7 +496,7 @@ "enableLog": false, "logLevel": "warn", "positionVsParent": "upstream", - "positionIcon": "", + "positionIcon": "\u2192", "hasDistance": false, "distance": 0, "distanceUnit": "m", @@ -522,8 +522,8 @@ "i_min": 0, "i_max": 1, "i_offset": 0, - "o_min": 800, - "o_max": 2200, + "o_min": 1300, + "o_max": 1300, "simulator": true, "smooth_method": "mean", "count": "5", @@ -539,7 +539,7 @@ "enableLog": false, "logLevel": "warn", "positionVsParent": "downstream", - "positionIcon": "", + "positionIcon": "\u2190", "hasDistance": false, "distance": 0, "distanceUnit": "m", @@ -630,7 +630,7 @@ "enableLog": false, "logLevel": "warn", "positionVsParent": "atEquipment", - "positionIcon": "", + "positionIcon": "\u22a5", "hasDistance": false, "distance": 0, "distanceUnit": "m", @@ -703,7 +703,7 @@ "enableLog": false, "logLevel": "warn", "positionVsParent": "atEquipment", - "positionIcon": "", + "positionIcon": "\u22a5", "hasDistance": false, "distance": 0, "distanceUnit": "m", @@ -845,7 +845,7 @@ "enableLog": false, "logLevel": "warn", "positionVsParent": "atEquipment", - "positionIcon": "", + "positionIcon": "\u22a5", "hasDistance": false, "distance": 0, "distanceUnit": "m", @@ -855,17 +855,16 @@ "controlMode": "levelbased", "basinVolume": 30, "basinHeight": 4, - "heightInlet": 3.5, - "heightOutlet": 0.3, - "heightOverflow": 3.8, + "inflowLevel": 3.5, + "outflowLevel": 0.3, + "overflowLevel": 3.8, "inletPipeDiameter": 0.3, "outletPipeDiameter": 0.3, + "minLevel": 1.0, "startLevel": 2.0, - "stopLevel": 1.0, - "minFlowLevel": 2.0, - "maxFlowLevel": 3.5, + "maxLevel": 3.5, "refHeight": "NAP", - "minHeightBasedOn": "inlet", + "minHeightBasedOn": "outlet", "basinBottomRef": 0, "staticHead": 12, "maxDischargeHead": 24, @@ -892,7 +891,7 @@ "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\n// Basin capacity = basinVolume (config). Don't hardcode \u2014 read it once.\nif (!context.get('maxVol')) context.set('maxVol', 30.0); // basinVolume from PS config\nconst maxVol = context.get('maxVol');\nconst fillPct = vol != null ? Math.min(100, Math.max(0, 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;", + "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.predicted.in.');\nconst qOut = find('flow.predicted.out.');\nconst netFlowRate = find('netFlowRate.predicted.');\n// Compute derived metrics\n// Basin capacity = basinVolume (config). Don't hardcode \u2014 read it once.\nif (!context.get('maxVol')) context.set('maxVol', 30.0); // basinVolume from PS config\nconst maxVol = context.get('maxVol');\nconst fillPct = vol != null ? Math.min(100, Math.max(0, Math.round(Number(vol) / maxVol * 100))) : null;\nconst netM3h = netFlowRate != null ? Number(netFlowRate) * 3600 : null;\nconst seconds = (c.timeleft != null && Number.isFinite(Number(c.timeleft))) ? Number(c.timeleft) : 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 percControl: c.percControl != null ? Number(c.percControl) : null,\n qInNum: qIn != null ? Number(qIn) * 3600 : null,\n qOutNum: qOut != null ? Number(qOut) * 3600 : null,\n};\nreturn msg;", "outputs": 1, "noerr": 0, "initialize": "", @@ -1896,8 +1895,8 @@ "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, + "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 p.percControl != null ? {topic: 'PS demand', payload: p.percControl, timestamp: ts} : null,\n p.qInNum != null ? {topic: 'Inflow', payload: p.qInNum, timestamp: ts} : null,\n p.qOutNum != null ? {topic: 'Outflow', payload: p.qOutNum, timestamp: ts} : null,\n];", + "outputs": 13, "noerr": 0, "initialize": "", "finalize": "", @@ -1939,8 +1938,20 @@ "gauge_ps_level_long" ], [ - "trend_short_level", - "trend_long_level" + "trend_short_flow", + "trend_long_flow" + ], + [ + "trend_short_fill", + "trend_long_fill" + ], + [ + "trend_short_flow", + "trend_long_flow" + ], + [ + "trend_short_flow", + "trend_long_flow" ] ] }, @@ -4101,49 +4112,5 @@ "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": [] } ] diff --git a/nodes/generalFunctions b/nodes/generalFunctions index 693517c..4252292 160000 --- a/nodes/generalFunctions +++ b/nodes/generalFunctions @@ -1 +1 @@ -Subproject commit 693517cc8f6c90dea9c9da369948e5128443185e +Subproject commit 4252292ae1b19568e8a4b54ab125769b2eae39f7 diff --git a/nodes/pumpingStation b/nodes/pumpingStation index 5e2ebe4..a218945 160000 --- a/nodes/pumpingStation +++ b/nodes/pumpingStation @@ -1 +1 @@ -Subproject commit 5e2ebe4d96d0b925a17007804815ed893916942f +Subproject commit a2189457f6515a42db50bdc8f55c0da20d45b675 diff --git a/wiki/manuals/README.md b/wiki/manuals/README.md index 2a65569..bbf3518 100644 --- a/wiki/manuals/README.md +++ b/wiki/manuals/README.md @@ -8,3 +8,7 @@ Local reference manuals used by EVOLV agents while implementing and reviewing No - `manuals/node-red/messages-and-editor-structure.md`: Message shape and HTML/editor/runtime contracts. - `manuals/node-red/flowfuse-ui-chart-manual.md`: FlowFuse `ui-chart` data contract and runtime controls. - `manuals/node-red/flowfuse-dashboard-layout-manual.md`: Compact FlowFuse dashboard layout guidance. +- `manuals/nodes/rotatingMachine.md`: rotatingMachine node — operator reference (editor config, topics, ports, state machine, pressure/curve predictions). +- `manuals/nodes/measurement.md`: measurement node — operator reference (analog/digital modes, pipeline, smoothing/outliers). + +Note: pumpingStation documentation has moved into its own repo's `wiki/` folder — see [pumpingStation/wiki](https://gitea.wbd-rd.nl/RnD/pumpingStation/src/branch/main/wiki).