2025-10-14 13:51:32 +02:00
<!--
| S88-niveau | Primair (blokkleur) | Tekstkleur |
| ---------------------- | ------------------- | ---------- |
| **Area** | `#0f52a5` | wit |
| **Process Cell** | `#0c99d9` | wit |
| **Unit** | `#50a8d9` | zwart |
| **Equipment (Module)** | `#86bbdd` | zwart |
| **Control Module** | `#a9daee` | zwart |
-->
2025-10-14 08:36:45 +02:00
< script src = "/pumpingStation/menu.js" > < / script > <!-- Load the menu script for dynamic dropdowns -->
< script src = "/pumpingStation/configData.js" > < / script > <!-- Load the config script for node information -->
2025-10-07 18:05:54 +02:00
2025-10-13 11:50:54 +02:00
< script > / / t e s t
2025-10-14 08:36:45 +02:00
RED.nodes.registerType("pumpingStation", {
2025-10-07 18:05:54 +02:00
category: "EVOLV",
2025-10-14 13:51:32 +02:00
color: "#0c99d9", // color for the node based on the S88 schema
2025-10-07 18:05:54 +02:00
defaults: {
2026-04-22 16:13:59 +02:00
name: { value: "" },
2025-10-07 18:05:54 +02:00
2025-10-14 16:32:44 +02:00
// Define station-specific properties
simulator: { value: false },
basinVolume: { value: 1 }, // m³, total empty basin
basinHeight: { value: 1 }, // m, floor to top
2026-04-22 16:13:59 +02:00
inflowLevel: { value: 0.8 }, // m, centre of inlet pipe above floor
outflowLevel: { value: 0.2 }, // m, centre of outlet pipe above floor
overflowLevel: { value: 0.9 }, // m, overflow elevation
defaultFluid: { value: "wastewater" },
inletPipeDiameter: { value: 0.3 }, // m
outletPipeDiameter: { value: 0.3 }, // m
pipelineLength: { value: 80 }, // m
maxDischargeHead: { value: 24 }, // m
staticHead: { value: 12 }, // m
maxInflowRate: { value: 200 }, // m³/h
temperatureReferenceDegC: { value: 15 },
2025-11-25 14:57:39 +01:00
timeleftToFullOrEmptyThresholdSeconds:{value:0}, // time threshold to safeguard starting or stopping pumps in seconds
enableDryRunProtection: { value: true },
enableOverfillProtection: { value: true },
dryRunThresholdPercent: { value: 2 },
overfillThresholdPercent: { value: 98 },
2025-11-20 12:15:46 +01:00
minHeightBasedOn: { value: "outlet" }, // basis for minimum height check: inlet or outlet
2026-03-12 16:39:25 +01:00
processOutputFormat: { value: "process" },
dbaseOutputFormat: { value: "influxdb" },
2025-10-14 16:32:44 +02:00
// Advanced reference information
refHeight: { value: "NAP" }, // reference height
basinBottomRef: { value: 1 }, // absolute elevation of basin floor
2025-10-07 18:05:54 +02:00
//define asset properties
uuid: { value: "" },
supplier: { value: "" },
category: { value: "" },
assetType: { value: "" },
model: { value: "" },
unit: { value: "" },
//logger properties
enableLog: { value: false },
logLevel: { value: "error" },
//physicalAspect
positionVsParent: { value: "" },
positionIcon: { value: "" },
hasDistance: { value: false },
distance: { value: 0 },
distanceUnit: { value: "m" },
2025-11-27 17:46:24 +01:00
distanceDescription: { value: "" },
// control strategy
controlMode: { value: "none" },
startLevel: { value: null },
2026-04-22 16:13:59 +02:00
minLevel: { value: null },
maxLevel: { value: null },
2025-11-27 17:46:24 +01:00
flowSetpoint: { value: null },
flowDeadband: { value: null }
2025-10-07 18:05:54 +02:00
},
inputs: 1,
outputs: 3,
inputLabels: ["Input"],
outputLabels: ["process", "dbase", "parent"],
2025-10-14 13:51:32 +02:00
icon: "font-awesome/fa-tint",
2025-10-07 18:05:54 +02:00
label: function () {
2025-10-21 12:45:19 +02:00
return this.positionIcon + " PumpingStation";
2025-10-07 18:05:54 +02:00
},
oneditprepare: function() {
const waitForMenuData = () => {
2025-10-14 13:51:32 +02:00
if (window.EVOLV?.nodes?.pumpingStation?.initEditor) {
window.EVOLV.nodes.pumpingStation.initEditor(this);
2025-10-07 18:05:54 +02:00
} else {
setTimeout(waitForMenuData, 50);
}
};
// Wait for the menu data to be ready before initializing the editor
waitForMenuData();
2025-10-14 16:32:44 +02:00
// NODE SPECIFIC
document.getElementById("node-input-basinVolume");
document.getElementById("node-input-basinHeight");
2026-04-22 16:13:59 +02:00
document.getElementById("node-input-inflowLevel");
document.getElementById("node-input-outflowLevel");
document.getElementById("node-input-overflowLevel");
2025-10-14 16:32:44 +02:00
document.getElementById("node-input-refHeight");
document.getElementById("node-input-basinBottomRef");
const refHeightEl = document.getElementById("node-input-refHeight");
if (refHeightEl) {
refHeightEl.value = this.refHeight || "NAP";
2025-10-07 18:05:54 +02:00
}
2025-11-20 12:15:46 +01:00
const minHeightBasedOnEl = document.getElementById("node-input-minHeightBasedOn");
if (minHeightBasedOnEl) {
minHeightBasedOnEl.value = this.minHeightBasedOn;
}
2025-10-07 18:05:54 +02:00
2025-11-25 14:57:39 +01:00
const dryRunToggle = document.getElementById("node-input-enableDryRunProtection");
const dryRunPercent = document.getElementById("node-input-dryRunThresholdPercent");
const overfillToggle = document.getElementById("node-input-enableOverfillProtection");
const overfillPercent = document.getElementById("node-input-overfillThresholdPercent");
const toggleInput = (toggleEl, inputEl) => {
if (!toggleEl || !inputEl) { return; }
inputEl.disabled = !toggleEl.checked;
inputEl.parentElement.classList.toggle('disabled', inputEl.disabled);
};
if (dryRunToggle & & dryRunPercent) {
dryRunToggle.checked = !!this.enableDryRunProtection;
dryRunPercent.value = Number.isFinite(this.dryRunThresholdPercent) ? this.dryRunThresholdPercent : 2;
dryRunToggle.addEventListener('change', () => toggleInput(dryRunToggle, dryRunPercent));
toggleInput(dryRunToggle, dryRunPercent);
}
if (overfillToggle & & overfillPercent) {
overfillToggle.checked = !!this.enableOverfillProtection;
overfillPercent.value = Number.isFinite(this.overfillThresholdPercent) ? this.overfillThresholdPercent : 98;
overfillToggle.addEventListener('change', () => toggleInput(overfillToggle, overfillPercent));
toggleInput(overfillToggle, overfillPercent);
}
const timeLeftInput = document.getElementById("node-input-timeleftToFullOrEmptyThresholdSeconds");
if (timeLeftInput) {
timeLeftInput.value = Number.isFinite(this.timeleftToFullOrEmptyThresholdSeconds)
? this.timeleftToFullOrEmptyThresholdSeconds
: 0;
}
2025-11-27 17:46:24 +01:00
// control mode toggle UI
const toggleModeSections = (val) => {
document.querySelectorAll('.ps-mode-section').forEach((el) => el.style.display = 'none');
const active = document.getElementById(`ps-mode-${val}`);
if (active) active.style.display = '';
};
const modeSelect = document.getElementById('node-input-controlMode');
if (modeSelect) {
modeSelect.value = this.controlMode || 'none';
toggleModeSections(modeSelect.value);
modeSelect.addEventListener('change', (e) => toggleModeSections(e.target.value));
}
const setNumberField = (id, val) => {
const el = document.getElementById(id);
if (el) el.value = Number.isFinite(val) ? val : '';
};
setNumberField('node-input-startLevel', this.startLevel);
2026-04-22 16:13:59 +02:00
setNumberField('node-input-minLevel', this.minLevel);
setNumberField('node-input-maxLevel', this.maxLevel);
2025-11-27 17:46:24 +01:00
setNumberField('node-input-flowSetpoint', this.flowSetpoint);
setNumberField('node-input-flowDeadband', this.flowDeadband);
Editor: interactive basin diagram — inputs placed at each threshold line
Replaces the static parameters-diagram-above-form-rows layout with a
single interactive SVG where every threshold input sits directly on
the tank at its proportional y-position. Typing a value repositions
the corresponding line + input + label live.
What moved into the diagram (via <foreignObject> holding real
<input> elements with their existing node-input-* IDs so Node-RED
save/restore is untouched):
basinHeight — top of tank (fixed at rim by definition)
overflowLevel — weir crest (red, dashed)
maxLevel — 100 % demand line (orange, dashed)
startLevel — ramp-start line (green, dashed)
minLevel — MGC-shutdown line (purple, dashed)
inflowLevel — Inlet arrow + input on left
outflowLevel — Outlet arrow + input on right
dryRunLevel — read-only, computed from outflow × (1+dryRunPct/100)
Also in the diagram:
- Dead-volume band fills the area below outflowLevel dynamically
- Warning ribbon appears below the tank if ordering invariants break
(mirrors specificClass._validateThresholdOrdering)
- All positions scale against the user's basinHeight; if empty, a
default 5 m scale is used just to keep the diagram readable
What stayed as regular form rows:
- Basin Volume (m³) — not a height, can't be placed on a y-axis
- minLevel / startLevel / maxLevel were in the Control Strategy >
Level-based section; removed from there and moved into the diagram
(the level-based subsection now contains a one-line pointer)
- Safety % inputs (dryRun, overfill) stay in the Safety section with
their derived-level readouts, now synced with the diagram
No schema changes, no field additions, no behaviour changes in the
runtime. Pure editor-UX.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 10:28:18 +02:00
// Interactive diagram: place every threshold line/input at its
// proportional y on the tank, plus compute derived safety levels
// (dryRunLevel, overfillLevel) that are shown both in the diagram
// and next to the safety-% fields. Same formulas as
// specificClass._validateThresholdOrdering.
2026-04-23 12:10:23 +02:00
const DIAG = { topY: 40, botY: 380 };
Editor: interactive basin diagram — inputs placed at each threshold line
Replaces the static parameters-diagram-above-form-rows layout with a
single interactive SVG where every threshold input sits directly on
the tank at its proportional y-position. Typing a value repositions
the corresponding line + input + label live.
What moved into the diagram (via <foreignObject> holding real
<input> elements with their existing node-input-* IDs so Node-RED
save/restore is untouched):
basinHeight — top of tank (fixed at rim by definition)
overflowLevel — weir crest (red, dashed)
maxLevel — 100 % demand line (orange, dashed)
startLevel — ramp-start line (green, dashed)
minLevel — MGC-shutdown line (purple, dashed)
inflowLevel — Inlet arrow + input on left
outflowLevel — Outlet arrow + input on right
dryRunLevel — read-only, computed from outflow × (1+dryRunPct/100)
Also in the diagram:
- Dead-volume band fills the area below outflowLevel dynamically
- Warning ribbon appears below the tank if ordering invariants break
(mirrors specificClass._validateThresholdOrdering)
- All positions scale against the user's basinHeight; if empty, a
default 5 m scale is used just to keep the diagram readable
What stayed as regular form rows:
- Basin Volume (m³) — not a height, can't be placed on a y-axis
- minLevel / startLevel / maxLevel were in the Control Strategy >
Level-based section; removed from there and moved into the diagram
(the level-based subsection now contains a one-line pointer)
- Safety % inputs (dryRun, overfill) stay in the Safety section with
their derived-level readouts, now synced with the diagram
No schema changes, no field additions, no behaviour changes in the
runtime. Pure editor-UX.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 10:28:18 +02:00
const fNum = (id) => {
const v = parseFloat(document.getElementById(`node-input-${id}`)?.value);
return Number.isFinite(v) ? v : null;
};
const yForLevel = (val, basinH) => {
if (val == null || !basinH) return null;
const y = DIAG.botY - (val / basinH) * (DIAG.botY - DIAG.topY);
return Math.max(DIAG.topY - 8, Math.min(DIAG.botY + 8, y));
};
2026-04-23 12:10:23 +02:00
// Place a row — line, label, input, unit all share the same y.
// The diagram is a schematic ordered list (value order is
// preserved, but the y-positions are distributed with a
// guaranteed minimum gap for readability), not a strictly
// proportional rendering.
const placeItem = (id, y) => {
2026-04-23 10:41:16 +02:00
const line = document.getElementById(`ps-line-${id}`);
Editor: interactive basin diagram — inputs placed at each threshold line
Replaces the static parameters-diagram-above-form-rows layout with a
single interactive SVG where every threshold input sits directly on
the tank at its proportional y-position. Typing a value repositions
the corresponding line + input + label live.
What moved into the diagram (via <foreignObject> holding real
<input> elements with their existing node-input-* IDs so Node-RED
save/restore is untouched):
basinHeight — top of tank (fixed at rim by definition)
overflowLevel — weir crest (red, dashed)
maxLevel — 100 % demand line (orange, dashed)
startLevel — ramp-start line (green, dashed)
minLevel — MGC-shutdown line (purple, dashed)
inflowLevel — Inlet arrow + input on left
outflowLevel — Outlet arrow + input on right
dryRunLevel — read-only, computed from outflow × (1+dryRunPct/100)
Also in the diagram:
- Dead-volume band fills the area below outflowLevel dynamically
- Warning ribbon appears below the tank if ordering invariants break
(mirrors specificClass._validateThresholdOrdering)
- All positions scale against the user's basinHeight; if empty, a
default 5 m scale is used just to keep the diagram readable
What stayed as regular form rows:
- Basin Volume (m³) — not a height, can't be placed on a y-axis
- minLevel / startLevel / maxLevel were in the Control Strategy >
Level-based section; removed from there and moved into the diagram
(the level-based subsection now contains a one-line pointer)
- Safety % inputs (dryRun, overfill) stay in the Safety section with
their derived-level readouts, now synced with the diagram
No schema changes, no field additions, no behaviour changes in the
runtime. Pure editor-UX.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 10:28:18 +02:00
const label = document.getElementById(`ps-label-${id}`);
2026-04-23 10:41:16 +02:00
const unit = document.getElementById(`ps-unit-${id}`);
const fo = document.getElementById(`ps-fo-${id}`);
const sub = document.getElementById(`ps-sub-${id}`);
const lead = document.getElementById(`ps-leader-${id}`);
2026-04-23 12:10:23 +02:00
if (line) { line.setAttribute('y1', y); line.setAttribute('y2', y); }
if (label) label.setAttribute('y', y + 4);
if (unit) unit.setAttribute('y', y + 4);
if (fo) fo.setAttribute('y', y - 11);
if (sub) sub.setAttribute('y', y + 15);
if (lead) lead.setAttribute('visibility', 'hidden');
Editor: interactive basin diagram — inputs placed at each threshold line
Replaces the static parameters-diagram-above-form-rows layout with a
single interactive SVG where every threshold input sits directly on
the tank at its proportional y-position. Typing a value repositions
the corresponding line + input + label live.
What moved into the diagram (via <foreignObject> holding real
<input> elements with their existing node-input-* IDs so Node-RED
save/restore is untouched):
basinHeight — top of tank (fixed at rim by definition)
overflowLevel — weir crest (red, dashed)
maxLevel — 100 % demand line (orange, dashed)
startLevel — ramp-start line (green, dashed)
minLevel — MGC-shutdown line (purple, dashed)
inflowLevel — Inlet arrow + input on left
outflowLevel — Outlet arrow + input on right
dryRunLevel — read-only, computed from outflow × (1+dryRunPct/100)
Also in the diagram:
- Dead-volume band fills the area below outflowLevel dynamically
- Warning ribbon appears below the tank if ordering invariants break
(mirrors specificClass._validateThresholdOrdering)
- All positions scale against the user's basinHeight; if empty, a
default 5 m scale is used just to keep the diagram readable
What stayed as regular form rows:
- Basin Volume (m³) — not a height, can't be placed on a y-axis
- minLevel / startLevel / maxLevel were in the Control Strategy >
Level-based section; removed from there and moved into the diagram
(the level-based subsection now contains a one-line pointer)
- Safety % inputs (dryRun, overfill) stay in the Safety section with
their derived-level readouts, now synced with the diagram
No schema changes, no field additions, no behaviour changes in the
runtime. Pure editor-UX.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 10:28:18 +02:00
};
2026-04-23 10:41:16 +02:00
Editor: interactive basin diagram — inputs placed at each threshold line
Replaces the static parameters-diagram-above-form-rows layout with a
single interactive SVG where every threshold input sits directly on
the tank at its proportional y-position. Typing a value repositions
the corresponding line + input + label live.
What moved into the diagram (via <foreignObject> holding real
<input> elements with their existing node-input-* IDs so Node-RED
save/restore is untouched):
basinHeight — top of tank (fixed at rim by definition)
overflowLevel — weir crest (red, dashed)
maxLevel — 100 % demand line (orange, dashed)
startLevel — ramp-start line (green, dashed)
minLevel — MGC-shutdown line (purple, dashed)
inflowLevel — Inlet arrow + input on left
outflowLevel — Outlet arrow + input on right
dryRunLevel — read-only, computed from outflow × (1+dryRunPct/100)
Also in the diagram:
- Dead-volume band fills the area below outflowLevel dynamically
- Warning ribbon appears below the tank if ordering invariants break
(mirrors specificClass._validateThresholdOrdering)
- All positions scale against the user's basinHeight; if empty, a
default 5 m scale is used just to keep the diagram readable
What stayed as regular form rows:
- Basin Volume (m³) — not a height, can't be placed on a y-axis
- minLevel / startLevel / maxLevel were in the Control Strategy >
Level-based section; removed from there and moved into the diagram
(the level-based subsection now contains a one-line pointer)
- Safety % inputs (dryRun, overfill) stay in the Safety section with
their derived-level readouts, now synced with the diagram
No schema changes, no field additions, no behaviour changes in the
runtime. Pure editor-UX.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 10:28:18 +02:00
const redraw = () => {
const basinH = fNum('basinHeight') || 5;
2026-04-23 10:41:16 +02:00
// Derived safety levels (participate in the right-column stack)
const basedOn = document.getElementById('node-input-minHeightBasedOn')?.value || 'outlet';
const refLow = basedOn === 'inlet' ? fNum('inflowLevel') : fNum('outflowLevel');
const dryPct = fNum('dryRunThresholdPercent');
const ovfPct = fNum('overfillThresholdPercent');
const ovf = fNum('overflowLevel');
const dryLvl = (refLow != null & & dryPct != null) ? refLow * (1 + dryPct / 100) : null;
const ovfLvl = (ovf != null & & ovfPct != null) ? ovf * (ovfPct / 100) : null;
2026-04-23 13:19:58 +02:00
// Right-column stack. TWO anchors: basinHeight pinned at the
// tank rim (top) and outflowLevel pinned at its proportional y
// (bottom). Everything between is nudged to maintain a minimum
// vertical gap via two passes — top-down from the rim, then
// bottom-up from the outlet — so the dashed lines keep their
// value-order and outlet stays near the floor where it belongs.
2026-04-23 10:41:16 +02:00
const items = [
2026-04-23 13:19:58 +02:00
{ id: 'basinHeight', yIdeal: DIAG.topY, pinned: true },
2026-04-23 12:10:23 +02:00
{ id: 'overflowLevel', yIdeal: yForLevel(fNum('overflowLevel'), basinH) },
{ id: 'maxLevel', yIdeal: yForLevel(fNum('maxLevel'), basinH) },
{ id: 'startLevel', yIdeal: yForLevel(fNum('startLevel'), basinH) },
{ id: 'minLevel', yIdeal: yForLevel(fNum('minLevel'), basinH) },
{ id: 'dryRunLevel', yIdeal: yForLevel(dryLvl, basinH) },
2026-04-23 13:19:58 +02:00
{ id: 'outflowLevel', yIdeal: yForLevel(fNum('outflowLevel'), basinH), pinned: true },
2026-04-23 12:10:23 +02:00
].filter(it => it.yIdeal != null);
const GAP = 36;
items.sort((a, b) => a.yIdeal - b.yIdeal);
2026-04-23 13:19:58 +02:00
for (const it of items) it.y = it.yIdeal;
// Pass 1: top-down — push DOWN to maintain GAP; pinned items don't move
for (let i = 1; i < items.length ; i + + ) {
if (items[i].pinned) continue;
items[i].y = Math.max(items[i].y, items[i - 1].y + GAP);
}
// Pass 2: bottom-up — push UP so outflow's pin propagates up the stack
for (let i = items.length - 2; i >= 0; i--) {
if (items[i].pinned) continue;
items[i].y = Math.min(items[i].y, items[i + 1].y - GAP);
2026-04-23 10:41:16 +02:00
}
2026-04-23 12:10:23 +02:00
for (const it of items) placeItem(it.id, it.y);
2026-04-23 10:41:16 +02:00
2026-04-23 13:19:58 +02:00
// Zone labels between adjacent thresholds (italic, centered).
// Hidden if either bracketing threshold is missing, or the gap
// is too small to read (< 14 px ) .
const placeZone = (zoneId, topId, botId) => {
const el = document.getElementById(`ps-zone-${zoneId}`);
if (!el) return;
const top = items.find(it => it.id === topId);
const bot = items.find(it => it.id === botId);
if (!top || !bot || (bot.y - top.y) < 14 ) {
el.setAttribute('visibility', 'hidden'); return;
}
el.setAttribute('y', (top.y + bot.y) / 2 + 3);
el.setAttribute('visibility', 'visible');
};
placeZone('spare', 'overflowLevel', 'maxLevel');
placeZone('sewage', 'maxLevel', 'startLevel');
placeZone('buffer1', 'startLevel', 'minLevel');
placeZone('buffer2', 'minLevel', 'dryRunLevel');
// "Dead volume" sits inside the blue band between outflowLevel and the floor
const outflowPinned = items.find(it => it.id === 'outflowLevel');
const deadLbl = document.getElementById('ps-zone-dead');
if (deadLbl & & outflowPinned & & (DIAG.botY - outflowPinned.y) > 14) {
deadLbl.setAttribute('y', (outflowPinned.y + DIAG.botY) / 2 + 3);
deadLbl.setAttribute('visibility', 'visible');
} else if (deadLbl) {
deadLbl.setAttribute('visibility', 'hidden');
}
2026-04-23 10:41:16 +02:00
// Inlet arrow — sole item on the left, no stacking concerns
const inflowY = yForLevel(fNum('inflowLevel'), basinH);
if (inflowY != null) {
const line = document.getElementById('ps-line-inflowLevel');
const lbl = document.getElementById('ps-label-inflowLevel');
const sub = document.getElementById('ps-sub-inflowLevel');
const fo = document.getElementById('ps-fo-inflowLevel');
const unit = document.getElementById('ps-unit-inflowLevel');
if (line) { line.setAttribute('y1', inflowY); line.setAttribute('y2', inflowY); }
if (lbl) lbl.setAttribute('y', inflowY - 4);
if (sub) sub.setAttribute('y', inflowY + 8);
if (fo) fo.setAttribute('y', inflowY - 11);
if (unit) unit.setAttribute('y', inflowY + 4);
}
2026-04-23 12:10:23 +02:00
// Dead-volume band: from the (possibly-nudged) outflow line
// down to the floor. Use the nudged y so the band meets the
// outflow line exactly.
const outflowItem = items.find(it => it.id === 'outflowLevel');
const deadvol = document.getElementById('ps-deadvol');
if (deadvol & & outflowItem) {
deadvol.setAttribute('y', outflowItem.y);
deadvol.setAttribute('height', Math.max(0, DIAG.botY - outflowItem.y));
Editor: interactive basin diagram — inputs placed at each threshold line
Replaces the static parameters-diagram-above-form-rows layout with a
single interactive SVG where every threshold input sits directly on
the tank at its proportional y-position. Typing a value repositions
the corresponding line + input + label live.
What moved into the diagram (via <foreignObject> holding real
<input> elements with their existing node-input-* IDs so Node-RED
save/restore is untouched):
basinHeight — top of tank (fixed at rim by definition)
overflowLevel — weir crest (red, dashed)
maxLevel — 100 % demand line (orange, dashed)
startLevel — ramp-start line (green, dashed)
minLevel — MGC-shutdown line (purple, dashed)
inflowLevel — Inlet arrow + input on left
outflowLevel — Outlet arrow + input on right
dryRunLevel — read-only, computed from outflow × (1+dryRunPct/100)
Also in the diagram:
- Dead-volume band fills the area below outflowLevel dynamically
- Warning ribbon appears below the tank if ordering invariants break
(mirrors specificClass._validateThresholdOrdering)
- All positions scale against the user's basinHeight; if empty, a
default 5 m scale is used just to keep the diagram readable
What stayed as regular form rows:
- Basin Volume (m³) — not a height, can't be placed on a y-axis
- minLevel / startLevel / maxLevel were in the Control Strategy >
Level-based section; removed from there and moved into the diagram
(the level-based subsection now contains a one-line pointer)
- Safety % inputs (dryRun, overfill) stay in the Safety section with
their derived-level readouts, now synced with the diagram
No schema changes, no field additions, no behaviour changes in the
runtime. Pure editor-UX.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 10:28:18 +02:00
}
2026-04-23 10:41:16 +02:00
// dryRunLevel label text (derived, read-only)
Editor: interactive basin diagram — inputs placed at each threshold line
Replaces the static parameters-diagram-above-form-rows layout with a
single interactive SVG where every threshold input sits directly on
the tank at its proportional y-position. Typing a value repositions
the corresponding line + input + label live.
What moved into the diagram (via <foreignObject> holding real
<input> elements with their existing node-input-* IDs so Node-RED
save/restore is untouched):
basinHeight — top of tank (fixed at rim by definition)
overflowLevel — weir crest (red, dashed)
maxLevel — 100 % demand line (orange, dashed)
startLevel — ramp-start line (green, dashed)
minLevel — MGC-shutdown line (purple, dashed)
inflowLevel — Inlet arrow + input on left
outflowLevel — Outlet arrow + input on right
dryRunLevel — read-only, computed from outflow × (1+dryRunPct/100)
Also in the diagram:
- Dead-volume band fills the area below outflowLevel dynamically
- Warning ribbon appears below the tank if ordering invariants break
(mirrors specificClass._validateThresholdOrdering)
- All positions scale against the user's basinHeight; if empty, a
default 5 m scale is used just to keep the diagram readable
What stayed as regular form rows:
- Basin Volume (m³) — not a height, can't be placed on a y-axis
- minLevel / startLevel / maxLevel were in the Control Strategy >
Level-based section; removed from there and moved into the diagram
(the level-based subsection now contains a one-line pointer)
- Safety % inputs (dryRun, overfill) stay in the Safety section with
their derived-level readouts, now synced with the diagram
No schema changes, no field additions, no behaviour changes in the
runtime. Pure editor-UX.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 10:28:18 +02:00
const dryLbl = document.getElementById('ps-label-dryRunLevel');
if (dryLbl) dryLbl.textContent = dryLvl != null
? `dryRunLevel ≈ ${dryLvl.toFixed(2)} m (safety — from %)`
: 'dryRunLevel ≈ — m (safety — from %)';
2026-04-23 10:41:16 +02:00
// Safety-section readouts (second view, beneath the diagram)
Editor: interactive basin diagram — inputs placed at each threshold line
Replaces the static parameters-diagram-above-form-rows layout with a
single interactive SVG where every threshold input sits directly on
the tank at its proportional y-position. Typing a value repositions
the corresponding line + input + label live.
What moved into the diagram (via <foreignObject> holding real
<input> elements with their existing node-input-* IDs so Node-RED
save/restore is untouched):
basinHeight — top of tank (fixed at rim by definition)
overflowLevel — weir crest (red, dashed)
maxLevel — 100 % demand line (orange, dashed)
startLevel — ramp-start line (green, dashed)
minLevel — MGC-shutdown line (purple, dashed)
inflowLevel — Inlet arrow + input on left
outflowLevel — Outlet arrow + input on right
dryRunLevel — read-only, computed from outflow × (1+dryRunPct/100)
Also in the diagram:
- Dead-volume band fills the area below outflowLevel dynamically
- Warning ribbon appears below the tank if ordering invariants break
(mirrors specificClass._validateThresholdOrdering)
- All positions scale against the user's basinHeight; if empty, a
default 5 m scale is used just to keep the diagram readable
What stayed as regular form rows:
- Basin Volume (m³) — not a height, can't be placed on a y-axis
- minLevel / startLevel / maxLevel were in the Control Strategy >
Level-based section; removed from there and moved into the diagram
(the level-based subsection now contains a one-line pointer)
- Safety % inputs (dryRun, overfill) stay in the Safety section with
their derived-level readouts, now synced with the diagram
No schema changes, no field additions, no behaviour changes in the
runtime. Pure editor-UX.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 10:28:18 +02:00
const d1 = document.getElementById('derived-dryRunLevel');
if (d1) d1.textContent = dryLvl != null ? `→ dryRunLevel ≈ ${dryLvl.toFixed(2)} m` : '→ dryRunLevel ≈ — m';
const d2 = document.getElementById('derived-overfillLevel');
if (d2) d2.textContent = ovfLvl != null ? `→ overfillLevel ≈ ${ovfLvl.toFixed(2)} m` : '→ overfillLevel ≈ — m';
2026-04-23 10:41:16 +02:00
Editor: interactive basin diagram — inputs placed at each threshold line
Replaces the static parameters-diagram-above-form-rows layout with a
single interactive SVG where every threshold input sits directly on
the tank at its proportional y-position. Typing a value repositions
the corresponding line + input + label live.
What moved into the diagram (via <foreignObject> holding real
<input> elements with their existing node-input-* IDs so Node-RED
save/restore is untouched):
basinHeight — top of tank (fixed at rim by definition)
overflowLevel — weir crest (red, dashed)
maxLevel — 100 % demand line (orange, dashed)
startLevel — ramp-start line (green, dashed)
minLevel — MGC-shutdown line (purple, dashed)
inflowLevel — Inlet arrow + input on left
outflowLevel — Outlet arrow + input on right
dryRunLevel — read-only, computed from outflow × (1+dryRunPct/100)
Also in the diagram:
- Dead-volume band fills the area below outflowLevel dynamically
- Warning ribbon appears below the tank if ordering invariants break
(mirrors specificClass._validateThresholdOrdering)
- All positions scale against the user's basinHeight; if empty, a
default 5 m scale is used just to keep the diagram readable
What stayed as regular form rows:
- Basin Volume (m³) — not a height, can't be placed on a y-axis
- minLevel / startLevel / maxLevel were in the Control Strategy >
Level-based section; removed from there and moved into the diagram
(the level-based subsection now contains a one-line pointer)
- Safety % inputs (dryRun, overfill) stay in the Safety section with
their derived-level readouts, now synced with the diagram
No schema changes, no field additions, no behaviour changes in the
runtime. Pure editor-UX.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 10:28:18 +02:00
// Ordering warning ribbon
const warn = document.getElementById('ps-warning');
const issues = [];
const pairs = [
['outflowLevel', 'inflowLevel', '< '],
['inflowLevel', 'overflowLevel', '< '],
['minLevel', 'startLevel', '< ='],
['startLevel', 'maxLevel', '< '],
['maxLevel', 'overflowLevel', '< ='],
];
for (const [a, b, op] of pairs) {
const av = fNum(a), bv = fNum(b);
if (av == null || bv == null) continue;
if (op === '< ' ? !(av < bv ) : ! ( av < = bv ) ) issues . push ( ` $ { a } $ { op } $ { b } ` ) ;
}
if (warn) {
if (issues.length) { warn.setAttribute('visibility', 'visible'); warn.textContent = `⚠ Check ordering: ${issues.join(', ')}`; }
else { warn.setAttribute('visibility', 'hidden'); }
}
Editor: pipe-edge conventions + live derived safety levels
### P1 — match diagram naming (labels only, no schema change)
- "Inlet Elevation" → "Inlet (bottom of pipe, m)"
- "Outlet Elevation" → "Outlet (top of pipe, m)"
- "Overflow Level" → "Overflow (weir crest, m)"
- "Basin Bottom (m Refheight)" → "Basin floor above datum (m)"
- Added a one-line banner at the top of Basin Geometry:
"All heights measured from the basin floor (0 m)."
These map directly to the clarifications added to basin-model.drawio.svg
so editor and diagram speak the same vocabulary.
### P3 — live derived safety levels next to the % fields
Low/High Volume Threshold fields now show the resulting trip level
live as the operator types:
Low Volume Threshold (%) [ 2 ] → dryRunLevel ≈ 0.21 m
High Volume Threshold (%) [ 98 ] → overfillLevel ≈ 4.41 m
Recomputed on every input/change of outflowLevel, inflowLevel,
overflowLevel, minHeightBasedOn, or either %. Pure UI feedback —
no schema change, no save-side change, same formulas as
specificClass._validateThresholdOrdering().
Also includes the user's latest basin-model.drawio.svg update
(inlet=bottom/outlet=top labels + datum annotation).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 09:58:17 +02:00
};
Editor: interactive basin diagram — inputs placed at each threshold line
Replaces the static parameters-diagram-above-form-rows layout with a
single interactive SVG where every threshold input sits directly on
the tank at its proportional y-position. Typing a value repositions
the corresponding line + input + label live.
What moved into the diagram (via <foreignObject> holding real
<input> elements with their existing node-input-* IDs so Node-RED
save/restore is untouched):
basinHeight — top of tank (fixed at rim by definition)
overflowLevel — weir crest (red, dashed)
maxLevel — 100 % demand line (orange, dashed)
startLevel — ramp-start line (green, dashed)
minLevel — MGC-shutdown line (purple, dashed)
inflowLevel — Inlet arrow + input on left
outflowLevel — Outlet arrow + input on right
dryRunLevel — read-only, computed from outflow × (1+dryRunPct/100)
Also in the diagram:
- Dead-volume band fills the area below outflowLevel dynamically
- Warning ribbon appears below the tank if ordering invariants break
(mirrors specificClass._validateThresholdOrdering)
- All positions scale against the user's basinHeight; if empty, a
default 5 m scale is used just to keep the diagram readable
What stayed as regular form rows:
- Basin Volume (m³) — not a height, can't be placed on a y-axis
- minLevel / startLevel / maxLevel were in the Control Strategy >
Level-based section; removed from there and moved into the diagram
(the level-based subsection now contains a one-line pointer)
- Safety % inputs (dryRun, overfill) stay in the Safety section with
their derived-level readouts, now synced with the diagram
No schema changes, no field additions, no behaviour changes in the
runtime. Pure editor-UX.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 10:28:18 +02:00
['basinHeight','overflowLevel','maxLevel','startLevel','minLevel','inflowLevel','outflowLevel',
'dryRunThresholdPercent','overfillThresholdPercent','minHeightBasedOn'].forEach((id) => {
const el = document.getElementById(`node-input-${id}`);
if (el) { el.addEventListener('input', redraw); el.addEventListener('change', redraw); }
});
setTimeout(redraw, 60);
Editor: pipe-edge conventions + live derived safety levels
### P1 — match diagram naming (labels only, no schema change)
- "Inlet Elevation" → "Inlet (bottom of pipe, m)"
- "Outlet Elevation" → "Outlet (top of pipe, m)"
- "Overflow Level" → "Overflow (weir crest, m)"
- "Basin Bottom (m Refheight)" → "Basin floor above datum (m)"
- Added a one-line banner at the top of Basin Geometry:
"All heights measured from the basin floor (0 m)."
These map directly to the clarifications added to basin-model.drawio.svg
so editor and diagram speak the same vocabulary.
### P3 — live derived safety levels next to the % fields
Low/High Volume Threshold fields now show the resulting trip level
live as the operator types:
Low Volume Threshold (%) [ 2 ] → dryRunLevel ≈ 0.21 m
High Volume Threshold (%) [ 98 ] → overfillLevel ≈ 4.41 m
Recomputed on every input/change of outflowLevel, inflowLevel,
overflowLevel, minHeightBasedOn, or either %. Pure UI feedback —
no schema change, no save-side change, same formulas as
specificClass._validateThresholdOrdering().
Also includes the user's latest basin-model.drawio.svg update
(inlet=bottom/outlet=top labels + datum annotation).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 09:58:17 +02:00
2025-10-07 18:05:54 +02:00
//------------------- END OF CUSTOM config UI ELEMENTS ------------------- //
},
oneditsave: function () {
const node = this;
2025-10-14 16:32:44 +02:00
//window.EVOLV?.nodes?.pumpingStation?.assetMenu?.saveEditor?.(node);
window.EVOLV?.nodes?.pumpingStation?.loggerMenu?.saveEditor?.(node);
window.EVOLV?.nodes?.pumpingStation?.positionMenu?.saveEditor?.(node);
2025-10-07 18:05:54 +02:00
2025-10-14 16:32:44 +02:00
//node specific
node.refHeight = document.getElementById("node-input-refHeight").value || "NAP";
2025-11-20 12:15:46 +01:00
node.minHeightBasedOn = document.getElementById("node-input-minHeightBasedOn").value || "outlet";
2025-10-14 16:32:44 +02:00
node.simulator = document.getElementById("node-input-simulator").checked;
2025-10-07 18:05:54 +02:00
2026-04-22 16:13:59 +02:00
["basinVolume","basinHeight","inflowLevel","outflowLevel","overflowLevel","basinBottomRef","timeleftToFullOrEmptyThresholdSeconds","dryRunThresholdPercent","overfillThresholdPercent"]
2025-10-14 16:32:44 +02:00
.forEach(field => {
node[field] = parseFloat(document.getElementById(`node-input-${field}`).value) || 0;
});
2025-10-07 18:05:54 +02:00
2025-10-14 16:32:44 +02:00
node.refHeight = document.getElementById("node-input-refHeight").value || "";
2025-11-25 14:57:39 +01:00
node.enableDryRunProtection = document.getElementById("node-input-enableDryRunProtection").checked;
node.enableOverfillProtection = document.getElementById("node-input-enableOverfillProtection").checked;
2025-11-27 17:46:24 +01:00
// control strategy
node.controlMode = document.getElementById('node-input-controlMode').value || 'none';
const parseNum = (id) => parseFloat(document.getElementById(id)?.value);
node.startLevel = parseNum('node-input-startLevel');
2026-04-22 16:13:59 +02:00
node.minLevel = parseNum('node-input-minLevel');
node.maxLevel = parseNum('node-input-maxLevel');
2025-11-27 17:46:24 +01:00
node.flowSetpoint = parseNum('node-input-flowSetpoint');
node.flowDeadband = parseNum('node-input-flowDeadband');
2025-10-07 18:05:54 +02:00
},
2025-10-14 16:32:44 +02:00
2025-10-07 18:05:54 +02:00
});
< / script >
<!-- Main UI -->
2025-10-14 13:51:32 +02:00
< script type = "text/html" data-template-name = "pumpingStation" >
2025-10-07 18:05:54 +02:00
2025-11-27 17:46:24 +01:00
< h4 > Simulation< / h4 >
2025-10-07 18:05:54 +02:00
< div class = "form-row" >
2025-10-14 16:32:44 +02:00
< label for = "node-input-simulator" > < i class = "fa fa-play-circle" > < / i > Simulator< / label >
< input type = "checkbox" id = "node-input-simulator" style = "width:20px;vertical-align:baseline;" / >
< span > Run station in simulated mode< / span >
2025-10-07 18:05:54 +02:00
< / div >
2025-10-14 16:32:44 +02:00
< hr >
2025-11-27 17:46:24 +01:00
Editor: interactive basin diagram — inputs placed at each threshold line
Replaces the static parameters-diagram-above-form-rows layout with a
single interactive SVG where every threshold input sits directly on
the tank at its proportional y-position. Typing a value repositions
the corresponding line + input + label live.
What moved into the diagram (via <foreignObject> holding real
<input> elements with their existing node-input-* IDs so Node-RED
save/restore is untouched):
basinHeight — top of tank (fixed at rim by definition)
overflowLevel — weir crest (red, dashed)
maxLevel — 100 % demand line (orange, dashed)
startLevel — ramp-start line (green, dashed)
minLevel — MGC-shutdown line (purple, dashed)
inflowLevel — Inlet arrow + input on left
outflowLevel — Outlet arrow + input on right
dryRunLevel — read-only, computed from outflow × (1+dryRunPct/100)
Also in the diagram:
- Dead-volume band fills the area below outflowLevel dynamically
- Warning ribbon appears below the tank if ordering invariants break
(mirrors specificClass._validateThresholdOrdering)
- All positions scale against the user's basinHeight; if empty, a
default 5 m scale is used just to keep the diagram readable
What stayed as regular form rows:
- Basin Volume (m³) — not a height, can't be placed on a y-axis
- minLevel / startLevel / maxLevel were in the Control Strategy >
Level-based section; removed from there and moved into the diagram
(the level-based subsection now contains a one-line pointer)
- Safety % inputs (dryRun, overfill) stay in the Safety section with
their derived-level readouts, now synced with the diagram
No schema changes, no field additions, no behaviour changes in the
runtime. Pure editor-UX.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 10:28:18 +02:00
< h4 > Basin parameters< / h4 >
< p style = "font-size:12px;color:#777;margin:0 0 8px 0;" > Heights are measured from the basin floor (0 m). Enter values next to each line — the diagram scales to whatever you enter.< / p >
< style >
#ps-basin-diagram input[type=number] {
width: 100%; height: 20px; box-sizing: border-box;
font-size: 11px; padding: 1px 4px; margin: 0;
border: 1px solid #ccc; border-radius: 3px; background: #fff;
}
#ps-basin-diagram input[type=number]:focus { outline: 1px solid #0c99d9; border-color: #0c99d9; }
< / style >
2026-04-23 12:10:23 +02:00
< svg id = "ps-basin-diagram" xmlns = "http://www.w3.org/2000/svg" viewBox = "0 0 520 430"
Editor: interactive basin diagram — inputs placed at each threshold line
Replaces the static parameters-diagram-above-form-rows layout with a
single interactive SVG where every threshold input sits directly on
the tank at its proportional y-position. Typing a value repositions
the corresponding line + input + label live.
What moved into the diagram (via <foreignObject> holding real
<input> elements with their existing node-input-* IDs so Node-RED
save/restore is untouched):
basinHeight — top of tank (fixed at rim by definition)
overflowLevel — weir crest (red, dashed)
maxLevel — 100 % demand line (orange, dashed)
startLevel — ramp-start line (green, dashed)
minLevel — MGC-shutdown line (purple, dashed)
inflowLevel — Inlet arrow + input on left
outflowLevel — Outlet arrow + input on right
dryRunLevel — read-only, computed from outflow × (1+dryRunPct/100)
Also in the diagram:
- Dead-volume band fills the area below outflowLevel dynamically
- Warning ribbon appears below the tank if ordering invariants break
(mirrors specificClass._validateThresholdOrdering)
- All positions scale against the user's basinHeight; if empty, a
default 5 m scale is used just to keep the diagram readable
What stayed as regular form rows:
- Basin Volume (m³) — not a height, can't be placed on a y-axis
- minLevel / startLevel / maxLevel were in the Control Strategy >
Level-based section; removed from there and moved into the diagram
(the level-based subsection now contains a one-line pointer)
- Safety % inputs (dryRun, overfill) stay in the Safety section with
their derived-level readouts, now synced with the diagram
No schema changes, no field additions, no behaviour changes in the
runtime. Pure editor-UX.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 10:28:18 +02:00
style="display:block;width:100%;max-width:540px;margin:0 0 12px 0;background:#fff;border:1px solid #e5e5e5;border-radius:4px;"
font-family="Arial,sans-serif" font-size="11">
< defs >
< marker id = "ps-arrow" viewBox = "0 0 10 10" refX = "9" refY = "5" markerWidth = "7" markerHeight = "7" orient = "auto-start-reverse" >
< path d = "M 0 0 L 10 5 L 0 10 z" fill = "#1F4E79" / >
< / marker >
< / defs >
<!-- Tank body -->
2026-04-23 12:10:23 +02:00
< rect x = "200" y = "40" width = "120" height = "340" fill = "#F0F8FF" stroke = "#333" stroke-width = "1.5" / >
Editor: interactive basin diagram — inputs placed at each threshold line
Replaces the static parameters-diagram-above-form-rows layout with a
single interactive SVG where every threshold input sits directly on
the tank at its proportional y-position. Typing a value repositions
the corresponding line + input + label live.
What moved into the diagram (via <foreignObject> holding real
<input> elements with their existing node-input-* IDs so Node-RED
save/restore is untouched):
basinHeight — top of tank (fixed at rim by definition)
overflowLevel — weir crest (red, dashed)
maxLevel — 100 % demand line (orange, dashed)
startLevel — ramp-start line (green, dashed)
minLevel — MGC-shutdown line (purple, dashed)
inflowLevel — Inlet arrow + input on left
outflowLevel — Outlet arrow + input on right
dryRunLevel — read-only, computed from outflow × (1+dryRunPct/100)
Also in the diagram:
- Dead-volume band fills the area below outflowLevel dynamically
- Warning ribbon appears below the tank if ordering invariants break
(mirrors specificClass._validateThresholdOrdering)
- All positions scale against the user's basinHeight; if empty, a
default 5 m scale is used just to keep the diagram readable
What stayed as regular form rows:
- Basin Volume (m³) — not a height, can't be placed on a y-axis
- minLevel / startLevel / maxLevel were in the Control Strategy >
Level-based section; removed from there and moved into the diagram
(the level-based subsection now contains a one-line pointer)
- Safety % inputs (dryRun, overfill) stay in the Safety section with
their derived-level readouts, now synced with the diagram
No schema changes, no field additions, no behaviour changes in the
runtime. Pure editor-UX.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 10:28:18 +02:00
<!-- Dead - volume band (y + height updated dynamically below outflowLevel) -->
< rect id = "ps-deadvol" x = "201" width = "118" fill = "#AACCE0" / >
2026-04-23 13:19:58 +02:00
<!-- basinVolume — pinned above the rim -->
< text id = "ps-label-basinVolume" x = "330" y = "19" fill = "#333" font-weight = "600" > basin volume< / text >
< foreignObject id = "ps-fo-basinVolume" x = "425" y = "4" width = "70" height = "22" >
< input xmlns = "http://www.w3.org/1999/xhtml" type = "number" id = "node-input-basinVolume" min = "0" step = "0.1" / >
< / foreignObject >
< text id = "ps-unit-basinVolume" x = "500" y = "19" fill = "#555" > m³< / text >
<!-- Zone labels (mid - tank italic, positioned dynamically at midpoint between adjacent thresholds) -->
< text id = "ps-zone-spare" x = "260" text-anchor = "middle" fill = "#B78200" font-size = "10" font-style = "italic" visibility = "hidden" > Spare volume before spilling< / text >
< text id = "ps-zone-sewage" x = "260" text-anchor = "middle" fill = "#1F4E79" font-size = "10" font-style = "italic" visibility = "hidden" > Sewage + tank buffer< / text >
< text id = "ps-zone-buffer1" x = "260" text-anchor = "middle" fill = "#1F4E79" font-size = "10" font-style = "italic" visibility = "hidden" > Tank buffer< / text >
< text id = "ps-zone-buffer2" x = "260" text-anchor = "middle" fill = "#1F4E79" font-size = "10" font-style = "italic" visibility = "hidden" > Tank buffer< / text >
< text id = "ps-zone-dead" x = "260" text-anchor = "middle" fill = "#444" font-size = "10" font-style = "italic" visibility = "hidden" > Dead volume< / text >
Editor: interactive basin diagram — inputs placed at each threshold line
Replaces the static parameters-diagram-above-form-rows layout with a
single interactive SVG where every threshold input sits directly on
the tank at its proportional y-position. Typing a value repositions
the corresponding line + input + label live.
What moved into the diagram (via <foreignObject> holding real
<input> elements with their existing node-input-* IDs so Node-RED
save/restore is untouched):
basinHeight — top of tank (fixed at rim by definition)
overflowLevel — weir crest (red, dashed)
maxLevel — 100 % demand line (orange, dashed)
startLevel — ramp-start line (green, dashed)
minLevel — MGC-shutdown line (purple, dashed)
inflowLevel — Inlet arrow + input on left
outflowLevel — Outlet arrow + input on right
dryRunLevel — read-only, computed from outflow × (1+dryRunPct/100)
Also in the diagram:
- Dead-volume band fills the area below outflowLevel dynamically
- Warning ribbon appears below the tank if ordering invariants break
(mirrors specificClass._validateThresholdOrdering)
- All positions scale against the user's basinHeight; if empty, a
default 5 m scale is used just to keep the diagram readable
What stayed as regular form rows:
- Basin Volume (m³) — not a height, can't be placed on a y-axis
- minLevel / startLevel / maxLevel were in the Control Strategy >
Level-based section; removed from there and moved into the diagram
(the level-based subsection now contains a one-line pointer)
- Safety % inputs (dryRun, overfill) stay in the Safety section with
their derived-level readouts, now synced with the diagram
No schema changes, no field additions, no behaviour changes in the
runtime. Pure editor-UX.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 10:28:18 +02:00
<!-- basinHeight — always at tank rim (y=40 in viewBox coords) -->
< line id = "ps-line-basinHeight" x1 = "195" y1 = "40" x2 = "325" y2 = "40" stroke = "#333" stroke-width = "1.5" / >
< text id = "ps-label-basinHeight" x = "330" y = "44" fill = "#333" > basinHeight< / text >
< foreignObject id = "ps-fo-basinHeight" x = "425" y = "29" width = "70" height = "22" >
< input xmlns = "http://www.w3.org/1999/xhtml" type = "number" id = "node-input-basinHeight" min = "0" step = "0.1" / >
< / foreignObject >
< text id = "ps-unit-basinHeight" x = "500" y = "44" fill = "#555" > m< / text >
<!-- overflowLevel -->
< line id = "ps-line-overflowLevel" x1 = "195" x2 = "325" stroke = "#C0392B" stroke-dasharray = "4 2" stroke-width = "1.5" / >
< text id = "ps-label-overflowLevel" x = "330" fill = "#C0392B" > overflowLevel< / text >
< foreignObject id = "ps-fo-overflowLevel" x = "425" width = "70" height = "22" >
< input xmlns = "http://www.w3.org/1999/xhtml" type = "number" id = "node-input-overflowLevel" min = "0" step = "0.01" / >
< / foreignObject >
< text id = "ps-unit-overflowLevel" x = "500" fill = "#555" > m< / text >
<!-- maxLevel -->
< line id = "ps-line-maxLevel" x1 = "195" x2 = "325" stroke = "#D68910" stroke-dasharray = "4 2" stroke-width = "1.5" / >
< text id = "ps-label-maxLevel" x = "330" fill = "#D68910" > maxLevel< / text >
< foreignObject id = "ps-fo-maxLevel" x = "425" width = "70" height = "22" >
< input xmlns = "http://www.w3.org/1999/xhtml" type = "number" id = "node-input-maxLevel" min = "0" step = "0.01" / >
< / foreignObject >
< text id = "ps-unit-maxLevel" x = "500" fill = "#555" > m< / text >
<!-- startLevel -->
< line id = "ps-line-startLevel" x1 = "195" x2 = "325" stroke = "#1E8449" stroke-dasharray = "4 2" stroke-width = "1.5" / >
< text id = "ps-label-startLevel" x = "330" fill = "#1E8449" > startLevel< / text >
< foreignObject id = "ps-fo-startLevel" x = "425" width = "70" height = "22" >
< input xmlns = "http://www.w3.org/1999/xhtml" type = "number" id = "node-input-startLevel" min = "0" step = "0.01" / >
< / foreignObject >
< text id = "ps-unit-startLevel" x = "500" fill = "#555" > m< / text >
<!-- Inlet — arrow + input on the left -->
< line id = "ps-line-inflowLevel" x1 = "140" x2 = "200" stroke = "#1F4E79" stroke-width = "2" marker-end = "url(#ps-arrow)" / >
< text id = "ps-label-inflowLevel" x = "135" text-anchor = "end" fill = "#1F4E79" font-weight = "bold" > Inlet< / text >
< text id = "ps-sub-inflowLevel" x = "135" text-anchor = "end" fill = "#777" font-size = "9" > bottom of pipe< / text >
< foreignObject id = "ps-fo-inflowLevel" x = "5" width = "70" height = "22" >
< input xmlns = "http://www.w3.org/1999/xhtml" type = "number" id = "node-input-inflowLevel" min = "0" step = "0.01" / >
< / foreignObject >
< text id = "ps-unit-inflowLevel" x = "80" fill = "#555" > m< / text >
<!-- minLevel -->
< line id = "ps-line-minLevel" x1 = "195" x2 = "325" stroke = "#6C3483" stroke-dasharray = "4 2" stroke-width = "1.5" / >
< text id = "ps-label-minLevel" x = "330" fill = "#6C3483" > minLevel< / text >
< foreignObject id = "ps-fo-minLevel" x = "425" width = "70" height = "22" >
< input xmlns = "http://www.w3.org/1999/xhtml" type = "number" id = "node-input-minLevel" min = "0" step = "0.01" / >
< / foreignObject >
< text id = "ps-unit-minLevel" x = "500" fill = "#555" > m< / text >
<!-- dryRunLevel (derived, read - only) -->
< line id = "ps-line-dryRunLevel" x1 = "195" x2 = "325" stroke = "#C0392B" stroke-dasharray = "1 2" stroke-width = "1" opacity = "0.6" / >
< text id = "ps-label-dryRunLevel" x = "330" fill = "#C0392B" font-size = "10" font-style = "italic" > dryRunLevel ≈ — m (safety — from %)< / text >
<!-- Outlet — arrow on right, input below the threshold column -->
< line id = "ps-line-outflowLevel" x1 = "320" x2 = "360" stroke = "#1F4E79" stroke-width = "2" marker-end = "url(#ps-arrow)" / >
< text id = "ps-label-outflowLevel" x = "365" fill = "#1F4E79" font-weight = "bold" > Outlet< / text >
< text id = "ps-sub-outflowLevel" x = "365" fill = "#777" font-size = "9" > top of pipe< / text >
< foreignObject id = "ps-fo-outflowLevel" x = "425" width = "70" height = "22" >
< input xmlns = "http://www.w3.org/1999/xhtml" type = "number" id = "node-input-outflowLevel" min = "0" step = "0.01" / >
< / foreignObject >
< text id = "ps-unit-outflowLevel" x = "500" fill = "#555" > m< / text >
<!-- Floor / datum -->
2026-04-23 12:10:23 +02:00
< line x1 = "195" y1 = "380" x2 = "325" y2 = "380" stroke = "#000" stroke-width = "2" / >
< text x = "330" y = "384" fill = "#000" > 0 m (datum)< / text >
Editor: interactive basin diagram — inputs placed at each threshold line
Replaces the static parameters-diagram-above-form-rows layout with a
single interactive SVG where every threshold input sits directly on
the tank at its proportional y-position. Typing a value repositions
the corresponding line + input + label live.
What moved into the diagram (via <foreignObject> holding real
<input> elements with their existing node-input-* IDs so Node-RED
save/restore is untouched):
basinHeight — top of tank (fixed at rim by definition)
overflowLevel — weir crest (red, dashed)
maxLevel — 100 % demand line (orange, dashed)
startLevel — ramp-start line (green, dashed)
minLevel — MGC-shutdown line (purple, dashed)
inflowLevel — Inlet arrow + input on left
outflowLevel — Outlet arrow + input on right
dryRunLevel — read-only, computed from outflow × (1+dryRunPct/100)
Also in the diagram:
- Dead-volume band fills the area below outflowLevel dynamically
- Warning ribbon appears below the tank if ordering invariants break
(mirrors specificClass._validateThresholdOrdering)
- All positions scale against the user's basinHeight; if empty, a
default 5 m scale is used just to keep the diagram readable
What stayed as regular form rows:
- Basin Volume (m³) — not a height, can't be placed on a y-axis
- minLevel / startLevel / maxLevel were in the Control Strategy >
Level-based section; removed from there and moved into the diagram
(the level-based subsection now contains a one-line pointer)
- Safety % inputs (dryRun, overfill) stay in the Safety section with
their derived-level readouts, now synced with the diagram
No schema changes, no field additions, no behaviour changes in the
runtime. Pure editor-UX.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 10:28:18 +02:00
2026-04-23 10:41:16 +02:00
<!-- Leader lines: shown when the input row had to be nudged off its threshold's ideal y -->
< line id = "ps-leader-basinHeight" x1 = "0" y1 = "0" x2 = "0" y2 = "0" stroke = "#bbb" stroke-width = "0.6" stroke-dasharray = "2 2" visibility = "hidden" / >
< line id = "ps-leader-overflowLevel" x1 = "0" y1 = "0" x2 = "0" y2 = "0" stroke = "#bbb" stroke-width = "0.6" stroke-dasharray = "2 2" visibility = "hidden" / >
< line id = "ps-leader-maxLevel" x1 = "0" y1 = "0" x2 = "0" y2 = "0" stroke = "#bbb" stroke-width = "0.6" stroke-dasharray = "2 2" visibility = "hidden" / >
< line id = "ps-leader-startLevel" x1 = "0" y1 = "0" x2 = "0" y2 = "0" stroke = "#bbb" stroke-width = "0.6" stroke-dasharray = "2 2" visibility = "hidden" / >
< line id = "ps-leader-minLevel" x1 = "0" y1 = "0" x2 = "0" y2 = "0" stroke = "#bbb" stroke-width = "0.6" stroke-dasharray = "2 2" visibility = "hidden" / >
< line id = "ps-leader-dryRunLevel" x1 = "0" y1 = "0" x2 = "0" y2 = "0" stroke = "#bbb" stroke-width = "0.6" stroke-dasharray = "2 2" visibility = "hidden" / >
< line id = "ps-leader-outflowLevel" x1 = "0" y1 = "0" x2 = "0" y2 = "0" stroke = "#bbb" stroke-width = "0.6" stroke-dasharray = "2 2" visibility = "hidden" / >
Editor: interactive basin diagram — inputs placed at each threshold line
Replaces the static parameters-diagram-above-form-rows layout with a
single interactive SVG where every threshold input sits directly on
the tank at its proportional y-position. Typing a value repositions
the corresponding line + input + label live.
What moved into the diagram (via <foreignObject> holding real
<input> elements with their existing node-input-* IDs so Node-RED
save/restore is untouched):
basinHeight — top of tank (fixed at rim by definition)
overflowLevel — weir crest (red, dashed)
maxLevel — 100 % demand line (orange, dashed)
startLevel — ramp-start line (green, dashed)
minLevel — MGC-shutdown line (purple, dashed)
inflowLevel — Inlet arrow + input on left
outflowLevel — Outlet arrow + input on right
dryRunLevel — read-only, computed from outflow × (1+dryRunPct/100)
Also in the diagram:
- Dead-volume band fills the area below outflowLevel dynamically
- Warning ribbon appears below the tank if ordering invariants break
(mirrors specificClass._validateThresholdOrdering)
- All positions scale against the user's basinHeight; if empty, a
default 5 m scale is used just to keep the diagram readable
What stayed as regular form rows:
- Basin Volume (m³) — not a height, can't be placed on a y-axis
- minLevel / startLevel / maxLevel were in the Control Strategy >
Level-based section; removed from there and moved into the diagram
(the level-based subsection now contains a one-line pointer)
- Safety % inputs (dryRun, overfill) stay in the Safety section with
their derived-level readouts, now synced with the diagram
No schema changes, no field additions, no behaviour changes in the
runtime. Pure editor-UX.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 10:28:18 +02:00
<!-- Ordering - warning ribbon -->
2026-04-23 12:10:23 +02:00
< text id = "ps-warning" x = "260" y = "410" text-anchor = "middle" fill = "#C0392B" font-size = "10" font-style = "italic" visibility = "hidden" > < / text >
Editor: interactive basin diagram — inputs placed at each threshold line
Replaces the static parameters-diagram-above-form-rows layout with a
single interactive SVG where every threshold input sits directly on
the tank at its proportional y-position. Typing a value repositions
the corresponding line + input + label live.
What moved into the diagram (via <foreignObject> holding real
<input> elements with their existing node-input-* IDs so Node-RED
save/restore is untouched):
basinHeight — top of tank (fixed at rim by definition)
overflowLevel — weir crest (red, dashed)
maxLevel — 100 % demand line (orange, dashed)
startLevel — ramp-start line (green, dashed)
minLevel — MGC-shutdown line (purple, dashed)
inflowLevel — Inlet arrow + input on left
outflowLevel — Outlet arrow + input on right
dryRunLevel — read-only, computed from outflow × (1+dryRunPct/100)
Also in the diagram:
- Dead-volume band fills the area below outflowLevel dynamically
- Warning ribbon appears below the tank if ordering invariants break
(mirrors specificClass._validateThresholdOrdering)
- All positions scale against the user's basinHeight; if empty, a
default 5 m scale is used just to keep the diagram readable
What stayed as regular form rows:
- Basin Volume (m³) — not a height, can't be placed on a y-axis
- minLevel / startLevel / maxLevel were in the Control Strategy >
Level-based section; removed from there and moved into the diagram
(the level-based subsection now contains a one-line pointer)
- Safety % inputs (dryRun, overfill) stay in the Safety section with
their derived-level readouts, now synced with the diagram
No schema changes, no field additions, no behaviour changes in the
runtime. Pure editor-UX.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 10:28:18 +02:00
< / svg >
Editor: inline parameters diagram at top of Basin Geometry
~3 KB inline SVG showing the 5 threshold lines + inlet/outlet pipe
arrows + floor datum, using the same names as the editor fields
(basinHeight, overflowLevel, maxLevel, startLevel, minLevel,
dryRunLevel). No new inputs — purely a visual reminder of what
each field refers to, so operators don't have to alt-tab to the
wiki to figure out which pipe edge to measure.
Wrapped in <details open> so users can collapse it once they
know the layout — no forced scroll through the diagram on every
edit session.
Matches the vocabulary in wiki/diagrams/basin-model.drawio.svg
(inlet = bottom of pipe, outlet = top of pipe, 0 m datum at
basin floor, dryRunLevel derived from %).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 10:19:23 +02:00
2025-10-14 16:32:44 +02:00
< hr >
2025-11-27 17:46:24 +01:00
< h4 > Control Strategy< / h4 >
< div class = "form-row" >
< label for = "node-input-controlMode" > < i class = "fa fa-sliders" > < / i > Control mode< / label >
< select id = "node-input-controlMode" >
< option value = "none" > None / Manual< / option >
< option value = "levelbased" > Level-based< / option >
< option value = "flowbased" > Flow-based< / option >
< / select >
< / div >
< div id = "ps-mode-levelbased" class = "ps-mode-section" >
Editor: interactive basin diagram — inputs placed at each threshold line
Replaces the static parameters-diagram-above-form-rows layout with a
single interactive SVG where every threshold input sits directly on
the tank at its proportional y-position. Typing a value repositions
the corresponding line + input + label live.
What moved into the diagram (via <foreignObject> holding real
<input> elements with their existing node-input-* IDs so Node-RED
save/restore is untouched):
basinHeight — top of tank (fixed at rim by definition)
overflowLevel — weir crest (red, dashed)
maxLevel — 100 % demand line (orange, dashed)
startLevel — ramp-start line (green, dashed)
minLevel — MGC-shutdown line (purple, dashed)
inflowLevel — Inlet arrow + input on left
outflowLevel — Outlet arrow + input on right
dryRunLevel — read-only, computed from outflow × (1+dryRunPct/100)
Also in the diagram:
- Dead-volume band fills the area below outflowLevel dynamically
- Warning ribbon appears below the tank if ordering invariants break
(mirrors specificClass._validateThresholdOrdering)
- All positions scale against the user's basinHeight; if empty, a
default 5 m scale is used just to keep the diagram readable
What stayed as regular form rows:
- Basin Volume (m³) — not a height, can't be placed on a y-axis
- minLevel / startLevel / maxLevel were in the Control Strategy >
Level-based section; removed from there and moved into the diagram
(the level-based subsection now contains a one-line pointer)
- Safety % inputs (dryRun, overfill) stay in the Safety section with
their derived-level readouts, now synced with the diagram
No schema changes, no field additions, no behaviour changes in the
runtime. Pure editor-UX.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 10:28:18 +02:00
< p style = "font-size:12px;color:#777;margin:0;" > Level-based uses < code > minLevel< / code > / < code > startLevel< / code > / < code > maxLevel< / code > from the diagram above.< / p >
2025-11-27 17:46:24 +01:00
< / div >
< div id = "ps-mode-flowbased" class = "ps-mode-section" style = "display:none" >
< div class = "form-row" >
< label for = "node-input-flowSetpoint" > Flow setpoint< / label >
< input type = "number" id = "node-input-flowSetpoint" placeholder = "m3/h" / >
< / div >
< div class = "form-row" >
< label for = "node-input-flowDeadband" > Deadband< / label >
< input type = "number" id = "node-input-flowDeadband" placeholder = "m3/h" / >
< / div >
< / div >
< hr >
< h4 > Reference< / h4 >
2025-10-14 16:32:44 +02:00
<!-- Reference data -->
2025-11-20 12:15:46 +01:00
< div class = "form-row" >
< label for = "node-input-minHeightBasedOn" > < i class = "fa fa-arrows-v" > < / i > Minimum Height Based On< / label >
< select id = "node-input-minHeightBasedOn" style = "width:60%;" >
< option value = "inlet" > Inlet Elevation< / option >
< option value = "outlet" > Outlet Elevation< / option >
< / select >
< / div >
2025-10-07 18:05:54 +02:00
< div class = "form-row" >
2025-10-14 16:32:44 +02:00
< label for = "node-input-refHeight" > < i class = "fa fa-map-marker" > < / i > Reference height< / label >
< select id = "node-input-refHeight" style = "width:60%;" >
< option value = "NAP" > NAP< / option >
2025-10-07 18:05:54 +02:00
< / select >
< / div >
< div class = "form-row" >
Editor: pipe-edge conventions + live derived safety levels
### P1 — match diagram naming (labels only, no schema change)
- "Inlet Elevation" → "Inlet (bottom of pipe, m)"
- "Outlet Elevation" → "Outlet (top of pipe, m)"
- "Overflow Level" → "Overflow (weir crest, m)"
- "Basin Bottom (m Refheight)" → "Basin floor above datum (m)"
- Added a one-line banner at the top of Basin Geometry:
"All heights measured from the basin floor (0 m)."
These map directly to the clarifications added to basin-model.drawio.svg
so editor and diagram speak the same vocabulary.
### P3 — live derived safety levels next to the % fields
Low/High Volume Threshold fields now show the resulting trip level
live as the operator types:
Low Volume Threshold (%) [ 2 ] → dryRunLevel ≈ 0.21 m
High Volume Threshold (%) [ 98 ] → overfillLevel ≈ 4.41 m
Recomputed on every input/change of outflowLevel, inflowLevel,
overflowLevel, minHeightBasedOn, or either %. Pure UI feedback —
no schema change, no save-side change, same formulas as
specificClass._validateThresholdOrdering().
Also includes the user's latest basin-model.drawio.svg update
(inlet=bottom/outlet=top labels + datum annotation).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 09:58:17 +02:00
< label for = "node-input-basinBottomRef" > < i class = "fa fa-level-down" > < / i > Basin floor above datum (m)< / label >
2025-10-14 16:32:44 +02:00
< input type = "number" id = "node-input-basinBottomRef" step = "0.01" / >
2025-10-07 18:05:54 +02:00
< / div >
2025-11-25 14:57:39 +01:00
< hr >
2025-11-27 17:46:24 +01:00
< h4 > Safety< / h4 >
2025-11-25 14:57:39 +01:00
<!-- Safety settings -->
< div class = "form-row" >
< label for = "node-input-timeleftToFullOrEmptyThresholdSeconds" > < i class = "fa fa-clock-o" > < / i > Time To Empty/Full (s)< / label >
< input type = "number" id = "node-input-timeleftToFullOrEmptyThresholdSeconds" min = "0" step = "1" / >
< / div >
< div class = "form-row" >
< label for = "node-input-enableDryRunProtection" >
< i class = "fa fa-shield" > < / i > Dry-run Protection
< / label >
< input type = "checkbox" id = "node-input-enableDryRunProtection" style = "width:20px;vertical-align:baseline;" / >
< span > Prevent pumps from running on low volume< / span >
< / div >
< div class = "form-row" >
< label for = "node-input-dryRunThresholdPercent" style = "padding-left:20px;" > Low Volume Threshold (%)< / label >
Editor: pipe-edge conventions + live derived safety levels
### P1 — match diagram naming (labels only, no schema change)
- "Inlet Elevation" → "Inlet (bottom of pipe, m)"
- "Outlet Elevation" → "Outlet (top of pipe, m)"
- "Overflow Level" → "Overflow (weir crest, m)"
- "Basin Bottom (m Refheight)" → "Basin floor above datum (m)"
- Added a one-line banner at the top of Basin Geometry:
"All heights measured from the basin floor (0 m)."
These map directly to the clarifications added to basin-model.drawio.svg
so editor and diagram speak the same vocabulary.
### P3 — live derived safety levels next to the % fields
Low/High Volume Threshold fields now show the resulting trip level
live as the operator types:
Low Volume Threshold (%) [ 2 ] → dryRunLevel ≈ 0.21 m
High Volume Threshold (%) [ 98 ] → overfillLevel ≈ 4.41 m
Recomputed on every input/change of outflowLevel, inflowLevel,
overflowLevel, minHeightBasedOn, or either %. Pure UI feedback —
no schema change, no save-side change, same formulas as
specificClass._validateThresholdOrdering().
Also includes the user's latest basin-model.drawio.svg update
(inlet=bottom/outlet=top labels + datum annotation).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 09:58:17 +02:00
< input type = "number" id = "node-input-dryRunThresholdPercent" min = "0" max = "100" step = "0.1" style = "width:80px;" / >
< span id = "derived-dryRunLevel" style = "margin-left:8px;color:#777;font-size:12px;" > → dryRunLevel ≈ — m< / span >
2025-11-25 14:57:39 +01:00
< / div >
< div class = "form-row" >
< label for = "node-input-enableOverfillProtection" >
< i class = "fa fa-exclamation-triangle" > < / i > Overfill Protection
< / label >
< input type = "checkbox" id = "node-input-enableOverfillProtection" style = "width:20px;vertical-align:baseline;" / >
< span > Stop filling when approaching overflow< / span >
< / div >
< div class = "form-row" >
< label for = "node-input-overfillThresholdPercent" style = "padding-left:20px;" > High Volume Threshold (%)< / label >
Editor: pipe-edge conventions + live derived safety levels
### P1 — match diagram naming (labels only, no schema change)
- "Inlet Elevation" → "Inlet (bottom of pipe, m)"
- "Outlet Elevation" → "Outlet (top of pipe, m)"
- "Overflow Level" → "Overflow (weir crest, m)"
- "Basin Bottom (m Refheight)" → "Basin floor above datum (m)"
- Added a one-line banner at the top of Basin Geometry:
"All heights measured from the basin floor (0 m)."
These map directly to the clarifications added to basin-model.drawio.svg
so editor and diagram speak the same vocabulary.
### P3 — live derived safety levels next to the % fields
Low/High Volume Threshold fields now show the resulting trip level
live as the operator types:
Low Volume Threshold (%) [ 2 ] → dryRunLevel ≈ 0.21 m
High Volume Threshold (%) [ 98 ] → overfillLevel ≈ 4.41 m
Recomputed on every input/change of outflowLevel, inflowLevel,
overflowLevel, minHeightBasedOn, or either %. Pure UI feedback —
no schema change, no save-side change, same formulas as
specificClass._validateThresholdOrdering().
Also includes the user's latest basin-model.drawio.svg update
(inlet=bottom/outlet=top labels + datum annotation).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 09:58:17 +02:00
< input type = "number" id = "node-input-overfillThresholdPercent" min = "0" max = "100" step = "0.1" style = "width:80px;" / >
< span id = "derived-overfillLevel" style = "margin-left:8px;color:#777;font-size:12px;" > → overfillLevel ≈ — m< / span >
2025-11-25 14:57:39 +01:00
< / div >
2026-03-31 18:20:09 +02:00
< hr >
2026-03-12 16:39:25 +01:00
< h3 > Output Formats< / h3 >
< div class = "form-row" >
< label for = "node-input-processOutputFormat" > < i class = "fa fa-random" > < / i > Process Output< / label >
< select id = "node-input-processOutputFormat" style = "width:60%;" >
< option value = "process" > process< / option >
< option value = "json" > json< / option >
< option value = "csv" > csv< / option >
< / select >
< / div >
< div class = "form-row" >
< label for = "node-input-dbaseOutputFormat" > < i class = "fa fa-database" > < / i > Database Output< / label >
< select id = "node-input-dbaseOutputFormat" style = "width:60%;" >
< option value = "influxdb" > influxdb< / option >
< option value = "json" > json< / option >
< option value = "csv" > csv< / option >
< / select >
< / div >
2025-10-14 16:32:44 +02:00
<!-- Shared asset/logger/position menus -->
2025-10-07 18:05:54 +02:00
< div id = "asset-fields-placeholder" > < / div >
< div id = "logger-fields-placeholder" > < / div >
< div id = "position-fields-placeholder" > < / div >
2025-10-14 16:32:44 +02:00
2025-10-07 18:05:54 +02:00
< / script >
2025-10-14 13:51:32 +02:00
< script type = "text/html" data-help-name = "pumpingStation" >
2025-10-07 18:05:54 +02:00
< / script >