Files
pumpingStation/src/editor/basin-diagram.js

197 lines
9.2 KiB
JavaScript
Raw Normal View History

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');
fix(levelBased): drop hold zone, route through MGC.setDemand, add holdLevel + integrator variant pick; slim npm pack levelBased ramp + engagement: - Ramp foot is now max(startLevel, holdLevel) — was max(startLevel, inflowLevel). inflowLevel is basin geometry, not a control setpoint; the implicit hold zone it created was causing pumps to "start at inflowLevel" instead of startLevel. - New optional `holdLevel` config (defaults to startLevel = no hold band). When raised, pumps engage at startLevel and hold at 0 % = MGC flow.min across [startLevel, holdLevel], then ramp 0..100 % to maxLevel. - Engagement decided in run() (not in `_applyMachineGroupLevelControl`): rising-edge hysteresis arming gates a clean turnOff early-return. Once armed, the helper always forwards setDemand(pct, '%') — 0 % legitimately means "engaged at min flow", no more soft-turnOff at the boundary. - Disengagement paths (minLevel hard-stop, stopLevel falling-edge, pre-arming idle) now all clear the shifted-ramp hysteresis state too. - Threshold validator drops the startLevel ≤ inflowLevel rule; adds startLevel ≤ holdLevel < maxLevel (only checked when holdLevel is explicitly set, so default-null doesn't false-flag). MGC unit math: - Replace direct group.handleInput(percent) with group.setDemand(pct, '%') in _applyMachineGroupLevelControl. The percent → m³/s resolution now lives in MGC.setDemand (committed separately in the MGC submodule). FlowAggregator variant picking: - New _pickFlowSum() helper mirrors selectBestNetFlow's variant precedence (measured first, then predicted) and resolves each side independently. Realistic mixed case — real measured upstream sensor + predicted pump outflow — now feeds the predicted-volume integrator. Was reading only `flow.predicted.*` so a real upstream sensor (which writes `flow.measured.*`) never moved the level. Editor: - New `holdLevel` and `deadZoneKeepAlivePercent` defaults + side-panel input rows in the levelbased mode preview. - Add the missing `ps-mode-line-holdLevel` SVG marker (was declared in the side-panel coupling but the SVG element didn't exist, so the dashed line never rendered). - Relax stopLevel marker gate so it renders for any non-negative typed value — start/stop ordering is the ribbon's job, not the marker's (was hiding the line whenever startLevel was momentarily smaller). - Add holdLevel to the marker loop in mode-preview so changes track. - Add stopLevel + holdLevel + maxLevel to all three bindRedraw lists (basin-diagram, mode-preview, bounds.apply) so the SVG, validation ribbon, and HTML5 min/max attrs update on every edit. - Initialise stopLevel + holdLevel + deadZoneKeepAlivePercent inputs in oneditprepare so reopening the editor shows the saved values. - nodeClass passes holdLevel + deadZoneKeepAlivePercent into the domain config. Tests: - New test/basic/_probe_upstream_emit.test.js: confirms the parent surfaces flow.measured.upstream.* on Port 0 after a measurement child write — pins the previously-invisible measured variant flow. - flowAggregator.basic.test.js: two new regression cases — measured inflow when predicted side is empty, and the measured-in / predicted-out mixed case. - control-levelBased.basic.test.js: new cases for the holdLevel hold band, the [stopLevel, startLevel] keep-alive, the engagement gate, and the "0 % at startLevel = setDemand" contract. - specificClass.test.js: zone tests adjusted to the new ramp foot. Shifted-ramp tests pin holdLevel = 3 explicitly so their legacy arithmetic (ramp foot at inflowLevel) stays self-consistent. - shifted-ramp-end-to-end.test.js: same holdLevel pin for the same reason. Packaging: - Add .gitignore + .npmignore so the published tarball drops the wiki/, simulations/, test/, tools/, .claude/ etc. The pack went from 1.5 MB (72 files) to ~57 KB (30 files). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 21:36:29 +02:00
const hold = fNum('holdLevel');
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 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`);
fix(levelBased): drop hold zone, route through MGC.setDemand, add holdLevel + integrator variant pick; slim npm pack levelBased ramp + engagement: - Ramp foot is now max(startLevel, holdLevel) — was max(startLevel, inflowLevel). inflowLevel is basin geometry, not a control setpoint; the implicit hold zone it created was causing pumps to "start at inflowLevel" instead of startLevel. - New optional `holdLevel` config (defaults to startLevel = no hold band). When raised, pumps engage at startLevel and hold at 0 % = MGC flow.min across [startLevel, holdLevel], then ramp 0..100 % to maxLevel. - Engagement decided in run() (not in `_applyMachineGroupLevelControl`): rising-edge hysteresis arming gates a clean turnOff early-return. Once armed, the helper always forwards setDemand(pct, '%') — 0 % legitimately means "engaged at min flow", no more soft-turnOff at the boundary. - Disengagement paths (minLevel hard-stop, stopLevel falling-edge, pre-arming idle) now all clear the shifted-ramp hysteresis state too. - Threshold validator drops the startLevel ≤ inflowLevel rule; adds startLevel ≤ holdLevel < maxLevel (only checked when holdLevel is explicitly set, so default-null doesn't false-flag). MGC unit math: - Replace direct group.handleInput(percent) with group.setDemand(pct, '%') in _applyMachineGroupLevelControl. The percent → m³/s resolution now lives in MGC.setDemand (committed separately in the MGC submodule). FlowAggregator variant picking: - New _pickFlowSum() helper mirrors selectBestNetFlow's variant precedence (measured first, then predicted) and resolves each side independently. Realistic mixed case — real measured upstream sensor + predicted pump outflow — now feeds the predicted-volume integrator. Was reading only `flow.predicted.*` so a real upstream sensor (which writes `flow.measured.*`) never moved the level. Editor: - New `holdLevel` and `deadZoneKeepAlivePercent` defaults + side-panel input rows in the levelbased mode preview. - Add the missing `ps-mode-line-holdLevel` SVG marker (was declared in the side-panel coupling but the SVG element didn't exist, so the dashed line never rendered). - Relax stopLevel marker gate so it renders for any non-negative typed value — start/stop ordering is the ribbon's job, not the marker's (was hiding the line whenever startLevel was momentarily smaller). - Add holdLevel to the marker loop in mode-preview so changes track. - Add stopLevel + holdLevel + maxLevel to all three bindRedraw lists (basin-diagram, mode-preview, bounds.apply) so the SVG, validation ribbon, and HTML5 min/max attrs update on every edit. - Initialise stopLevel + holdLevel + deadZoneKeepAlivePercent inputs in oneditprepare so reopening the editor shows the saved values. - nodeClass passes holdLevel + deadZoneKeepAlivePercent into the domain config. Tests: - New test/basic/_probe_upstream_emit.test.js: confirms the parent surfaces flow.measured.upstream.* on Port 0 after a measurement child write — pins the previously-invisible measured variant flow. - flowAggregator.basic.test.js: two new regression cases — measured inflow when predicted side is empty, and the measured-in / predicted-out mixed case. - control-levelBased.basic.test.js: new cases for the holdLevel hold band, the [stopLevel, startLevel] keep-alive, the engagement gate, and the "0 % at startLevel = setDemand" contract. - specificClass.test.js: zone tests adjusted to the new ramp foot. Shifted-ramp tests pin holdLevel = 3 explicitly so their legacy arithmetic (ramp foot at inflowLevel) stays self-consistent. - shifted-ramp-end-to-end.test.js: same holdLevel pin for the same reason. Packaging: - Add .gitignore + .npmignore so the published tarball drops the wiki/, simulations/, test/, tools/, .claude/ etc. The pack went from 1.5 MB (72 files) to ~57 KB (30 files). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 21:36:29 +02:00
if (!ok(start, max, '<'))
issues.push('startLevel must be < maxLevel');
if (!ok(start, hold, '<='))
issues.push('holdLevel must be ≥ startLevel (use startLevel for no hold band)');
if (!ok(hold, max, '<'))
issues.push('holdLevel must be < maxLevel');
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 (!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
},
};
})();