Level-armed shift, derived dryRunLevel, side-panel editor + manual q_out
Runtime (specificClass.js):
- Replace direction-based hysteresis with level-armed _shiftArmed state.
Arms when level rises past shiftLevel; disarms when level drops below
startLevel. While armed, ramp foot moves to startLevel and ramp top
to shiftLevel — both ends shift left, then saturate at 100 % up to
maxLevel.
- _scaleLevelToFlowPercent now takes (rampStartLevel, rampTopLevel) so
the saturation point follows the shift state.
- New setManualOutflow mirroring setManualInflow.
Adapter (nodeClass.js):
- Pipe enableShiftedRamp / shiftLevel through to control.levelbased.
- New q_out topic handler.
Editor (pumpingStation.html + new src/editor/ modules):
- Split monolithic <script> into modules: index.js (helpers),
basin-diagram.js, mode-preview.js, hover-couple.js, oneditprepare.js,
oneditsave.js — served via /pumpingStation/editor/:file.
- Mode preview redrawn per the SVG diagrams: OFF tier below 0 %, 0 %
flat from start→inlet, ramp inlet→max, optional shifted-down curve
start→shift with 100 % saturation past shift.
- Mode preview gains zone bands (dryRun / safetyLow / safe / safetyHigh /
overflow), level markers (dryRun derived, start, inlet, max, shift,
overflow), validation ribbon that blocks save on bad ordering.
- Auto-default shiftLevel to 0.9 × maxLevel on enable so the marker is
always visible.
- All level inputs moved to a side panel left of each diagram, color-
coded to match line strokes; hover-couple highlights the paired SVG
line on input focus / mouseover.
- Removed UI for non-static parameters: minHeightBasedOn,
pipelineLength, maxDischargeHead, staticHead, defaultFluid,
maxInflowRate, temperatureReferenceDegC,
timeleftToFullOrEmptyThresholdSeconds, inletPipeDiameter,
outletPipeDiameter, minLevel (now derived = dryRunLevel).
- foreignObject inputs in basin SVG removed (single source of truth in
side panel).
Dashboard example (examples/basic-dashboard.flow.json):
- Add manual Q_OUT slider + q_out builder mirroring the existing q_in
trio so the basin can be exercised end-to-end without a connected
rotating-machine downstream.
Tests (test/basic/specificClass.test.js):
- Replace direction-shift test with two new cases covering shift-disabled
hold-zone behaviour and shift-armed/disarmed transitions through
shiftLevel and startLevel boundaries. 53/53 tests pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 19:29:34 +02:00
|
|
|
// PumpingStation editor — interactive basin SVG (top of the editor).
|
|
|
|
|
// Places threshold lines, derived safety levels, zone labels, dead-volume
|
|
|
|
|
// band, and ordering warnings. Same formulas as
|
|
|
|
|
// specificClass._validateThresholdOrdering.
|
|
|
|
|
|
|
|
|
|
(function () {
|
|
|
|
|
const ns = window.PSEditor = window.PSEditor || {};
|
|
|
|
|
const fNum = (id) => ns.fNum(id);
|
|
|
|
|
|
|
|
|
|
// viewBox y bounds of the tank rect (now 120,40)..(240,380); width
|
|
|
|
|
// shrunk to 360 in the new side-panel layout. y-bounds unchanged.
|
|
|
|
|
const DIAG = { topY: 40, botY: 380 };
|
|
|
|
|
|
|
|
|
|
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));
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Place a row — line, label, input, unit all share the same y.
|
|
|
|
|
const placeItem = (id, y) => {
|
|
|
|
|
const line = document.getElementById(`ps-line-${id}`);
|
|
|
|
|
const label = document.getElementById(`ps-label-${id}`);
|
|
|
|
|
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}`);
|
|
|
|
|
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');
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
ns.basinDiagram = {
|
|
|
|
|
redraw() {
|
|
|
|
|
const basinH = fNum('basinHeight') || 5;
|
|
|
|
|
|
|
|
|
|
const refLow = fNum('outflowLevel');
|
|
|
|
|
const dryPct = fNum('dryRunThresholdPercent');
|
|
|
|
|
const highPct = fNum('highVolumeSafetyThresholdPercent');
|
|
|
|
|
const ovf = fNum('overflowLevel');
|
|
|
|
|
const dryLvl = (refLow != null && dryPct != null) ? refLow * (1 + dryPct / 100) : null;
|
|
|
|
|
const highLvl = (ovf != null && highPct != null) ? ovf * (highPct / 100) : null;
|
|
|
|
|
|
|
|
|
|
// Right-column stack. TWO anchors: basinHeight pinned at the rim,
|
|
|
|
|
// outflowLevel pinned at its proportional y. Two passes (top-down +
|
|
|
|
|
// bottom-up) maintain a minimum vertical gap.
|
|
|
|
|
const items = [
|
|
|
|
|
{ id: 'basinHeight', yIdeal: DIAG.topY, pinned: true },
|
|
|
|
|
{ id: 'overflowLevel', yIdeal: yForLevel(fNum('overflowLevel'), basinH) },
|
|
|
|
|
{ id: 'highVolumeSafetyLevel', yIdeal: yForLevel(highLvl, basinH) },
|
|
|
|
|
{ id: 'inflowLevelGuide', yIdeal: yForLevel(fNum('inflowLevel'), basinH) },
|
|
|
|
|
{ id: 'dryRunLevel', yIdeal: yForLevel(dryLvl, basinH) },
|
|
|
|
|
{ id: 'outflowLevel', yIdeal: yForLevel(fNum('outflowLevel'), basinH), pinned: true },
|
|
|
|
|
].filter(it => it.yIdeal != null);
|
|
|
|
|
|
|
|
|
|
const GAP = 36;
|
|
|
|
|
items.sort((a, b) => a.yIdeal - b.yIdeal);
|
|
|
|
|
for (const it of items) it.y = it.yIdeal;
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
for (const it of items) placeItem(it.id, it.y);
|
|
|
|
|
|
Editor: dynamic input bounds + full hierarchy validation, layout polish
Bounds (new src/editor/bounds.js):
- Sets HTML5 min/max on every level + percent input each redraw,
derived from the current values of related inputs so the spinner
stops at the basin hierarchy:
0 < outflowLevel < dryRunLevel < startLevel ≤ inflowLevel
≤ shiftLevel ≤ maxLevel ≤ overflowLevel ≤ basinHeight
- dryRunPercent capped so dryRunLevel ≤ startLevel given current outflow.
- shiftArmPercent ∈ [1, 100]; highVolumeSafety% ∈ [1, 100].
Validation:
- New visible ribbon above the basin diagram (#ps-basin-validation)
listing every hierarchy violation. The in-SVG warning text is now a
small reminder ("⚠ N ordering issues").
- basin-diagram.js owns hierarchy issues; mode-preview.js trimmed to
only own shift-specific issues (shift > start, shift ≤ max,
shiftArmPercent range, shiftLevel required-when-enabled).
- oneditsave blocks Deploy on the union of _psBasinValidationIssues
and _psModeValidationIssues with a RED.notify listing all problems.
Layout polish:
- Side panel widened to 220 px with minmax(0, 1fr) first column so long
labels can no longer push the rows past the panel edge.
- Basin SVG max-width 380 → 360, gap between side panel and SVG bumped
14 → 28 px. Tank shifted right (x=145 width=110) so the inlet
"bottom of pipe" sub-label is no longer clipped on the left edge.
- "0 m (datum)" moved below the tank (y=395, centred) so it can't
collide with "Outlet / top of pipe" when outflowLevel is near floor.
- Zone labels shortened (Spare / Sewage + buffer / Buffer / Dead vol)
and only show when the bracketing thresholds are ≥ 28 px apart, so
they never sit on a threshold label.
- Mode preview axis labels under the chart removed — line colour +
side-panel labels + hover-couple already identify each line. Stub
<text> elements left hidden to keep the redraw loop simple. Arm-%
line + label trimmed in 10 px on the right so they're not clipped.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 14:10:22 +02:00
|
|
|
// Zone labels show only when the gap between the bracketing
|
|
|
|
|
// thresholds is at least MIN_ZONE_GAP px high — otherwise the label
|
|
|
|
|
// collides with one of the threshold labels (which sit at threshold
|
|
|
|
|
// y ±6 px text-height). 28 px keeps a 6 px clear gap above and
|
|
|
|
|
// below the zone label.
|
|
|
|
|
const MIN_ZONE_GAP = 28;
|
Level-armed shift, derived dryRunLevel, side-panel editor + manual q_out
Runtime (specificClass.js):
- Replace direction-based hysteresis with level-armed _shiftArmed state.
Arms when level rises past shiftLevel; disarms when level drops below
startLevel. While armed, ramp foot moves to startLevel and ramp top
to shiftLevel — both ends shift left, then saturate at 100 % up to
maxLevel.
- _scaleLevelToFlowPercent now takes (rampStartLevel, rampTopLevel) so
the saturation point follows the shift state.
- New setManualOutflow mirroring setManualInflow.
Adapter (nodeClass.js):
- Pipe enableShiftedRamp / shiftLevel through to control.levelbased.
- New q_out topic handler.
Editor (pumpingStation.html + new src/editor/ modules):
- Split monolithic <script> into modules: index.js (helpers),
basin-diagram.js, mode-preview.js, hover-couple.js, oneditprepare.js,
oneditsave.js — served via /pumpingStation/editor/:file.
- Mode preview redrawn per the SVG diagrams: OFF tier below 0 %, 0 %
flat from start→inlet, ramp inlet→max, optional shifted-down curve
start→shift with 100 % saturation past shift.
- Mode preview gains zone bands (dryRun / safetyLow / safe / safetyHigh /
overflow), level markers (dryRun derived, start, inlet, max, shift,
overflow), validation ribbon that blocks save on bad ordering.
- Auto-default shiftLevel to 0.9 × maxLevel on enable so the marker is
always visible.
- All level inputs moved to a side panel left of each diagram, color-
coded to match line strokes; hover-couple highlights the paired SVG
line on input focus / mouseover.
- Removed UI for non-static parameters: minHeightBasedOn,
pipelineLength, maxDischargeHead, staticHead, defaultFluid,
maxInflowRate, temperatureReferenceDegC,
timeleftToFullOrEmptyThresholdSeconds, inletPipeDiameter,
outletPipeDiameter, minLevel (now derived = dryRunLevel).
- foreignObject inputs in basin SVG removed (single source of truth in
side panel).
Dashboard example (examples/basic-dashboard.flow.json):
- Add manual Q_OUT slider + q_out builder mirroring the existing q_in
trio so the basin can be exercised end-to-end without a connected
rotating-machine downstream.
Tests (test/basic/specificClass.test.js):
- Replace direction-shift test with two new cases covering shift-disabled
hold-zone behaviour and shift-armed/disarmed transitions through
shiftLevel and startLevel boundaries. 53/53 tests pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 19:29:34 +02:00
|
|
|
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);
|
Editor: dynamic input bounds + full hierarchy validation, layout polish
Bounds (new src/editor/bounds.js):
- Sets HTML5 min/max on every level + percent input each redraw,
derived from the current values of related inputs so the spinner
stops at the basin hierarchy:
0 < outflowLevel < dryRunLevel < startLevel ≤ inflowLevel
≤ shiftLevel ≤ maxLevel ≤ overflowLevel ≤ basinHeight
- dryRunPercent capped so dryRunLevel ≤ startLevel given current outflow.
- shiftArmPercent ∈ [1, 100]; highVolumeSafety% ∈ [1, 100].
Validation:
- New visible ribbon above the basin diagram (#ps-basin-validation)
listing every hierarchy violation. The in-SVG warning text is now a
small reminder ("⚠ N ordering issues").
- basin-diagram.js owns hierarchy issues; mode-preview.js trimmed to
only own shift-specific issues (shift > start, shift ≤ max,
shiftArmPercent range, shiftLevel required-when-enabled).
- oneditsave blocks Deploy on the union of _psBasinValidationIssues
and _psModeValidationIssues with a RED.notify listing all problems.
Layout polish:
- Side panel widened to 220 px with minmax(0, 1fr) first column so long
labels can no longer push the rows past the panel edge.
- Basin SVG max-width 380 → 360, gap between side panel and SVG bumped
14 → 28 px. Tank shifted right (x=145 width=110) so the inlet
"bottom of pipe" sub-label is no longer clipped on the left edge.
- "0 m (datum)" moved below the tank (y=395, centred) so it can't
collide with "Outlet / top of pipe" when outflowLevel is near floor.
- Zone labels shortened (Spare / Sewage + buffer / Buffer / Dead vol)
and only show when the bracketing thresholds are ≥ 28 px apart, so
they never sit on a threshold label.
- Mode preview axis labels under the chart removed — line colour +
side-panel labels + hover-couple already identify each line. Stub
<text> elements left hidden to keep the redraw loop simple. Arm-%
line + label trimmed in 10 px on the right so they're not clipped.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 14:10:22 +02:00
|
|
|
if (!top || !bot || (bot.y - top.y) < MIN_ZONE_GAP) {
|
Level-armed shift, derived dryRunLevel, side-panel editor + manual q_out
Runtime (specificClass.js):
- Replace direction-based hysteresis with level-armed _shiftArmed state.
Arms when level rises past shiftLevel; disarms when level drops below
startLevel. While armed, ramp foot moves to startLevel and ramp top
to shiftLevel — both ends shift left, then saturate at 100 % up to
maxLevel.
- _scaleLevelToFlowPercent now takes (rampStartLevel, rampTopLevel) so
the saturation point follows the shift state.
- New setManualOutflow mirroring setManualInflow.
Adapter (nodeClass.js):
- Pipe enableShiftedRamp / shiftLevel through to control.levelbased.
- New q_out topic handler.
Editor (pumpingStation.html + new src/editor/ modules):
- Split monolithic <script> into modules: index.js (helpers),
basin-diagram.js, mode-preview.js, hover-couple.js, oneditprepare.js,
oneditsave.js — served via /pumpingStation/editor/:file.
- Mode preview redrawn per the SVG diagrams: OFF tier below 0 %, 0 %
flat from start→inlet, ramp inlet→max, optional shifted-down curve
start→shift with 100 % saturation past shift.
- Mode preview gains zone bands (dryRun / safetyLow / safe / safetyHigh /
overflow), level markers (dryRun derived, start, inlet, max, shift,
overflow), validation ribbon that blocks save on bad ordering.
- Auto-default shiftLevel to 0.9 × maxLevel on enable so the marker is
always visible.
- All level inputs moved to a side panel left of each diagram, color-
coded to match line strokes; hover-couple highlights the paired SVG
line on input focus / mouseover.
- Removed UI for non-static parameters: minHeightBasedOn,
pipelineLength, maxDischargeHead, staticHead, defaultFluid,
maxInflowRate, temperatureReferenceDegC,
timeleftToFullOrEmptyThresholdSeconds, inletPipeDiameter,
outletPipeDiameter, minLevel (now derived = dryRunLevel).
- foreignObject inputs in basin SVG removed (single source of truth in
side panel).
Dashboard example (examples/basic-dashboard.flow.json):
- Add manual Q_OUT slider + q_out builder mirroring the existing q_in
trio so the basin can be exercised end-to-end without a connected
rotating-machine downstream.
Tests (test/basic/specificClass.test.js):
- Replace direction-shift test with two new cases covering shift-disabled
hold-zone behaviour and shift-armed/disarmed transitions through
shiftLevel and startLevel boundaries. 53/53 tests pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 19:29:34 +02:00
|
|
|
el.setAttribute('visibility', 'hidden'); return;
|
|
|
|
|
}
|
|
|
|
|
el.setAttribute('y', (top.y + bot.y) / 2 + 3);
|
|
|
|
|
el.setAttribute('visibility', 'visible');
|
|
|
|
|
};
|
|
|
|
|
placeZone('spare', 'overflowLevel', 'highVolumeSafetyLevel');
|
|
|
|
|
placeZone('sewage', 'highVolumeSafetyLevel', 'inflowLevelGuide');
|
|
|
|
|
placeZone('buffer1', 'inflowLevelGuide', 'dryRunLevel');
|
|
|
|
|
placeZone('buffer2', 'dryRunLevel', 'outflowLevel');
|
|
|
|
|
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');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// SVG labels — keep them short, side panel shows the numeric value.
|
|
|
|
|
const dryLbl = document.getElementById('ps-label-dryRunLevel');
|
|
|
|
|
if (dryLbl) dryLbl.textContent = 'dryRunLevel';
|
|
|
|
|
const highLbl = document.getElementById('ps-label-highVolumeSafetyLevel');
|
|
|
|
|
if (highLbl) highLbl.textContent = 'highVolumeSafety';
|
|
|
|
|
|
|
|
|
|
// Side-panel read-only displays — number only ("m" is shown in the unit span).
|
|
|
|
|
const fmt = (v) => Number.isFinite(v) ? v.toFixed(2) : '—';
|
|
|
|
|
const d1 = document.getElementById('derived-dryRunLevel');
|
|
|
|
|
if (d1) d1.textContent = fmt(dryLvl);
|
|
|
|
|
const d2 = document.getElementById('derived-highVolumeSafetyLevel');
|
|
|
|
|
if (d2) d2.textContent = fmt(highLvl);
|
|
|
|
|
|
Editor: dynamic input bounds + full hierarchy validation, layout polish
Bounds (new src/editor/bounds.js):
- Sets HTML5 min/max on every level + percent input each redraw,
derived from the current values of related inputs so the spinner
stops at the basin hierarchy:
0 < outflowLevel < dryRunLevel < startLevel ≤ inflowLevel
≤ shiftLevel ≤ maxLevel ≤ overflowLevel ≤ basinHeight
- dryRunPercent capped so dryRunLevel ≤ startLevel given current outflow.
- shiftArmPercent ∈ [1, 100]; highVolumeSafety% ∈ [1, 100].
Validation:
- New visible ribbon above the basin diagram (#ps-basin-validation)
listing every hierarchy violation. The in-SVG warning text is now a
small reminder ("⚠ N ordering issues").
- basin-diagram.js owns hierarchy issues; mode-preview.js trimmed to
only own shift-specific issues (shift > start, shift ≤ max,
shiftArmPercent range, shiftLevel required-when-enabled).
- oneditsave blocks Deploy on the union of _psBasinValidationIssues
and _psModeValidationIssues with a RED.notify listing all problems.
Layout polish:
- Side panel widened to 220 px with minmax(0, 1fr) first column so long
labels can no longer push the rows past the panel edge.
- Basin SVG max-width 380 → 360, gap between side panel and SVG bumped
14 → 28 px. Tank shifted right (x=145 width=110) so the inlet
"bottom of pipe" sub-label is no longer clipped on the left edge.
- "0 m (datum)" moved below the tank (y=395, centred) so it can't
collide with "Outlet / top of pipe" when outflowLevel is near floor.
- Zone labels shortened (Spare / Sewage + buffer / Buffer / Dead vol)
and only show when the bracketing thresholds are ≥ 28 px apart, so
they never sit on a threshold label.
- Mode preview axis labels under the chart removed — line colour +
side-panel labels + hover-couple already identify each line. Stub
<text> elements left hidden to keep the redraw loop simple. Arm-%
line + label trimmed in 10 px on the right so they're not clipped.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 14:10:22 +02:00
|
|
|
// Hierarchy validation. Soft '≤' relations follow the user's choice:
|
|
|
|
|
// start ≤ inflow, max ≤ overflow, overflow ≤ basinHeight (equality OK).
|
|
|
|
|
// dryRunLevel must be < startLevel strictly (otherwise the runtime
|
|
|
|
|
// would trip dry-run before it could ramp).
|
|
|
|
|
// Re-read the raw value (basinH falls back to 5 for diagram scaling;
|
|
|
|
|
// here we want null when the user hasn't entered anything so the
|
|
|
|
|
// ≤-checks below are skipped rather than false-flagged).
|
|
|
|
|
const basinHraw = fNum('basinHeight');
|
|
|
|
|
const start = fNum('startLevel');
|
|
|
|
|
const inlet = fNum('inflowLevel');
|
|
|
|
|
const max = fNum('maxLevel');
|
|
|
|
|
const ovfl = fNum('overflowLevel');
|
Level-armed shift, derived dryRunLevel, side-panel editor + manual q_out
Runtime (specificClass.js):
- Replace direction-based hysteresis with level-armed _shiftArmed state.
Arms when level rises past shiftLevel; disarms when level drops below
startLevel. While armed, ramp foot moves to startLevel and ramp top
to shiftLevel — both ends shift left, then saturate at 100 % up to
maxLevel.
- _scaleLevelToFlowPercent now takes (rampStartLevel, rampTopLevel) so
the saturation point follows the shift state.
- New setManualOutflow mirroring setManualInflow.
Adapter (nodeClass.js):
- Pipe enableShiftedRamp / shiftLevel through to control.levelbased.
- New q_out topic handler.
Editor (pumpingStation.html + new src/editor/ modules):
- Split monolithic <script> into modules: index.js (helpers),
basin-diagram.js, mode-preview.js, hover-couple.js, oneditprepare.js,
oneditsave.js — served via /pumpingStation/editor/:file.
- Mode preview redrawn per the SVG diagrams: OFF tier below 0 %, 0 %
flat from start→inlet, ramp inlet→max, optional shifted-down curve
start→shift with 100 % saturation past shift.
- Mode preview gains zone bands (dryRun / safetyLow / safe / safetyHigh /
overflow), level markers (dryRun derived, start, inlet, max, shift,
overflow), validation ribbon that blocks save on bad ordering.
- Auto-default shiftLevel to 0.9 × maxLevel on enable so the marker is
always visible.
- All level inputs moved to a side panel left of each diagram, color-
coded to match line strokes; hover-couple highlights the paired SVG
line on input focus / mouseover.
- Removed UI for non-static parameters: minHeightBasedOn,
pipelineLength, maxDischargeHead, staticHead, defaultFluid,
maxInflowRate, temperatureReferenceDegC,
timeleftToFullOrEmptyThresholdSeconds, inletPipeDiameter,
outletPipeDiameter, minLevel (now derived = dryRunLevel).
- foreignObject inputs in basin SVG removed (single source of truth in
side panel).
Dashboard example (examples/basic-dashboard.flow.json):
- Add manual Q_OUT slider + q_out builder mirroring the existing q_in
trio so the basin can be exercised end-to-end without a connected
rotating-machine downstream.
Tests (test/basic/specificClass.test.js):
- Replace direction-shift test with two new cases covering shift-disabled
hold-zone behaviour and shift-armed/disarmed transitions through
shiftLevel and startLevel boundaries. 53/53 tests pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 19:29:34 +02:00
|
|
|
const issues = [];
|
Editor: dynamic input bounds + full hierarchy validation, layout polish
Bounds (new src/editor/bounds.js):
- Sets HTML5 min/max on every level + percent input each redraw,
derived from the current values of related inputs so the spinner
stops at the basin hierarchy:
0 < outflowLevel < dryRunLevel < startLevel ≤ inflowLevel
≤ shiftLevel ≤ maxLevel ≤ overflowLevel ≤ basinHeight
- dryRunPercent capped so dryRunLevel ≤ startLevel given current outflow.
- shiftArmPercent ∈ [1, 100]; highVolumeSafety% ∈ [1, 100].
Validation:
- New visible ribbon above the basin diagram (#ps-basin-validation)
listing every hierarchy violation. The in-SVG warning text is now a
small reminder ("⚠ N ordering issues").
- basin-diagram.js owns hierarchy issues; mode-preview.js trimmed to
only own shift-specific issues (shift > start, shift ≤ max,
shiftArmPercent range, shiftLevel required-when-enabled).
- oneditsave blocks Deploy on the union of _psBasinValidationIssues
and _psModeValidationIssues with a RED.notify listing all problems.
Layout polish:
- Side panel widened to 220 px with minmax(0, 1fr) first column so long
labels can no longer push the rows past the panel edge.
- Basin SVG max-width 380 → 360, gap between side panel and SVG bumped
14 → 28 px. Tank shifted right (x=145 width=110) so the inlet
"bottom of pipe" sub-label is no longer clipped on the left edge.
- "0 m (datum)" moved below the tank (y=395, centred) so it can't
collide with "Outlet / top of pipe" when outflowLevel is near floor.
- Zone labels shortened (Spare / Sewage + buffer / Buffer / Dead vol)
and only show when the bracketing thresholds are ≥ 28 px apart, so
they never sit on a threshold label.
- Mode preview axis labels under the chart removed — line colour +
side-panel labels + hover-couple already identify each line. Stub
<text> elements left hidden to keep the redraw loop simple. Arm-%
line + label trimmed in 10 px on the right so they're not clipped.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 14:10:22 +02:00
|
|
|
const ok = (a, b, op) => {
|
|
|
|
|
if (!Number.isFinite(a) || !Number.isFinite(b)) return true;
|
|
|
|
|
return op === '<' ? a < b : a <= b;
|
|
|
|
|
};
|
|
|
|
|
if (Number.isFinite(refLow) && refLow <= 0)
|
|
|
|
|
issues.push('outflowLevel must be > 0');
|
|
|
|
|
if (!ok(dryLvl, start, '<'))
|
|
|
|
|
issues.push(`dryRunLevel (${(dryLvl ?? NaN).toFixed(2)} m, derived) must be < startLevel — lower dryRun% or raise startLevel`);
|
|
|
|
|
if (!ok(start, inlet, '<='))
|
|
|
|
|
issues.push('startLevel must be ≤ inflowLevel');
|
|
|
|
|
if (!ok(inlet, max, '<='))
|
|
|
|
|
issues.push('inflowLevel must be ≤ maxLevel');
|
|
|
|
|
if (!ok(max, ovfl, '<='))
|
|
|
|
|
issues.push('maxLevel must be ≤ overflowLevel');
|
|
|
|
|
if (!ok(ovfl, basinHraw, '<='))
|
|
|
|
|
issues.push('overflowLevel must be ≤ basinHeight');
|
|
|
|
|
|
|
|
|
|
// Visible ribbon above the basin diagram.
|
|
|
|
|
const warnDiv = document.getElementById('ps-basin-validation');
|
|
|
|
|
if (warnDiv) {
|
|
|
|
|
if (issues.length) {
|
|
|
|
|
warnDiv.innerHTML = '⚠ Fix before deploy:<ul style="margin:4px 0 0 18px;padding:0;">'
|
|
|
|
|
+ issues.map((i) => `<li>${i}</li>`).join('') + '</ul>';
|
|
|
|
|
warnDiv.style.display = '';
|
|
|
|
|
} else {
|
|
|
|
|
warnDiv.style.display = 'none';
|
|
|
|
|
}
|
Level-armed shift, derived dryRunLevel, side-panel editor + manual q_out
Runtime (specificClass.js):
- Replace direction-based hysteresis with level-armed _shiftArmed state.
Arms when level rises past shiftLevel; disarms when level drops below
startLevel. While armed, ramp foot moves to startLevel and ramp top
to shiftLevel — both ends shift left, then saturate at 100 % up to
maxLevel.
- _scaleLevelToFlowPercent now takes (rampStartLevel, rampTopLevel) so
the saturation point follows the shift state.
- New setManualOutflow mirroring setManualInflow.
Adapter (nodeClass.js):
- Pipe enableShiftedRamp / shiftLevel through to control.levelbased.
- New q_out topic handler.
Editor (pumpingStation.html + new src/editor/ modules):
- Split monolithic <script> into modules: index.js (helpers),
basin-diagram.js, mode-preview.js, hover-couple.js, oneditprepare.js,
oneditsave.js — served via /pumpingStation/editor/:file.
- Mode preview redrawn per the SVG diagrams: OFF tier below 0 %, 0 %
flat from start→inlet, ramp inlet→max, optional shifted-down curve
start→shift with 100 % saturation past shift.
- Mode preview gains zone bands (dryRun / safetyLow / safe / safetyHigh /
overflow), level markers (dryRun derived, start, inlet, max, shift,
overflow), validation ribbon that blocks save on bad ordering.
- Auto-default shiftLevel to 0.9 × maxLevel on enable so the marker is
always visible.
- All level inputs moved to a side panel left of each diagram, color-
coded to match line strokes; hover-couple highlights the paired SVG
line on input focus / mouseover.
- Removed UI for non-static parameters: minHeightBasedOn,
pipelineLength, maxDischargeHead, staticHead, defaultFluid,
maxInflowRate, temperatureReferenceDegC,
timeleftToFullOrEmptyThresholdSeconds, inletPipeDiameter,
outletPipeDiameter, minLevel (now derived = dryRunLevel).
- foreignObject inputs in basin SVG removed (single source of truth in
side panel).
Dashboard example (examples/basic-dashboard.flow.json):
- Add manual Q_OUT slider + q_out builder mirroring the existing q_in
trio so the basin can be exercised end-to-end without a connected
rotating-machine downstream.
Tests (test/basic/specificClass.test.js):
- Replace direction-shift test with two new cases covering shift-disabled
hold-zone behaviour and shift-armed/disarmed transitions through
shiftLevel and startLevel boundaries. 53/53 tests pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 19:29:34 +02:00
|
|
|
}
|
Editor: dynamic input bounds + full hierarchy validation, layout polish
Bounds (new src/editor/bounds.js):
- Sets HTML5 min/max on every level + percent input each redraw,
derived from the current values of related inputs so the spinner
stops at the basin hierarchy:
0 < outflowLevel < dryRunLevel < startLevel ≤ inflowLevel
≤ shiftLevel ≤ maxLevel ≤ overflowLevel ≤ basinHeight
- dryRunPercent capped so dryRunLevel ≤ startLevel given current outflow.
- shiftArmPercent ∈ [1, 100]; highVolumeSafety% ∈ [1, 100].
Validation:
- New visible ribbon above the basin diagram (#ps-basin-validation)
listing every hierarchy violation. The in-SVG warning text is now a
small reminder ("⚠ N ordering issues").
- basin-diagram.js owns hierarchy issues; mode-preview.js trimmed to
only own shift-specific issues (shift > start, shift ≤ max,
shiftArmPercent range, shiftLevel required-when-enabled).
- oneditsave blocks Deploy on the union of _psBasinValidationIssues
and _psModeValidationIssues with a RED.notify listing all problems.
Layout polish:
- Side panel widened to 220 px with minmax(0, 1fr) first column so long
labels can no longer push the rows past the panel edge.
- Basin SVG max-width 380 → 360, gap between side panel and SVG bumped
14 → 28 px. Tank shifted right (x=145 width=110) so the inlet
"bottom of pipe" sub-label is no longer clipped on the left edge.
- "0 m (datum)" moved below the tank (y=395, centred) so it can't
collide with "Outlet / top of pipe" when outflowLevel is near floor.
- Zone labels shortened (Spare / Sewage + buffer / Buffer / Dead vol)
and only show when the bracketing thresholds are ≥ 28 px apart, so
they never sit on a threshold label.
- Mode preview axis labels under the chart removed — line colour +
side-panel labels + hover-couple already identify each line. Stub
<text> elements left hidden to keep the redraw loop simple. Arm-%
line + label trimmed in 10 px on the right so they're not clipped.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 14:10:22 +02:00
|
|
|
// Legacy in-SVG warning text — kept for the small reminder inside
|
|
|
|
|
// the diagram. Only shows the count.
|
|
|
|
|
const warn = document.getElementById('ps-warning');
|
Level-armed shift, derived dryRunLevel, side-panel editor + manual q_out
Runtime (specificClass.js):
- Replace direction-based hysteresis with level-armed _shiftArmed state.
Arms when level rises past shiftLevel; disarms when level drops below
startLevel. While armed, ramp foot moves to startLevel and ramp top
to shiftLevel — both ends shift left, then saturate at 100 % up to
maxLevel.
- _scaleLevelToFlowPercent now takes (rampStartLevel, rampTopLevel) so
the saturation point follows the shift state.
- New setManualOutflow mirroring setManualInflow.
Adapter (nodeClass.js):
- Pipe enableShiftedRamp / shiftLevel through to control.levelbased.
- New q_out topic handler.
Editor (pumpingStation.html + new src/editor/ modules):
- Split monolithic <script> into modules: index.js (helpers),
basin-diagram.js, mode-preview.js, hover-couple.js, oneditprepare.js,
oneditsave.js — served via /pumpingStation/editor/:file.
- Mode preview redrawn per the SVG diagrams: OFF tier below 0 %, 0 %
flat from start→inlet, ramp inlet→max, optional shifted-down curve
start→shift with 100 % saturation past shift.
- Mode preview gains zone bands (dryRun / safetyLow / safe / safetyHigh /
overflow), level markers (dryRun derived, start, inlet, max, shift,
overflow), validation ribbon that blocks save on bad ordering.
- Auto-default shiftLevel to 0.9 × maxLevel on enable so the marker is
always visible.
- All level inputs moved to a side panel left of each diagram, color-
coded to match line strokes; hover-couple highlights the paired SVG
line on input focus / mouseover.
- Removed UI for non-static parameters: minHeightBasedOn,
pipelineLength, maxDischargeHead, staticHead, defaultFluid,
maxInflowRate, temperatureReferenceDegC,
timeleftToFullOrEmptyThresholdSeconds, inletPipeDiameter,
outletPipeDiameter, minLevel (now derived = dryRunLevel).
- foreignObject inputs in basin SVG removed (single source of truth in
side panel).
Dashboard example (examples/basic-dashboard.flow.json):
- Add manual Q_OUT slider + q_out builder mirroring the existing q_in
trio so the basin can be exercised end-to-end without a connected
rotating-machine downstream.
Tests (test/basic/specificClass.test.js):
- Replace direction-shift test with two new cases covering shift-disabled
hold-zone behaviour and shift-armed/disarmed transitions through
shiftLevel and startLevel boundaries. 53/53 tests pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 19:29:34 +02:00
|
|
|
if (warn) {
|
Editor: dynamic input bounds + full hierarchy validation, layout polish
Bounds (new src/editor/bounds.js):
- Sets HTML5 min/max on every level + percent input each redraw,
derived from the current values of related inputs so the spinner
stops at the basin hierarchy:
0 < outflowLevel < dryRunLevel < startLevel ≤ inflowLevel
≤ shiftLevel ≤ maxLevel ≤ overflowLevel ≤ basinHeight
- dryRunPercent capped so dryRunLevel ≤ startLevel given current outflow.
- shiftArmPercent ∈ [1, 100]; highVolumeSafety% ∈ [1, 100].
Validation:
- New visible ribbon above the basin diagram (#ps-basin-validation)
listing every hierarchy violation. The in-SVG warning text is now a
small reminder ("⚠ N ordering issues").
- basin-diagram.js owns hierarchy issues; mode-preview.js trimmed to
only own shift-specific issues (shift > start, shift ≤ max,
shiftArmPercent range, shiftLevel required-when-enabled).
- oneditsave blocks Deploy on the union of _psBasinValidationIssues
and _psModeValidationIssues with a RED.notify listing all problems.
Layout polish:
- Side panel widened to 220 px with minmax(0, 1fr) first column so long
labels can no longer push the rows past the panel edge.
- Basin SVG max-width 380 → 360, gap between side panel and SVG bumped
14 → 28 px. Tank shifted right (x=145 width=110) so the inlet
"bottom of pipe" sub-label is no longer clipped on the left edge.
- "0 m (datum)" moved below the tank (y=395, centred) so it can't
collide with "Outlet / top of pipe" when outflowLevel is near floor.
- Zone labels shortened (Spare / Sewage + buffer / Buffer / Dead vol)
and only show when the bracketing thresholds are ≥ 28 px apart, so
they never sit on a threshold label.
- Mode preview axis labels under the chart removed — line colour +
side-panel labels + hover-couple already identify each line. Stub
<text> elements left hidden to keep the redraw loop simple. Arm-%
line + label trimmed in 10 px on the right so they're not clipped.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 14:10:22 +02:00
|
|
|
if (issues.length) {
|
|
|
|
|
warn.setAttribute('visibility', 'visible');
|
|
|
|
|
warn.textContent = `⚠ ${issues.length} ordering issue${issues.length > 1 ? 's' : ''}`;
|
|
|
|
|
} else {
|
|
|
|
|
warn.setAttribute('visibility', 'hidden');
|
|
|
|
|
}
|
Level-armed shift, derived dryRunLevel, side-panel editor + manual q_out
Runtime (specificClass.js):
- Replace direction-based hysteresis with level-armed _shiftArmed state.
Arms when level rises past shiftLevel; disarms when level drops below
startLevel. While armed, ramp foot moves to startLevel and ramp top
to shiftLevel — both ends shift left, then saturate at 100 % up to
maxLevel.
- _scaleLevelToFlowPercent now takes (rampStartLevel, rampTopLevel) so
the saturation point follows the shift state.
- New setManualOutflow mirroring setManualInflow.
Adapter (nodeClass.js):
- Pipe enableShiftedRamp / shiftLevel through to control.levelbased.
- New q_out topic handler.
Editor (pumpingStation.html + new src/editor/ modules):
- Split monolithic <script> into modules: index.js (helpers),
basin-diagram.js, mode-preview.js, hover-couple.js, oneditprepare.js,
oneditsave.js — served via /pumpingStation/editor/:file.
- Mode preview redrawn per the SVG diagrams: OFF tier below 0 %, 0 %
flat from start→inlet, ramp inlet→max, optional shifted-down curve
start→shift with 100 % saturation past shift.
- Mode preview gains zone bands (dryRun / safetyLow / safe / safetyHigh /
overflow), level markers (dryRun derived, start, inlet, max, shift,
overflow), validation ribbon that blocks save on bad ordering.
- Auto-default shiftLevel to 0.9 × maxLevel on enable so the marker is
always visible.
- All level inputs moved to a side panel left of each diagram, color-
coded to match line strokes; hover-couple highlights the paired SVG
line on input focus / mouseover.
- Removed UI for non-static parameters: minHeightBasedOn,
pipelineLength, maxDischargeHead, staticHead, defaultFluid,
maxInflowRate, temperatureReferenceDegC,
timeleftToFullOrEmptyThresholdSeconds, inletPipeDiameter,
outletPipeDiameter, minLevel (now derived = dryRunLevel).
- foreignObject inputs in basin SVG removed (single source of truth in
side panel).
Dashboard example (examples/basic-dashboard.flow.json):
- Add manual Q_OUT slider + q_out builder mirroring the existing q_in
trio so the basin can be exercised end-to-end without a connected
rotating-machine downstream.
Tests (test/basic/specificClass.test.js):
- Replace direction-shift test with two new cases covering shift-disabled
hold-zone behaviour and shift-armed/disarmed transitions through
shiftLevel and startLevel boundaries. 53/53 tests pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 19:29:34 +02:00
|
|
|
}
|
Editor: dynamic input bounds + full hierarchy validation, layout polish
Bounds (new src/editor/bounds.js):
- Sets HTML5 min/max on every level + percent input each redraw,
derived from the current values of related inputs so the spinner
stops at the basin hierarchy:
0 < outflowLevel < dryRunLevel < startLevel ≤ inflowLevel
≤ shiftLevel ≤ maxLevel ≤ overflowLevel ≤ basinHeight
- dryRunPercent capped so dryRunLevel ≤ startLevel given current outflow.
- shiftArmPercent ∈ [1, 100]; highVolumeSafety% ∈ [1, 100].
Validation:
- New visible ribbon above the basin diagram (#ps-basin-validation)
listing every hierarchy violation. The in-SVG warning text is now a
small reminder ("⚠ N ordering issues").
- basin-diagram.js owns hierarchy issues; mode-preview.js trimmed to
only own shift-specific issues (shift > start, shift ≤ max,
shiftArmPercent range, shiftLevel required-when-enabled).
- oneditsave blocks Deploy on the union of _psBasinValidationIssues
and _psModeValidationIssues with a RED.notify listing all problems.
Layout polish:
- Side panel widened to 220 px with minmax(0, 1fr) first column so long
labels can no longer push the rows past the panel edge.
- Basin SVG max-width 380 → 360, gap between side panel and SVG bumped
14 → 28 px. Tank shifted right (x=145 width=110) so the inlet
"bottom of pipe" sub-label is no longer clipped on the left edge.
- "0 m (datum)" moved below the tank (y=395, centred) so it can't
collide with "Outlet / top of pipe" when outflowLevel is near floor.
- Zone labels shortened (Spare / Sewage + buffer / Buffer / Dead vol)
and only show when the bracketing thresholds are ≥ 28 px apart, so
they never sit on a threshold label.
- Mode preview axis labels under the chart removed — line colour +
side-panel labels + hover-couple already identify each line. Stub
<text> elements left hidden to keep the redraw loop simple. Arm-%
line + label trimmed in 10 px on the right so they're not clipped.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 14:10:22 +02:00
|
|
|
window._psBasinValidationIssues = issues;
|
Level-armed shift, derived dryRunLevel, side-panel editor + manual q_out
Runtime (specificClass.js):
- Replace direction-based hysteresis with level-armed _shiftArmed state.
Arms when level rises past shiftLevel; disarms when level drops below
startLevel. While armed, ramp foot moves to startLevel and ramp top
to shiftLevel — both ends shift left, then saturate at 100 % up to
maxLevel.
- _scaleLevelToFlowPercent now takes (rampStartLevel, rampTopLevel) so
the saturation point follows the shift state.
- New setManualOutflow mirroring setManualInflow.
Adapter (nodeClass.js):
- Pipe enableShiftedRamp / shiftLevel through to control.levelbased.
- New q_out topic handler.
Editor (pumpingStation.html + new src/editor/ modules):
- Split monolithic <script> into modules: index.js (helpers),
basin-diagram.js, mode-preview.js, hover-couple.js, oneditprepare.js,
oneditsave.js — served via /pumpingStation/editor/:file.
- Mode preview redrawn per the SVG diagrams: OFF tier below 0 %, 0 %
flat from start→inlet, ramp inlet→max, optional shifted-down curve
start→shift with 100 % saturation past shift.
- Mode preview gains zone bands (dryRun / safetyLow / safe / safetyHigh /
overflow), level markers (dryRun derived, start, inlet, max, shift,
overflow), validation ribbon that blocks save on bad ordering.
- Auto-default shiftLevel to 0.9 × maxLevel on enable so the marker is
always visible.
- All level inputs moved to a side panel left of each diagram, color-
coded to match line strokes; hover-couple highlights the paired SVG
line on input focus / mouseover.
- Removed UI for non-static parameters: minHeightBasedOn,
pipelineLength, maxDischargeHead, staticHead, defaultFluid,
maxInflowRate, temperatureReferenceDegC,
timeleftToFullOrEmptyThresholdSeconds, inletPipeDiameter,
outletPipeDiameter, minLevel (now derived = dryRunLevel).
- foreignObject inputs in basin SVG removed (single source of truth in
side panel).
Dashboard example (examples/basic-dashboard.flow.json):
- Add manual Q_OUT slider + q_out builder mirroring the existing q_in
trio so the basin can be exercised end-to-end without a connected
rotating-machine downstream.
Tests (test/basic/specificClass.test.js):
- Replace direction-shift test with two new cases covering shift-disabled
hold-zone behaviour and shift-armed/disarmed transitions through
shiftLevel and startLevel boundaries. 53/53 tests pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 19:29:34 +02:00
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
})();
|