Propagate threshold rename; point platform manual at pumpingStation wiki
Some checks failed
CI / lint-and-test (push) Has been cancelled
Some checks failed
CI / lint-and-test (push) Has been cancelled
Follows pumpingStation@a218945 + generalFunctions@4252292 rename: - Bump pumpingStation and generalFunctions submodule pointers. - Update examples/pumpingstation-3pumps-dashboard/ (build_flow.py, flow.json, README.md) to use the new threshold names. Collapsed minFlowLevel into startLevel; reshuffled order to match the basin bottom-to-top: minLevel, startLevel, maxLevel. - wiki/manuals/README.md: drop the stale pumpingStation.md line and point readers at pumpingStation/wiki instead (docs have moved into the node's own repo). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -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.)
|
||||
|
||||
Reference in New Issue
Block a user