[ { "id": "ps_tab_basic_dashboard", "type": "tab", "label": "PumpingStation Dashboard", "disabled": false, "info": "Basic level-based pumpingStation dashboard with basin trends and safety state." }, { "id": "ui_base_ps_basic", "type": "ui-base", "name": "EVOLV Demo", "path": "/dashboard", "appIcon": "", "includeClientData": true, "acceptsClientConfig": [ "ui-notification", "ui-control" ], "showPathInSidebar": false, "headerContent": "page", "navigationStyle": "default", "titleBarStyle": "default" }, { "id": "ui_theme_ps_basic", "type": "ui-theme", "name": "EVOLV Pumping Theme", "colors": { "surface": "#ffffff", "primary": "#0c99d9", "bgPage": "#f1f3f5", "groupBg": "#ffffff", "groupOutline": "#cfd7de" }, "sizes": { "density": "default", "pagePadding": "14px", "groupGap": "14px", "groupBorderRadius": "6px", "widgetGap": "12px" } }, { "id": "ui_page_ps_basic", "type": "ui-page", "name": "PumpingStation", "ui": "ui_base_ps_basic", "path": "/pumping-station", "icon": "water_drop", "layout": "grid", "theme": "ui_theme_ps_basic", "breakpoints": [ { "name": "Default", "px": "0", "cols": "12" } ], "order": 1, "className": "" }, { "id": "ui_group_ps_inputs", "type": "ui-group", "name": "Simulation Inputs", "page": "ui_page_ps_basic", "width": "4", "height": "1", "order": 1, "showTitle": true, "className": "" }, { "id": "ui_group_ps_trends", "type": "ui-group", "name": "Basin Trends", "page": "ui_page_ps_basic", "width": "8", "height": "1", "order": 2, "showTitle": true, "className": "" }, { "id": "ui_group_ps_state", "type": "ui-group", "name": "State", "page": "ui_page_ps_basic", "width": "12", "height": "1", "order": 3, "showTitle": true, "className": "" }, { "id": "ps_node_basic", "type": "pumpingStation", "z": "ps_tab_basic_dashboard", "name": "PS Dashboard Demo", "basinVolume": 50, "basinHeight": 5, "inflowLevel": 3, "outflowLevel": 0.2, "overflowLevel": 4.5, "defaultFluid": "wastewater", "inletPipeDiameter": 0.4, "outletPipeDiameter": 0.3, "pipelineLength": 80, "maxDischargeHead": 24, "staticHead": 12, "maxInflowRate": 200, "temperatureReferenceDegC": 15, "timeleftToFullOrEmptyThresholdSeconds": 0, "enableDryRunProtection": true, "enableHighVolumeSafety": true, "enableOverfillProtection": true, "dryRunThresholdPercent": 2, "highVolumeSafetyThresholdPercent": 98, "overfillThresholdPercent": 98, "minHeightBasedOn": "outlet", "processOutputFormat": "process", "dbaseOutputFormat": "influxdb", "refHeight": "NAP", "basinBottomRef": 0, "unit": "m3/h", "enableLog": false, "logLevel": "error", "positionVsParent": "atEquipment", "positionIcon": "", "hasDistance": false, "distance": 0, "distanceUnit": "m", "distanceDescription": "", "controlMode": "levelbased", "levelCurveType": "linear", "logCurveFactor": 9, "minLevel": 1, "startLevel": 2, "maxLevel": 4, "x": 720, "y": 260, "wires": [ [ "ps_parse_output" ], [ "ps_debug_influx" ], [ "ps_debug_parent" ] ] }, { "id": "ps_calibrate_initial", "type": "inject", "z": "ps_tab_basic_dashboard", "name": "Set start level 2 m", "props": [ { "p": "topic", "vt": "str" }, { "p": "payload" } ], "repeat": "", "crontab": "", "once": true, "onceDelay": "0.5", "topic": "calibratePredictedLevel", "payload": "2", "payloadType": "num", "x": 180, "y": 180, "wires": [ [ "ps_node_basic" ] ] }, { "id": "ps_auto_inflow", "type": "inject", "z": "ps_tab_basic_dashboard", "name": "Auto inflow 0.008 m3/s", "props": [ { "p": "payload" } ], "repeat": "1", "crontab": "", "once": true, "onceDelay": "1", "topic": "", "payload": "0.008", "payloadType": "num", "x": 180, "y": 240, "wires": [ [ "ps_build_qin" ] ] }, { "id": "ps_inflow_input", "type": "ui-number-input", "z": "ps_tab_basic_dashboard", "group": "ui_group_ps_inputs", "name": "Inflow", "label": "Inflow (m3/s)", "order": 1, "width": "4", "height": "1", "passthru": true, "topic": "", "min": 0, "max": 0.05, "step": 0.001, "x": 190, "y": 300, "wires": [ [ "ps_build_qin" ] ] }, { "id": "ps_build_qin", "type": "function", "z": "ps_tab_basic_dashboard", "name": "Build q_in", "func": "msg.topic = 'q_in';\nmsg.unit = 'm3/s';\nmsg.payload = Number(msg.payload);\nreturn Number.isFinite(msg.payload) ? msg : null;", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 440, "y": 260, "wires": [ [ "ps_node_basic" ] ] }, { "id": "ps_outflow_input", "type": "ui-number-input", "z": "ps_tab_basic_dashboard", "group": "ui_group_ps_inputs", "name": "Outflow", "label": "Outflow (m3/s)", "order": 2, "width": "4", "height": "1", "passthru": true, "topic": "", "min": 0, "max": 0.05, "step": 0.001, "x": 190, "y": 360, "wires": [ [ "ps_build_qout" ] ] }, { "id": "ps_build_qout", "type": "function", "z": "ps_tab_basic_dashboard", "name": "Build q_out", "func": "msg.topic = 'q_out';\nmsg.unit = 'm3/s';\nmsg.payload = Number(msg.payload);\nreturn Number.isFinite(msg.payload) ? msg : null;", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 440, "y": 360, "wires": [ [ "ps_node_basic" ] ] }, { "id": "ps_parse_output", "type": "function", "z": "ps_tab_basic_dashboard", "name": "Parse PS output", "func": "// MeasurementContainer flat keys are `${type}.${variant}.${position}.${childId}`.\n// When PS writes without an explicit .child(), the childId is the literal\n// string 'default' — DON'T strip it. See generalFunctions/src/measurements/\n// MeasurementContainer.js getFlattenedOutput for details.\nconst fields = (msg.payload && typeof msg.payload === 'object') ? msg.payload : {};\nconst snapshot = Object.assign({}, context.get('snapshot') || {}, fields);\ncontext.set('snapshot', snapshot);\nconst firstFinite = (...keys) => {\n for (const key of keys) {\n const value = Number(snapshot[key]);\n if (Number.isFinite(value)) return value;\n }\n return null;\n};\nconst level = firstFinite('level.predicted.atequipment.default', 'level.measured.atequipment.default');\nconst volume = firstFinite('volume.predicted.atequipment.default', 'volume.measured.atequipment.default');\nconst netFlow = firstFinite('netFlowRate.predicted.atequipment.default', 'netFlowRate.measured.atequipment.default');\nconst demand = firstFinite('percControl');\nconst safety = snapshot.safetyState || 'normal';\nconst direction = snapshot.direction || 'unknown';\nconst overflow = snapshot.isOverflowing === true || snapshot.isOverflowing === 'true';\nconst timeleft = Number(snapshot.timeleft);\nconst fmt = (value, digits = 2) => Number.isFinite(value) ? value.toFixed(digits) : '-';\nreturn [\n level == null ? null : { topic: 'level', payload: level },\n volume == null ? null : { topic: 'volume', payload: volume },\n demand == null ? null : { topic: 'demand', payload: demand },\n netFlow == null ? null : { topic: 'net_flow', payload: netFlow },\n { topic: 'safety', payload: `${safety} | overflowing=${overflow}` },\n { topic: 'snapshot', payload: `level=${fmt(level)} m | volume=${fmt(volume)} m3 | demand=${fmt(demand, 0)}% | direction=${direction} | t=${Number.isFinite(timeleft) ? Math.round(timeleft) + ' s' : '-'}` }\n];", "outputs": 6, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 980, "y": 220, "wires": [ [ "ps_chart_level" ], [ "ps_chart_volume" ], [ "ps_chart_demand" ], [ "ps_chart_netflow" ], [ "ps_text_safety" ], [ "ps_text_snapshot" ] ] }, { "id": "ps_chart_level", "type": "ui-chart", "z": "ps_tab_basic_dashboard", "group": "ui_group_ps_trends", "name": "Level", "label": "Level (m)", "order": 1, "width": 4, "height": 4, "chartType": "line", "category": "topic", "xAxisType": "time", "yAxisLabel": "m", "removeOlder": "15", "removeOlderUnit": "60", "x": 1230, "y": 140, "wires": [], "showLegend": false, "categoryType": "msg", "xAxisProperty": "", "xAxisPropertyType": "timestamp", "xAxisFormat": "", "xAxisFormatType": "auto", "yAxisProperty": "payload", "yAxisPropertyType": "msg", "xmin": "", "xmax": "", "ymin": "0", "ymax": "5", "bins": 10, "action": "append", "stackSeries": false, "pointShape": "circle", "pointRadius": 4, "interpolation": "linear", "className": "", "colors": [ "#0c99d9" ], "textColor": [ "#666666" ], "textColorDefault": true, "gridColor": [ "#e5e5e5" ], "gridColorDefault": true }, { "id": "ps_chart_volume", "type": "ui-chart", "z": "ps_tab_basic_dashboard", "group": "ui_group_ps_trends", "name": "Volume", "label": "Volume (m3)", "order": 2, "width": 4, "height": 4, "chartType": "line", "category": "topic", "xAxisType": "time", "yAxisLabel": "m3", "removeOlder": "15", "removeOlderUnit": "60", "x": 1230, "y": 200, "wires": [], "showLegend": false, "categoryType": "msg", "xAxisProperty": "", "xAxisPropertyType": "timestamp", "xAxisFormat": "", "xAxisFormatType": "auto", "yAxisProperty": "payload", "yAxisPropertyType": "msg", "xmin": "", "xmax": "", "ymin": "0", "ymax": "50", "bins": 10, "action": "append", "stackSeries": false, "pointShape": "circle", "pointRadius": 4, "interpolation": "linear", "className": "", "colors": [ "#2ca02c" ], "textColor": [ "#666666" ], "textColorDefault": true, "gridColor": [ "#e5e5e5" ], "gridColorDefault": true }, { "id": "ps_chart_demand", "type": "ui-chart", "z": "ps_tab_basic_dashboard", "group": "ui_group_ps_trends", "name": "Demand", "label": "Demand (%)", "order": 3, "width": 4, "height": 4, "chartType": "line", "category": "topic", "xAxisType": "time", "yAxisLabel": "%", "removeOlder": "15", "removeOlderUnit": "60", "x": 1230, "y": 260, "wires": [], "showLegend": false, "categoryType": "msg", "xAxisProperty": "", "xAxisPropertyType": "timestamp", "xAxisFormat": "", "xAxisFormatType": "auto", "yAxisProperty": "payload", "yAxisPropertyType": "msg", "xmin": "", "xmax": "", "ymin": "0", "ymax": "120", "bins": 10, "action": "append", "stackSeries": false, "pointShape": "circle", "pointRadius": 4, "interpolation": "linear", "className": "", "colors": [ "#d68910" ], "textColor": [ "#666666" ], "textColorDefault": true, "gridColor": [ "#e5e5e5" ], "gridColorDefault": true }, { "id": "ps_chart_netflow", "type": "ui-chart", "z": "ps_tab_basic_dashboard", "group": "ui_group_ps_trends", "name": "Net Flow", "label": "Net flow (m3/s)", "order": 4, "width": 4, "height": 4, "chartType": "line", "category": "topic", "xAxisType": "time", "yAxisLabel": "m3/s", "removeOlder": "15", "removeOlderUnit": "60", "x": 1240, "y": 320, "wires": [], "showLegend": false, "categoryType": "msg", "xAxisProperty": "", "xAxisPropertyType": "timestamp", "xAxisFormat": "", "xAxisFormatType": "auto", "yAxisProperty": "payload", "yAxisPropertyType": "msg", "xmin": "", "xmax": "", "ymin": "", "ymax": "", "bins": 10, "action": "append", "stackSeries": false, "pointShape": "circle", "pointRadius": 4, "interpolation": "linear", "className": "", "colors": [ "#9467bd" ], "textColor": [ "#666666" ], "textColorDefault": true, "gridColor": [ "#e5e5e5" ], "gridColorDefault": true }, { "id": "ps_text_safety", "type": "ui-text", "z": "ps_tab_basic_dashboard", "group": "ui_group_ps_state", "name": "Safety", "label": "Safety", "order": 1, "width": 4, "height": 1, "format": "{{msg.payload}}", "layout": "row-spread", "x": 1230, "y": 380, "wires": [] }, { "id": "ps_text_snapshot", "type": "ui-text", "z": "ps_tab_basic_dashboard", "group": "ui_group_ps_state", "name": "Snapshot", "label": "Snapshot", "order": 2, "width": 8, "height": 1, "format": "{{msg.payload}}", "layout": "row-spread", "x": 1240, "y": 440, "wires": [] }, { "id": "ps_debug_influx", "type": "debug", "z": "ps_tab_basic_dashboard", "name": "Influx output", "active": false, "tosidebar": true, "console": false, "tostatus": false, "complete": "true", "targetType": "full", "x": 980, "y": 320, "wires": [] }, { "id": "ps_debug_parent", "type": "debug", "z": "ps_tab_basic_dashboard", "name": "Parent output", "active": false, "tosidebar": true, "console": false, "tostatus": false, "complete": "true", "targetType": "full", "x": 980, "y": 380, "wires": [] } ]