diff --git a/examples/basic-dashboard.flow.json b/examples/basic-dashboard.flow.json new file mode 100644 index 0000000..7323bce --- /dev/null +++ b/examples/basic-dashboard.flow.json @@ -0,0 +1,589 @@ +[ + { + "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": "const 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', 'level.measured.atequipment');\nconst volume = firstFinite('volume.predicted.atequipment', 'volume.measured.atequipment');\nconst netFlow = firstFinite('netFlowRate.predicted.atequipment', 'netFlowRate.measured.atequipment');\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": [] + } +] diff --git a/pumpingStation.html b/pumpingStation.html index 216f660..92f664b 100644 --- a/pumpingStation.html +++ b/pumpingStation.html @@ -8,8 +8,17 @@ | **Control Module** | `#a9daee` | zwart | --> - - + + + + + + + + + +