Compare commits
5 Commits
90536d631d
...
8a26e17780
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8a26e17780 | ||
|
|
3cd749bf37 | ||
|
|
70151e52ec | ||
|
|
b3972d4a2f | ||
|
|
3529c9f970 |
@@ -21,49 +21,26 @@
|
||||
"links": [],
|
||||
"panels": [
|
||||
{
|
||||
"gridPos": {
|
||||
"h": 1,
|
||||
"w": 24,
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"gridPos": { "h": 1, "w": 24, "x": 0, "y": 0 },
|
||||
"id": 1,
|
||||
"title": "Status",
|
||||
"type": "row"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "influxdb",
|
||||
"uid": "cdzg44tv250jkd"
|
||||
},
|
||||
"datasource": { "type": "influxdb", "uid": "cdzg44tv250jkd" },
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "blue",
|
||||
"value": null
|
||||
}
|
||||
]
|
||||
"steps": [{ "color": "blue", "value": null }]
|
||||
}
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 4,
|
||||
"w": 5,
|
||||
"x": 0,
|
||||
"y": 1
|
||||
},
|
||||
"gridPos": { "h": 4, "w": 6, "x": 0, "y": 1 },
|
||||
"id": 2,
|
||||
"options": {
|
||||
"reduceOptions": {
|
||||
"calcs": [
|
||||
"lastNotNull"
|
||||
],
|
||||
"fields": "/.*/"
|
||||
},
|
||||
"reduceOptions": { "calcs": ["lastNotNull"], "fields": "/.*/" },
|
||||
"colorMode": "value",
|
||||
"graphMode": "none"
|
||||
},
|
||||
@@ -75,53 +52,28 @@
|
||||
],
|
||||
"title": "Direction",
|
||||
"type": "stat",
|
||||
"meta": {
|
||||
"emittedFields": [
|
||||
"direction"
|
||||
]
|
||||
}
|
||||
"meta": { "emittedFields": ["direction"] }
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "influxdb",
|
||||
"uid": "cdzg44tv250jkd"
|
||||
},
|
||||
"datasource": { "type": "influxdb", "uid": "cdzg44tv250jkd" },
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"unit": "s",
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green",
|
||||
"value": null
|
||||
},
|
||||
{
|
||||
"color": "orange",
|
||||
"value": 300
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 600
|
||||
}
|
||||
{ "color": "green", "value": null },
|
||||
{ "color": "orange", "value": 300 },
|
||||
{ "color": "red", "value": 600 }
|
||||
]
|
||||
}
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 4,
|
||||
"w": 5,
|
||||
"x": 5,
|
||||
"y": 1
|
||||
},
|
||||
"gridPos": { "h": 4, "w": 6, "x": 6, "y": 1 },
|
||||
"id": 3,
|
||||
"options": {
|
||||
"reduceOptions": {
|
||||
"calcs": [
|
||||
"lastNotNull"
|
||||
]
|
||||
},
|
||||
"reduceOptions": { "calcs": ["lastNotNull"] },
|
||||
"colorMode": "value",
|
||||
"graphMode": "area"
|
||||
},
|
||||
@@ -133,45 +85,23 @@
|
||||
],
|
||||
"title": "Time Left",
|
||||
"type": "stat",
|
||||
"meta": {
|
||||
"emittedFields": [
|
||||
"timeLeft"
|
||||
]
|
||||
}
|
||||
"meta": { "emittedFields": ["timeLeft"] }
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "influxdb",
|
||||
"uid": "cdzg44tv250jkd"
|
||||
},
|
||||
"datasource": { "type": "influxdb", "uid": "cdzg44tv250jkd" },
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "purple",
|
||||
"value": null
|
||||
}
|
||||
]
|
||||
"steps": [{ "color": "purple", "value": null }]
|
||||
}
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 4,
|
||||
"w": 4,
|
||||
"x": 10,
|
||||
"y": 1
|
||||
},
|
||||
"gridPos": { "h": 4, "w": 6, "x": 12, "y": 1 },
|
||||
"id": 4,
|
||||
"options": {
|
||||
"reduceOptions": {
|
||||
"calcs": [
|
||||
"lastNotNull"
|
||||
],
|
||||
"fields": "/.*/"
|
||||
},
|
||||
"reduceOptions": { "calcs": ["lastNotNull"], "fields": "/.*/" },
|
||||
"colorMode": "value",
|
||||
"graphMode": "none"
|
||||
},
|
||||
@@ -183,172 +113,322 @@
|
||||
],
|
||||
"title": "Flow Source",
|
||||
"type": "stat",
|
||||
"meta": {
|
||||
"emittedFields": [
|
||||
"flowSource"
|
||||
]
|
||||
}
|
||||
"meta": { "emittedFields": ["flowSource"] }
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "influxdb",
|
||||
"uid": "cdzg44tv250jkd"
|
||||
},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"min": 0,
|
||||
"max": 100,
|
||||
"unit": "percent",
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "red",
|
||||
"value": null
|
||||
},
|
||||
{
|
||||
"color": "orange",
|
||||
"value": 20
|
||||
},
|
||||
{
|
||||
"color": "green",
|
||||
"value": 40
|
||||
},
|
||||
{
|
||||
"color": "orange",
|
||||
"value": 80
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 95
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 4,
|
||||
"w": 5,
|
||||
"x": 14,
|
||||
"y": 1
|
||||
},
|
||||
"id": 5,
|
||||
"options": {
|
||||
"reduceOptions": {
|
||||
"calcs": [
|
||||
"lastNotNull"
|
||||
]
|
||||
},
|
||||
"showThresholdLabels": false,
|
||||
"showThresholdMarkers": true
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"query": "from(bucket: \"${bucket}\")\n |> range(start: -7d)\n |> filter(fn:(r) => r._measurement==\"${measurement}\" and r._field =~ /^volumePercent\\.predicted\\.atequipment/)\n |> group(columns:[\"_field\"])\n |> last()",
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"title": "Fill %",
|
||||
"type": "gauge",
|
||||
"meta": {
|
||||
"emittedFields": [
|
||||
"volumePercent"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "influxdb",
|
||||
"uid": "cdzg44tv250jkd"
|
||||
},
|
||||
"datasource": { "type": "influxdb", "uid": "cdzg44tv250jkd" },
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"unit": "lengthm",
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green",
|
||||
"value": null
|
||||
}
|
||||
]
|
||||
"steps": [{ "color": "green", "value": null }]
|
||||
}
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 4,
|
||||
"w": 5,
|
||||
"x": 19,
|
||||
"y": 1
|
||||
},
|
||||
"gridPos": { "h": 4, "w": 6, "x": 18, "y": 1 },
|
||||
"id": 6,
|
||||
"options": {
|
||||
"reduceOptions": {
|
||||
"calcs": [
|
||||
"lastNotNull"
|
||||
]
|
||||
},
|
||||
"reduceOptions": { "calcs": ["lastNotNull"] },
|
||||
"colorMode": "value",
|
||||
"graphMode": "area"
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"query": "from(bucket: \"${bucket}\")\n |> range(start: -7d)\n |> filter(fn:(r) => r._measurement==\"${measurement}\" and r._field =~ /^level\\.predicted\\.atequipment/)\n |> group(columns:[\"_field\"])\n |> last()",
|
||||
"query": "from(bucket: \"${bucket}\")\n |> range(start: -7d)\n |> filter(fn:(r) => r._measurement==\"${measurement}\" and r._field =~ /^level\\.predicted\\.atequipment/)\n |> group(columns:[\"_field\"])\n |> last()\n |> keep(columns:[\"_value\"])",
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"title": "Level",
|
||||
"type": "stat",
|
||||
"meta": {
|
||||
"emittedFields": [
|
||||
"level"
|
||||
]
|
||||
}
|
||||
"meta": { "emittedFields": ["level"] }
|
||||
},
|
||||
{
|
||||
"gridPos": {
|
||||
"h": 1,
|
||||
"w": 24,
|
||||
"x": 0,
|
||||
"y": 5
|
||||
},
|
||||
"id": 7,
|
||||
"gridPos": { "h": 1, "w": 24, "x": 0, "y": 5 },
|
||||
"id": 13,
|
||||
"title": "Basin",
|
||||
"type": "row"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "influxdb",
|
||||
"uid": "cdzg44tv250jkd"
|
||||
},
|
||||
"datasource": { "type": "influxdb", "uid": "cdzg44tv250jkd" },
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"unit": "lengthm",
|
||||
"custom": {
|
||||
"drawStyle": "line",
|
||||
"lineWidth": 2,
|
||||
"fillOpacity": 10
|
||||
"min": 0,
|
||||
"max": {{heightBasin}},
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{ "color": "#3a3a3a", "value": null },
|
||||
{ "color": "semi-dark-grey", "value": {{outflowLevel}} },
|
||||
{ "color": "blue", "value": {{dryRunLevel}} },
|
||||
{ "color": "green", "value": {{inflowLevel}} },
|
||||
{ "color": "orange", "value": {{highSafetyLevel}} },
|
||||
{ "color": "red", "value": {{overflowLevel}} }
|
||||
]
|
||||
}
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 8,
|
||||
"w": 12,
|
||||
"x": 0,
|
||||
"y": 6
|
||||
"gridPos": { "h": 20, "w": 4, "x": 0, "y": 6 },
|
||||
"id": 16,
|
||||
"options": {
|
||||
"displayMode": "basic",
|
||||
"orientation": "vertical",
|
||||
"reduceOptions": { "calcs": ["lastNotNull"], "fields": "" },
|
||||
"showThresholdLabels": true,
|
||||
"showThresholdMarkers": true,
|
||||
"showUnfilled": true,
|
||||
"minVizWidth": 8,
|
||||
"minVizHeight": 16,
|
||||
"valueMode": "color",
|
||||
"namePlacement": "auto"
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"query": "from(bucket: \"${bucket}\")\n |> range(start: -7d)\n |> filter(fn:(r) => r._measurement==\"${measurement}\" and r._field =~ /^level\\.predicted\\.atequipment/)\n |> group(columns:[\"_field\"])\n |> last()\n |> keep(columns:[\"_value\"])",
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"title": "Water Level",
|
||||
"type": "bargauge",
|
||||
"meta": { "emittedFields": ["basinLevel"] }
|
||||
},
|
||||
{
|
||||
"datasource": { "type": "influxdb", "uid": "cdzg44tv250jkd" },
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"unit": "none",
|
||||
"decimals": 2
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"matcher": { "id": "byRegexp", "options": "^(outflowLevel|inflowLevel|overflowLevel|heightBasin|dryRunLevel|highVolumeSafetyLevel|level)$" },
|
||||
"properties": [{ "id": "unit", "value": "lengthm" }, { "id": "decimals", "value": 2 }]
|
||||
},
|
||||
{
|
||||
"matcher": { "id": "byRegexp", "options": "^(volume|maxVol|minVol|maxVolAtOverflow|minVolAtOutflow|minVolAtInflow)$" },
|
||||
"properties": [{ "id": "unit", "value": "m3" }, { "id": "decimals", "value": 2 }]
|
||||
},
|
||||
{
|
||||
"matcher": { "id": "byRegexp", "options": "^volumePercent$" },
|
||||
"properties": [{ "id": "unit", "value": "percent" }, { "id": "decimals", "value": 1 }]
|
||||
}
|
||||
]
|
||||
},
|
||||
"gridPos": { "h": 20, "w": 6, "x": 4, "y": 6 },
|
||||
"id": 17,
|
||||
"options": {
|
||||
"inlineEditing": false,
|
||||
"showAdvancedTypes": true,
|
||||
"panZoom": false,
|
||||
"infinitePan": false,
|
||||
"root": {
|
||||
"name": "Basin",
|
||||
"type": "frame",
|
||||
"placement": { "left": 0, "top": 0, "width": 400, "height": 760 },
|
||||
"background": { "color": { "fixed": "transparent" } },
|
||||
"border": { "color": { "fixed": "dark-green" } },
|
||||
"elements": [
|
||||
{
|
||||
"name": "Zone Spill",
|
||||
"type": "rectangle",
|
||||
"placement": { "top": 20, "left": 10, "width": 380, "height": {{h_spill}} },
|
||||
"background": { "color": { "fixed": "rgba(229, 67, 67, 0.18)" } },
|
||||
"border": { "color": { "fixed": "transparent" }, "width": 0 },
|
||||
"config": { "text": { "mode": "fixed", "fixed": "" } }
|
||||
},
|
||||
{
|
||||
"name": "Zone HighSafety",
|
||||
"type": "rectangle",
|
||||
"placement": { "top": {{y_overflow}}, "left": 10, "width": 380, "height": {{h_highSafety}} },
|
||||
"background": { "color": { "fixed": "rgba(242, 165, 67, 0.16)" } },
|
||||
"border": { "color": { "fixed": "transparent" }, "width": 0 },
|
||||
"config": { "text": { "mode": "fixed", "fixed": "" } }
|
||||
},
|
||||
{
|
||||
"name": "Zone Operating",
|
||||
"type": "rectangle",
|
||||
"placement": { "top": {{y_highSafety}}, "left": 10, "width": 380, "height": {{h_operating}} },
|
||||
"background": { "color": { "fixed": "rgba(95, 179, 122, 0.14)" } },
|
||||
"border": { "color": { "fixed": "transparent" }, "width": 0 },
|
||||
"config": { "text": { "mode": "fixed", "fixed": "" } }
|
||||
},
|
||||
{
|
||||
"name": "Zone Dead",
|
||||
"type": "rectangle",
|
||||
"placement": { "top": {{y_outflow}}, "left": 10, "width": 380, "height": {{h_dead}} },
|
||||
"background": { "color": { "fixed": "rgba(128, 128, 128, 0.20)" } },
|
||||
"border": { "color": { "fixed": "transparent" }, "width": 0 },
|
||||
"config": { "text": { "mode": "fixed", "fixed": "" } }
|
||||
},
|
||||
{
|
||||
"name": "Tank Outline",
|
||||
"type": "rectangle",
|
||||
"placement": { "top": 20, "left": 10, "width": 380, "height": 680 },
|
||||
"background": { "color": { "fixed": "transparent" } },
|
||||
"border": { "color": { "fixed": "#8a8a8a" }, "width": 2 },
|
||||
"config": { "text": { "mode": "fixed", "fixed": "" } }
|
||||
},
|
||||
{
|
||||
"name": "Line Overflow",
|
||||
"type": "rectangle",
|
||||
"placement": { "top": {{y_overflow}}, "left": 10, "width": 380, "height": 1 },
|
||||
"background": { "color": { "fixed": "#e54343" } },
|
||||
"border": { "color": { "fixed": "#e54343" }, "width": 0 }
|
||||
},
|
||||
{
|
||||
"name": "Line HighSafety",
|
||||
"type": "rectangle",
|
||||
"placement": { "top": {{y_highSafety}}, "left": 10, "width": 380, "height": 1 },
|
||||
"background": { "color": { "fixed": "#f2a543" } },
|
||||
"border": { "color": { "fixed": "#f2a543" }, "width": 0 }
|
||||
},
|
||||
{
|
||||
"name": "Line Inflow",
|
||||
"type": "rectangle",
|
||||
"placement": { "top": {{y_inflow}}, "left": 10, "width": 380, "height": 1 },
|
||||
"background": { "color": { "fixed": "#5fb37a" } },
|
||||
"border": { "color": { "fixed": "#5fb37a" }, "width": 0 }
|
||||
},
|
||||
{
|
||||
"name": "Line DryRun",
|
||||
"type": "rectangle",
|
||||
"placement": { "top": {{y_dryRun}}, "left": 10, "width": 380, "height": 1 },
|
||||
"background": { "color": { "fixed": "#5b9bd5" } },
|
||||
"border": { "color": { "fixed": "#5b9bd5" }, "width": 0 }
|
||||
},
|
||||
{
|
||||
"name": "Line Outflow",
|
||||
"type": "rectangle",
|
||||
"placement": { "top": {{y_outflow}}, "left": 10, "width": 380, "height": 1 },
|
||||
"background": { "color": { "fixed": "#bfbfbf" } },
|
||||
"border": { "color": { "fixed": "#bfbfbf" }, "width": 0 }
|
||||
},
|
||||
{
|
||||
"name": "Label Overflow Name",
|
||||
"type": "text",
|
||||
"placement": { "top": {{ty_overflow}}, "left": 180, "width": 140, "height": 16 },
|
||||
"background": { "color": { "fixed": "transparent" } },
|
||||
"border": { "color": { "fixed": "transparent" }, "width": 0 },
|
||||
"config": { "text": { "mode": "fixed", "fixed": "overflowLevel" }, "color": { "fixed": "#c92020" }, "size": 11, "align": "right", "valign": "middle" }
|
||||
},
|
||||
{
|
||||
"name": "Label HighSafety Name",
|
||||
"type": "text",
|
||||
"placement": { "top": {{ty_highSafety}}, "left": 180, "width": 140, "height": 16 },
|
||||
"background": { "color": { "fixed": "transparent" } },
|
||||
"border": { "color": { "fixed": "transparent" }, "width": 0 },
|
||||
"config": { "text": { "mode": "fixed", "fixed": "highSafety" }, "color": { "fixed": "#cf7e20" }, "size": 11, "align": "right", "valign": "middle" }
|
||||
},
|
||||
{
|
||||
"name": "Label Inflow Name",
|
||||
"type": "text",
|
||||
"placement": { "top": {{ty_inflow}}, "left": 180, "width": 140, "height": 16 },
|
||||
"background": { "color": { "fixed": "transparent" } },
|
||||
"border": { "color": { "fixed": "transparent" }, "width": 0 },
|
||||
"config": { "text": { "mode": "fixed", "fixed": "inflowLevel" }, "color": { "fixed": "#3d8a5a" }, "size": 11, "align": "right", "valign": "middle" }
|
||||
},
|
||||
{
|
||||
"name": "Label DryRun Name",
|
||||
"type": "text",
|
||||
"placement": { "top": {{ty_dryRun}}, "left": 180, "width": 140, "height": 16 },
|
||||
"background": { "color": { "fixed": "transparent" } },
|
||||
"border": { "color": { "fixed": "transparent" }, "width": 0 },
|
||||
"config": { "text": { "mode": "fixed", "fixed": "dryRunLevel" }, "color": { "fixed": "#3a76a8" }, "size": 11, "align": "right", "valign": "middle" }
|
||||
},
|
||||
{
|
||||
"name": "Label Outflow Name",
|
||||
"type": "text",
|
||||
"placement": { "top": {{ty_outflow}}, "left": 180, "width": 140, "height": 16 },
|
||||
"background": { "color": { "fixed": "transparent" } },
|
||||
"border": { "color": { "fixed": "transparent" }, "width": 0 },
|
||||
"config": { "text": { "mode": "fixed", "fixed": "outflowLevel" }, "color": { "fixed": "#6a6a6a" }, "size": 11, "align": "right", "valign": "middle" }
|
||||
},
|
||||
{
|
||||
"name": "Value Overflow",
|
||||
"type": "metric-value",
|
||||
"placement": { "top": {{ty_overflow}}, "left": 323, "width": 65, "height": 16 },
|
||||
"background": { "color": { "fixed": "transparent" } },
|
||||
"border": { "color": { "fixed": "transparent" }, "width": 0 },
|
||||
"config": { "text": { "mode": "field", "fixed": "", "field": "overflowLevel" }, "color": { "fixed": "#c92020" }, "size": 11, "align": "left", "valign": "middle" }
|
||||
},
|
||||
{
|
||||
"name": "Value HighSafety",
|
||||
"type": "metric-value",
|
||||
"placement": { "top": {{ty_highSafety}}, "left": 323, "width": 65, "height": 16 },
|
||||
"background": { "color": { "fixed": "transparent" } },
|
||||
"border": { "color": { "fixed": "transparent" }, "width": 0 },
|
||||
"config": { "text": { "mode": "field", "fixed": "", "field": "highVolumeSafetyLevel" }, "color": { "fixed": "#cf7e20" }, "size": 11, "align": "left", "valign": "middle" }
|
||||
},
|
||||
{
|
||||
"name": "Value Inflow",
|
||||
"type": "metric-value",
|
||||
"placement": { "top": {{ty_inflow}}, "left": 323, "width": 65, "height": 16 },
|
||||
"background": { "color": { "fixed": "transparent" } },
|
||||
"border": { "color": { "fixed": "transparent" }, "width": 0 },
|
||||
"config": { "text": { "mode": "field", "fixed": "", "field": "inflowLevel" }, "color": { "fixed": "#3d8a5a" }, "size": 11, "align": "left", "valign": "middle" }
|
||||
},
|
||||
{
|
||||
"name": "Value DryRun",
|
||||
"type": "metric-value",
|
||||
"placement": { "top": {{ty_dryRun}}, "left": 323, "width": 65, "height": 16 },
|
||||
"background": { "color": { "fixed": "transparent" } },
|
||||
"border": { "color": { "fixed": "transparent" }, "width": 0 },
|
||||
"config": { "text": { "mode": "field", "fixed": "", "field": "dryRunLevel" }, "color": { "fixed": "#3a76a8" }, "size": 11, "align": "left", "valign": "middle" }
|
||||
},
|
||||
{
|
||||
"name": "Value Outflow",
|
||||
"type": "metric-value",
|
||||
"placement": { "top": {{ty_outflow}}, "left": 323, "width": 65, "height": 16 },
|
||||
"background": { "color": { "fixed": "transparent" } },
|
||||
"border": { "color": { "fixed": "transparent" }, "width": 0 },
|
||||
"config": { "text": { "mode": "field", "fixed": "", "field": "outflowLevel" }, "color": { "fixed": "#6a6a6a" }, "size": 11, "align": "left", "valign": "middle" }
|
||||
},
|
||||
{
|
||||
"name": "Header Rim",
|
||||
"type": "text",
|
||||
"placement": { "top": 2, "left": 10, "width": 380, "height": 16 },
|
||||
"background": { "color": { "fixed": "transparent" } },
|
||||
"border": { "color": { "fixed": "transparent" }, "width": 0 },
|
||||
"config": { "text": { "mode": "fixed", "fixed": "rim ({{heightBasin}} m)" }, "color": { "fixed": "#8a8a8a" }, "size": 10, "align": "center", "valign": "middle" }
|
||||
},
|
||||
{
|
||||
"name": "Footer Floor",
|
||||
"type": "text",
|
||||
"placement": { "top": 702, "left": 10, "width": 380, "height": 16 },
|
||||
"background": { "color": { "fixed": "transparent" } },
|
||||
"border": { "color": { "fixed": "transparent" }, "width": 0 },
|
||||
"config": { "text": { "mode": "fixed", "fixed": "floor (0.00 m)" }, "color": { "fixed": "#8a8a8a" }, "size": 10, "align": "center", "valign": "middle" }
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"query": "from(bucket: \"${bucket}\")\n |> range(start: -7d)\n |> filter(fn:(r) => r._measurement==\"${measurement}\" and (r._field==\"outflowLevel\" or r._field==\"inflowLevel\" or r._field==\"overflowLevel\" or r._field==\"heightBasin\" or r._field==\"dryRunLevel\" or r._field==\"highVolumeSafetyLevel\" or r._field =~ /^level\\.predicted\\.atequipment/ or r._field =~ /^volume\\.predicted\\.atequipment/ or r._field =~ /^volumePercent\\.predicted\\.atequipment/))\n |> last()\n |> map(fn: (r) => ({ r with _field: if r._field =~ /^volumePercent\\.predicted/ then \"volumePercent\" else if r._field =~ /^volume\\.predicted/ then \"volume\" else if r._field =~ /^level\\.predicted/ then \"level\" else r._field, _time: 2020-01-01T00:00:00Z }))\n |> group()\n |> keep(columns:[\"_field\",\"_value\",\"_time\"])\n |> pivot(rowKey:[\"_time\"], columnKey:[\"_field\"], valueColumn:\"_value\")",
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"title": "Tank Layout",
|
||||
"type": "canvas",
|
||||
"meta": { "emittedFields": ["basinLayout"] }
|
||||
},
|
||||
{
|
||||
"datasource": { "type": "influxdb", "uid": "cdzg44tv250jkd" },
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"unit": "lengthm",
|
||||
"custom": { "drawStyle": "line", "lineWidth": 2, "fillOpacity": 10 }
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": { "h": 10, "w": 14, "x": 10, "y": 6 },
|
||||
"id": 8,
|
||||
"options": {
|
||||
"legend": {
|
||||
"displayMode": "list",
|
||||
"placement": "bottom"
|
||||
},
|
||||
"tooltip": {
|
||||
"mode": "multi"
|
||||
}
|
||||
"legend": { "displayMode": "list", "placement": "bottom" },
|
||||
"tooltip": { "mode": "multi" }
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
@@ -356,45 +436,24 @@
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"title": "Level",
|
||||
"title": "Level (over time)",
|
||||
"type": "timeseries",
|
||||
"meta": {
|
||||
"emittedFields": [
|
||||
"level"
|
||||
]
|
||||
}
|
||||
"meta": { "emittedFields": ["level"] }
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "influxdb",
|
||||
"uid": "cdzg44tv250jkd"
|
||||
},
|
||||
"datasource": { "type": "influxdb", "uid": "cdzg44tv250jkd" },
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"unit": "m3",
|
||||
"custom": {
|
||||
"drawStyle": "line",
|
||||
"lineWidth": 2,
|
||||
"fillOpacity": 10
|
||||
}
|
||||
"custom": { "drawStyle": "line", "lineWidth": 2, "fillOpacity": 10 }
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 8,
|
||||
"w": 12,
|
||||
"x": 12,
|
||||
"y": 6
|
||||
},
|
||||
"gridPos": { "h": 10, "w": 14, "x": 10, "y": 16 },
|
||||
"id": 9,
|
||||
"options": {
|
||||
"legend": {
|
||||
"displayMode": "list",
|
||||
"placement": "bottom"
|
||||
},
|
||||
"tooltip": {
|
||||
"mode": "multi"
|
||||
}
|
||||
"legend": { "displayMode": "list", "placement": "bottom" },
|
||||
"tooltip": { "mode": "multi" }
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
@@ -402,56 +461,30 @@
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"title": "Volume",
|
||||
"title": "Volume (over time)",
|
||||
"type": "timeseries",
|
||||
"meta": {
|
||||
"emittedFields": [
|
||||
"volume"
|
||||
]
|
||||
}
|
||||
"meta": { "emittedFields": ["volume"] }
|
||||
},
|
||||
{
|
||||
"gridPos": {
|
||||
"h": 1,
|
||||
"w": 24,
|
||||
"x": 0,
|
||||
"y": 14
|
||||
},
|
||||
"gridPos": { "h": 1, "w": 24, "x": 0, "y": 26 },
|
||||
"id": 10,
|
||||
"title": "Flow",
|
||||
"type": "row"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "influxdb",
|
||||
"uid": "cdzg44tv250jkd"
|
||||
},
|
||||
"datasource": { "type": "influxdb", "uid": "cdzg44tv250jkd" },
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"unit": "m3/h",
|
||||
"custom": {
|
||||
"drawStyle": "line",
|
||||
"lineWidth": 2,
|
||||
"fillOpacity": 10
|
||||
}
|
||||
"custom": { "drawStyle": "line", "lineWidth": 2, "fillOpacity": 10 }
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 8,
|
||||
"w": 12,
|
||||
"x": 0,
|
||||
"y": 15
|
||||
},
|
||||
"gridPos": { "h": 8, "w": 12, "x": 0, "y": 27 },
|
||||
"id": 11,
|
||||
"options": {
|
||||
"legend": {
|
||||
"displayMode": "list",
|
||||
"placement": "bottom"
|
||||
},
|
||||
"tooltip": {
|
||||
"mode": "multi"
|
||||
}
|
||||
"legend": { "displayMode": "list", "placement": "bottom" },
|
||||
"tooltip": { "mode": "multi" }
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
@@ -461,44 +494,22 @@
|
||||
],
|
||||
"title": "Net Flow Rate",
|
||||
"type": "timeseries",
|
||||
"meta": {
|
||||
"emittedFields": [
|
||||
"flow.net",
|
||||
"flow"
|
||||
]
|
||||
}
|
||||
"meta": { "emittedFields": ["flow.net", "flow"] }
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "influxdb",
|
||||
"uid": "cdzg44tv250jkd"
|
||||
},
|
||||
"datasource": { "type": "influxdb", "uid": "cdzg44tv250jkd" },
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"unit": "m3/h",
|
||||
"custom": {
|
||||
"drawStyle": "line",
|
||||
"lineWidth": 2,
|
||||
"fillOpacity": 10
|
||||
}
|
||||
"custom": { "drawStyle": "line", "lineWidth": 2, "fillOpacity": 10 }
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 8,
|
||||
"w": 12,
|
||||
"x": 12,
|
||||
"y": 15
|
||||
},
|
||||
"gridPos": { "h": 8, "w": 12, "x": 12, "y": 27 },
|
||||
"id": 12,
|
||||
"options": {
|
||||
"legend": {
|
||||
"displayMode": "list",
|
||||
"placement": "bottom"
|
||||
},
|
||||
"tooltip": {
|
||||
"mode": "multi"
|
||||
}
|
||||
"legend": { "displayMode": "list", "placement": "bottom" },
|
||||
"tooltip": { "mode": "multi" }
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
@@ -508,133 +519,11 @@
|
||||
],
|
||||
"title": "Inflow + Outflow",
|
||||
"type": "timeseries",
|
||||
"meta": {
|
||||
"emittedFields": [
|
||||
"flow.in",
|
||||
"flow.out"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"gridPos": {
|
||||
"h": 1,
|
||||
"w": 24,
|
||||
"x": 0,
|
||||
"y": 23
|
||||
},
|
||||
"id": 13,
|
||||
"title": "Configuration",
|
||||
"type": "row"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "influxdb",
|
||||
"uid": "cdzg44tv250jkd"
|
||||
},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"unit": "lengthm",
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "blue",
|
||||
"value": null
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 4,
|
||||
"w": 12,
|
||||
"x": 0,
|
||||
"y": 24
|
||||
},
|
||||
"id": 14,
|
||||
"options": {
|
||||
"reduceOptions": {
|
||||
"calcs": [
|
||||
"lastNotNull"
|
||||
]
|
||||
},
|
||||
"colorMode": "value",
|
||||
"graphMode": "none"
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"query": "from(bucket: \"${bucket}\")\n |> range(start: -7d)\n |> filter(fn:(r) => r._measurement==\"${measurement}\" and (r._field==\"inflowLevel\" or r._field==\"outflowLevel\" or r._field==\"overflowLevel\" or r._field==\"heightBasin\"))\n |> group(columns:[\"_field\"])\n |> last()",
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"title": "Heights",
|
||||
"type": "stat",
|
||||
"meta": {
|
||||
"emittedFields": [
|
||||
"heights.min",
|
||||
"heights.max"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "influxdb",
|
||||
"uid": "cdzg44tv250jkd"
|
||||
},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"unit": "m3",
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "blue",
|
||||
"value": null
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 4,
|
||||
"w": 12,
|
||||
"x": 12,
|
||||
"y": 24
|
||||
},
|
||||
"id": 15,
|
||||
"options": {
|
||||
"reduceOptions": {
|
||||
"calcs": [
|
||||
"lastNotNull"
|
||||
]
|
||||
},
|
||||
"colorMode": "value",
|
||||
"graphMode": "none"
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"query": "from(bucket: \"${bucket}\")\n |> range(start: -7d)\n |> filter(fn:(r) => r._measurement==\"${measurement}\" and (r._field==\"maxVol\" or r._field==\"minVol\" or r._field==\"maxVolAtOverflow\" or r._field==\"minVolAtOutflow\" or r._field==\"minVolAtInflow\"))\n |> group(columns:[\"_field\"])\n |> last()",
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"title": "Volume Limits",
|
||||
"type": "stat",
|
||||
"meta": {
|
||||
"emittedFields": [
|
||||
"volume.min",
|
||||
"volume.max"
|
||||
]
|
||||
}
|
||||
"meta": { "emittedFields": ["flow.in", "flow.out"] }
|
||||
}
|
||||
],
|
||||
"schemaVersion": 39,
|
||||
"tags": [
|
||||
"EVOLV",
|
||||
"pumpingStation",
|
||||
"template"
|
||||
],
|
||||
"tags": ["EVOLV", "pumpingStation", "template"],
|
||||
"templating": {
|
||||
"list": [
|
||||
{
|
||||
@@ -642,60 +531,27 @@
|
||||
"type": "custom",
|
||||
"label": "dbase",
|
||||
"query": "cdzg44tv250jkd",
|
||||
"current": {
|
||||
"text": "cdzg44tv250jkd",
|
||||
"value": "cdzg44tv250jkd",
|
||||
"selected": false
|
||||
},
|
||||
"options": [
|
||||
{
|
||||
"text": "cdzg44tv250jkd",
|
||||
"value": "cdzg44tv250jkd",
|
||||
"selected": true
|
||||
}
|
||||
],
|
||||
"current": { "text": "cdzg44tv250jkd", "value": "cdzg44tv250jkd", "selected": false },
|
||||
"options": [{ "text": "cdzg44tv250jkd", "value": "cdzg44tv250jkd", "selected": true }],
|
||||
"hide": 2
|
||||
},
|
||||
{
|
||||
"name": "measurement",
|
||||
"type": "custom",
|
||||
"query": "template",
|
||||
"current": {
|
||||
"text": "template",
|
||||
"value": "template",
|
||||
"selected": false
|
||||
},
|
||||
"options": [
|
||||
{
|
||||
"text": "template",
|
||||
"value": "template",
|
||||
"selected": true
|
||||
}
|
||||
]
|
||||
"current": { "text": "template", "value": "template", "selected": false },
|
||||
"options": [{ "text": "template", "value": "template", "selected": true }]
|
||||
},
|
||||
{
|
||||
"name": "bucket",
|
||||
"type": "custom",
|
||||
"query": "lvl2",
|
||||
"current": {
|
||||
"text": "lvl2",
|
||||
"value": "lvl2",
|
||||
"selected": false
|
||||
},
|
||||
"options": [
|
||||
{
|
||||
"text": "lvl2",
|
||||
"value": "lvl2",
|
||||
"selected": true
|
||||
}
|
||||
]
|
||||
"current": { "text": "lvl2", "value": "lvl2", "selected": false },
|
||||
"options": [{ "text": "lvl2", "value": "lvl2", "selected": true }]
|
||||
}
|
||||
]
|
||||
},
|
||||
"time": {
|
||||
"from": "now-6h",
|
||||
"to": "now"
|
||||
},
|
||||
"time": { "from": "now-6h", "to": "now" },
|
||||
"timezone": "",
|
||||
"title": "template",
|
||||
"uid": null,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "dashboardAPI",
|
||||
"version": "1.0.0",
|
||||
"version": "1.1.0",
|
||||
"description": "EVOLV Grafana dashboard generator (Node-RED node).",
|
||||
"main": "dashboardAPI.js",
|
||||
"scripts": {
|
||||
|
||||
@@ -47,6 +47,20 @@ function defaultBucketForPosition(positionVsParent) {
|
||||
return 'lvl2';
|
||||
}
|
||||
|
||||
// Replace `{{name}}` placeholders in a raw JSON template string with values
|
||||
// from `vars`. Unknown placeholders are left intact. Used to inject node-config
|
||||
// derived constants (basin geometry, threshold y-positions) into a template
|
||||
// before JSON.parse — so the resulting dashboard has concrete numbers in
|
||||
// fieldConfig.thresholds and canvas element placements. Mustache-style braces
|
||||
// keep these placeholders distinct from Grafana's own `${var}` dashboard
|
||||
// variables (which are interpreted by Grafana at render time).
|
||||
function substituteTemplateVars(rawJson, vars) {
|
||||
if (!vars || !Object.keys(vars).length) return rawJson;
|
||||
return rawJson.replace(/\{\{([a-zA-Z_][a-zA-Z0-9_]*)\}\}/g, (m, key) => (
|
||||
Object.prototype.hasOwnProperty.call(vars, key) ? String(vars[key]) : m
|
||||
));
|
||||
}
|
||||
|
||||
function updateTemplatingVar(dashboard, varName, value) {
|
||||
const list = dashboard?.templating?.list;
|
||||
if (!Array.isArray(list)) return;
|
||||
@@ -140,13 +154,97 @@ class DashboardApi {
|
||||
return null;
|
||||
}
|
||||
|
||||
loadTemplate(softwareType) {
|
||||
loadTemplate(softwareType, templateVars = null) {
|
||||
const templatePath = this._templateFileForSoftwareType(softwareType);
|
||||
if (!templatePath) return null;
|
||||
const raw = fs.readFileSync(templatePath, 'utf8');
|
||||
let raw = fs.readFileSync(templatePath, 'utf8');
|
||||
// Always substitute — falls back to per-softwareType defaults so the
|
||||
// template is JSON-parseable even when no nodeConfig is provided (tests,
|
||||
// smoke-loading, etc.). _templateVarsForNode returns {} for types that
|
||||
// don't use placeholders, which is a no-op pass.
|
||||
const vars = templateVars || this._templateVarsForNode(softwareType, null);
|
||||
raw = substituteTemplateVars(raw, vars);
|
||||
return JSON.parse(raw);
|
||||
}
|
||||
|
||||
// Per-softwareType numeric vars baked into the template before JSON.parse.
|
||||
// Today only pumpingStation needs this (basin geometry → bar-gauge thresholds
|
||||
// and canvas y-positions). Other types return {} and skip substitution.
|
||||
_templateVarsForNode(softwareType, nodeConfig) {
|
||||
const st = String(softwareType || '').toLowerCase();
|
||||
if (st !== 'pumpingstation') return {};
|
||||
|
||||
// configManager.buildConfig nests basin geometry under `basin.*` and
|
||||
// safety percentages under `safety.*` (see generalFunctions/configManager).
|
||||
const basin = nodeConfig?.basin || {};
|
||||
const safety = nodeConfig?.safety || {};
|
||||
const heightBasin = Number(basin.height) || 4;
|
||||
const inflowLevel = Number(basin.inflowLevel) || 0;
|
||||
const outflowLevel = Number(basin.outflowLevel) || 0;
|
||||
const overflowLevel = Number(basin.overflowLevel) || heightBasin;
|
||||
const dryRunPct = Number(safety.dryRunThresholdPercent) || 30;
|
||||
const highPct = Number(safety.highVolumeSafetyThresholdPercent) || 90;
|
||||
|
||||
// Mirror specificClass._computeSafetyPoints derivation (pumpingStation).
|
||||
const dryRunLevel = outflowLevel * (1 + dryRunPct / 100);
|
||||
const highSafetyLevel = overflowLevel * (highPct / 100);
|
||||
|
||||
// Canvas tank: rim at y=20px, floor at y=700px (680px tall). Must match
|
||||
// hard-coded tank rectangle placement in config/pumpingStation.json
|
||||
// (basin row is h:20 grid rows; canvas root frame is 400x760 px — taller
|
||||
// than wide to match the card's aspect ratio so the tank fills the card
|
||||
// vertically with no letterboxing).
|
||||
const TANK_TOP = 20, TANK_BOT = 700, TANK_H = TANK_BOT - TANK_TOP;
|
||||
const yFor = (v) => +(TANK_BOT - (v / heightBasin) * TANK_H).toFixed(2);
|
||||
const tyFor = (yLine) => +(yLine - 8).toFixed(2); // centre 16px text on the line
|
||||
|
||||
const y_overflow = yFor(overflowLevel);
|
||||
const y_highSafety = yFor(highSafetyLevel);
|
||||
const y_inflow = yFor(inflowLevel);
|
||||
const y_dryRun = yFor(dryRunLevel);
|
||||
const y_outflow = yFor(outflowLevel);
|
||||
|
||||
// Label y-positions get min-gap enforcement so labels never overlap even
|
||||
// when thresholds sit nearly on top of each other (e.g. dryRun=2 % means
|
||||
// dryRunLevel sits right on outflowLevel; highSafety=98 % puts it under
|
||||
// overflow). Lines stay at proportional y; only the label text moves.
|
||||
// Two-pass (down + up) mirrors editor's basin-diagram.js placement logic.
|
||||
const GAP = 20;
|
||||
const labels = [
|
||||
{ id: 'overflow', y: tyFor(y_overflow) },
|
||||
{ id: 'highSafety', y: tyFor(y_highSafety) },
|
||||
{ id: 'inflow', y: tyFor(y_inflow) },
|
||||
{ id: 'dryRun', y: tyFor(y_dryRun) },
|
||||
{ id: 'outflow', y: tyFor(y_outflow) },
|
||||
].sort((a, b) => a.y - b.y);
|
||||
for (let i = 1; i < labels.length; i++) {
|
||||
if (labels[i].y < labels[i - 1].y + GAP) labels[i].y = labels[i - 1].y + GAP;
|
||||
}
|
||||
for (let i = labels.length - 2; i >= 0; i--) {
|
||||
if (labels[i].y > labels[i + 1].y - GAP) labels[i].y = labels[i + 1].y - GAP;
|
||||
}
|
||||
const ty = Object.fromEntries(labels.map((l) => [l.id, +l.y.toFixed(2)]));
|
||||
|
||||
return {
|
||||
heightBasin: +heightBasin.toFixed(2),
|
||||
outflowLevel: +outflowLevel.toFixed(3),
|
||||
inflowLevel: +inflowLevel.toFixed(3),
|
||||
overflowLevel: +overflowLevel.toFixed(3),
|
||||
dryRunLevel: +dryRunLevel.toFixed(3),
|
||||
highSafetyLevel: +highSafetyLevel.toFixed(3),
|
||||
y_overflow, y_highSafety, y_inflow, y_dryRun, y_outflow,
|
||||
h_spill: +(y_overflow - TANK_TOP).toFixed(2),
|
||||
h_highSafety: +(y_highSafety - y_overflow).toFixed(2),
|
||||
h_operating: +(y_outflow - y_highSafety).toFixed(2),
|
||||
h_dead: +(TANK_BOT - y_outflow).toFixed(2),
|
||||
ty_overflow: ty.overflow,
|
||||
ty_highSafety: ty.highSafety,
|
||||
ty_inflow: ty.inflow,
|
||||
ty_dryRun: ty.dryRun,
|
||||
ty_outflow: ty.outflow,
|
||||
};
|
||||
}
|
||||
|
||||
// Collect every `meta.emittedFields` declared by panels in a template.
|
||||
// Used by #39's parent panel filter — a parent panel whose emittedFields
|
||||
// are fully covered by its children's panels is removed.
|
||||
@@ -248,7 +346,8 @@ class DashboardApi {
|
||||
const title = nodeConfig?.general?.name || String(nodeId);
|
||||
|
||||
// Missing templates are treated as non-fatal: we skip only that dashboard.
|
||||
const dashboard = this.loadTemplate(softwareType);
|
||||
const templateVars = this._templateVarsForNode(softwareType, nodeConfig);
|
||||
const dashboard = this.loadTemplate(softwareType, templateVars);
|
||||
if (!dashboard) {
|
||||
this.logger.warn(`Skipping dashboard generation: no template for softwareType=${softwareType}`);
|
||||
return null;
|
||||
|
||||
Reference in New Issue
Block a user