Files
pumpingStation/src/editor/mode-preview.js

287 lines
13 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 — level-based mode preview SVG.
// Draws zone bands, level markers, the up curve (inflowLevel→maxLevel) and
// the optional shifted-down curve (startLevel→shiftLevel). Computes
// validation issues and stashes them on window._psModeValidationIssues
// for oneditsave to read.
(function () {
const ns = window.PSEditor = window.PSEditor || {};
const fNum = (id) => ns.fNum(id);
// Derive dryRunLevel the same way the basin diagram does.
// dryRunLevel = outflowLevel × (1 + dryRunThresholdPercent/100).
// Returns null if either input is missing.
ns.deriveDryRunLevel = () => {
const refLow = fNum('outflowLevel');
const dryPct = fNum('dryRunThresholdPercent');
if (refLow == null || dryPct == null) return null;
return refLow * (1 + dryPct / 100);
};
ns.modePreview = {
redraw() {
const svg = document.getElementById('ps-levelbased-mode-diagram');
if (!svg) return;
const start = fNum('startLevel');
const inlet = fNum('inflowLevel');
const max = fNum('maxLevel');
// Optional stopLevel — explicit pump-off threshold. Drawn as its
// own marker line; does NOT shift the ramp foot. Must be < startLevel
// for the marker to render.
const stopRaw = fNum('stopLevel');
const stop = Number.isFinite(stopRaw) && stopRaw >= 0 && Number.isFinite(start) && stopRaw < start ? stopRaw : null;
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
// dryRunLevel is derived from the basin's outflowLevel + dryRun%
// (no separate input). Below dryRunLevel the runtime hard-stops;
// we draw it as the leftmost vertical marker so the user sees
// exactly where it lands.
const dryRun = ns.deriveDryRunLevel();
const overflow = fNum('overflowLevel');
const shiftEnabled = !!document.getElementById('node-input-enableShiftedRamp')?.checked;
const shiftRaw = fNum('shiftLevel');
const shift = Number.isFinite(shiftRaw) && shiftRaw > 0 ? Math.min(shiftRaw, max ?? shiftRaw) : null;
Hold-then-ramp shift semantics + shiftArmPercent + e2e tests Runtime (specificClass.js): - Replace the "shift left both ramp ends" geometry with a true hold-then-ramp hysteresis driven by output %, not level: • Up-curve % crosses shiftArmPercent on the way up → ARM. • Filling→draining transition while armed → capture the up-curve % at that moment as _shiftHoldValue. • Draining + level ≥ shiftLevel → output stays at _shiftHoldValue (horizontal hold, matching the dashed segment in the SVG). • Draining + level in [start, shift] → output ramps holdValue → 0 % along the same curve shape (linear or log) as the up curve. • Draining + level < startLevel → 0 % AND disarm. • Returning to filling clears holdValue, stays armed; next drain transition captures a fresh hold so bouncing fills rearm cleanly. • Disarm only when level ≤ startLevel. - New _curveShape(x) helper for shared linear/log shaping. - Removed legacy _levelBasedRampStart / _levelBasedRampTop / _updateShiftArmed in favour of the inline state machine. Adapter (nodeClass.js): - Pipe shiftArmPercent through to control.levelbased. Editor (pumpingStation.html + src/editor/): - Add shiftArmPercent input row (% with unit) to the mode side panel (only shown when shifted ramp is enabled). Default 95 %. - Add the horizontal arming-% line + label inside the mode SVG — this is the "% Threshold triggering shifted ramp down" line from the original drawing that had been missing. - Redraw the shifted-down curve to match the SVG geometry literally: 100 % flat from maxLevel → shiftLevel, then ramp shiftLevel → startLevel down to 0 %, OFF below startLevel. Preview shows the worst-case envelope (hold = 100 %); runtime hold is captured live. - Validation extended: 0 < shiftArmPercent ≤ 100; ordering rules preserved (start < shift ≤ max etc.). - Auto-default shiftArmPercent to 95 when shift is enabled and the current value is missing or out of range. Dashboard example (examples/basic-dashboard.flow.json): - Parser now reads `level.predicted.atequipment.default` etc. The MeasurementContainer flatten format includes the implicit 'default' childId; consumers must include it. Comment in the parser points at the documenting source in generalFunctions. Tests: - test/basic: replace old level-armed-shift tests with two new ones that exercise the hold-then-ramp arming, capture, hold, ramp-down, disarm, and the bounce case (filling→draining→filling→draining captures a fresh hold each time). - test/integration/shifted-ramp-end-to-end.test.js: new file. Drives Q_IN/Q_OUT through the full runtime tick with a controllable clock, asserting the same hysteresis path the dashboard exercises. - test/integration/basic-dashboard-flow.test.js: fixture keys updated to the .default-suffixed form so they match the real flatten output. 56/56 tests pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 11:46:46 +02:00
const armRaw = fNum('shiftArmPercent');
const armPct = Number.isFinite(armRaw) ? Math.max(0, Math.min(100, armRaw)) : 95;
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 curveType = document.getElementById('node-input-levelCurveType')?.value || 'linear';
const factorRaw = parseFloat(document.getElementById('node-input-logCurveFactor')?.value);
const factor = Number.isFinite(factorRaw) && factorRaw > 0 ? factorRaw : 9;
// Plot window is FIXED relative to basin geometry so that moving any
// single level slides only that line, not all the others. Lower bound
// is the basin floor (0); upper bound is overflowLevel (or maxLevel
// if overflow isn't set) plus a small margin.
const upperRefs = [max, overflow].filter(Number.isFinite);
const upperBase = upperRefs.length ? Math.max(...upperRefs) : 1;
const pad = Math.max(upperBase * 0.05, 0.1);
const levelMin = 0;
const levelMax = upperBase + pad;
// Plot rectangle (viewBox px).
const x0 = 52, x1 = 390, y0 = 140, y1 = 24;
const yOffPx = 160;
const yOffPct = -((yOffPx - y0) / (y0 - y1)) * 100;
const xFor = (level) => x0 + ((level - levelMin) / (levelMax - levelMin)) * (x1 - x0);
const yForPct = (pct) => y0 - (pct / 100) * (y0 - y1);
const scale = (x) => {
const clamped = Math.max(0, Math.min(1, x));
if (curveType === 'log') return Math.log1p(factor * clamped) / Math.log1p(factor);
return clamped;
};
// Path with three flat regions and a ramp:
// [levelMin..startX] OFF (pump off; below startLevel)
// [startX..footX] 0 % (system armed but not yet ramping)
// [footX..topX] ramp (linear or log scaled 0..100 %)
// [topX..levelMax] 100 % (saturated)
// Up curve: startX=startLevel, footX=inflowLevel, topX=maxLevel.
// Shifted-down: startX=footX=startLevel, topX=shiftLevel.
const buildPath = (startX, footX, topX) => {
if (![startX, footX, topX].every(Number.isFinite) || topX <= footX) return '';
const pts = [];
pts.push(`${xFor(levelMin)},${yForPct(yOffPct)}`);
pts.push(`${xFor(startX)},${yForPct(yOffPct)}`);
pts.push(`${xFor(startX)},${yForPct(0)}`);
if (footX > startX) pts.push(`${xFor(footX)},${yForPct(0)}`);
for (let i = 0; i <= 24; i++) {
const t = i / 24;
const level = footX + t * (topX - footX);
pts.push(`${xFor(level)},${yForPct(scale(t) * 100)}`);
}
pts.push(`${xFor(levelMax)},${yForPct(100)}`);
return pts.join(' ');
};
// Up curve. Foot is startLevel (the configured pump-on threshold and
// ramp foot per the runtime in _controlLevelBased). The OFF baseline
// is drawn for level < startLevel; at startLevel demand jumps from
// OFF to 0 % and ramps up to 100 % at maxLevel.
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 up = document.getElementById('ps-mode-curve-up');
const down = document.getElementById('ps-mode-curve-down');
const downLabel = document.getElementById('ps-mode-curve-down-label');
if (up) up.setAttribute('points', buildPath(start, start, max));
Hold-then-ramp shift semantics + shiftArmPercent + e2e tests Runtime (specificClass.js): - Replace the "shift left both ramp ends" geometry with a true hold-then-ramp hysteresis driven by output %, not level: • Up-curve % crosses shiftArmPercent on the way up → ARM. • Filling→draining transition while armed → capture the up-curve % at that moment as _shiftHoldValue. • Draining + level ≥ shiftLevel → output stays at _shiftHoldValue (horizontal hold, matching the dashed segment in the SVG). • Draining + level in [start, shift] → output ramps holdValue → 0 % along the same curve shape (linear or log) as the up curve. • Draining + level < startLevel → 0 % AND disarm. • Returning to filling clears holdValue, stays armed; next drain transition captures a fresh hold so bouncing fills rearm cleanly. • Disarm only when level ≤ startLevel. - New _curveShape(x) helper for shared linear/log shaping. - Removed legacy _levelBasedRampStart / _levelBasedRampTop / _updateShiftArmed in favour of the inline state machine. Adapter (nodeClass.js): - Pipe shiftArmPercent through to control.levelbased. Editor (pumpingStation.html + src/editor/): - Add shiftArmPercent input row (% with unit) to the mode side panel (only shown when shifted ramp is enabled). Default 95 %. - Add the horizontal arming-% line + label inside the mode SVG — this is the "% Threshold triggering shifted ramp down" line from the original drawing that had been missing. - Redraw the shifted-down curve to match the SVG geometry literally: 100 % flat from maxLevel → shiftLevel, then ramp shiftLevel → startLevel down to 0 %, OFF below startLevel. Preview shows the worst-case envelope (hold = 100 %); runtime hold is captured live. - Validation extended: 0 < shiftArmPercent ≤ 100; ordering rules preserved (start < shift ≤ max etc.). - Auto-default shiftArmPercent to 95 when shift is enabled and the current value is missing or out of range. Dashboard example (examples/basic-dashboard.flow.json): - Parser now reads `level.predicted.atequipment.default` etc. The MeasurementContainer flatten format includes the implicit 'default' childId; consumers must include it. Comment in the parser points at the documenting source in generalFunctions. Tests: - test/basic: replace old level-armed-shift tests with two new ones that exercise the hold-then-ramp arming, capture, hold, ramp-down, disarm, and the bounce case (filling→draining→filling→draining captures a fresh hold each time). - test/integration/shifted-ramp-end-to-end.test.js: new file. Drives Q_IN/Q_OUT through the full runtime tick with a controllable clock, asserting the same hysteresis path the dashboard exercises. - test/integration/basic-dashboard-flow.test.js: fixture keys updated to the .default-suffixed form so they match the real flatten output. 56/56 tests pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 11:46:46 +02:00
// Shifted-DOWN curve (only when shift enabled): represents the
// worst-case held-then-ramp path drawn for hold=100 % (the SVG
// ideal). Geometry: 100 % flat from levelMax back to shiftLevel,
// then linear/log ramp from (shiftLevel, 100 %) down to
// (startLevel, 0 %), then OFF below startLevel.
// Real runtime hold value depends on where direction flips, so the
// preview shows the maximum extent.
const buildShiftedDown = () => {
if (![start, shift].every(Number.isFinite) || shift <= start) return '';
const pts = [];
// OFF baseline far-left to startLevel
pts.push(`${xFor(levelMin)},${yForPct(yOffPct)}`);
pts.push(`${xFor(start)},${yForPct(yOffPct)}`);
// Jump 0 % at startLevel
pts.push(`${xFor(start)},${yForPct(0)}`);
// Ramp start→shift = 0..100 % (peak hold = 100 % for this preview)
for (let i = 0; i <= 24; i++) {
const t = i / 24;
const lvl = start + t * (shift - start);
pts.push(`${xFor(lvl)},${yForPct(scale(t) * 100)}`);
}
// Held at 100 % from shift → far-right
pts.push(`${xFor(levelMax)},${yForPct(100)}`);
return pts.join(' ');
};
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 (down) {
if (shiftEnabled) {
Hold-then-ramp shift semantics + shiftArmPercent + e2e tests Runtime (specificClass.js): - Replace the "shift left both ramp ends" geometry with a true hold-then-ramp hysteresis driven by output %, not level: • Up-curve % crosses shiftArmPercent on the way up → ARM. • Filling→draining transition while armed → capture the up-curve % at that moment as _shiftHoldValue. • Draining + level ≥ shiftLevel → output stays at _shiftHoldValue (horizontal hold, matching the dashed segment in the SVG). • Draining + level in [start, shift] → output ramps holdValue → 0 % along the same curve shape (linear or log) as the up curve. • Draining + level < startLevel → 0 % AND disarm. • Returning to filling clears holdValue, stays armed; next drain transition captures a fresh hold so bouncing fills rearm cleanly. • Disarm only when level ≤ startLevel. - New _curveShape(x) helper for shared linear/log shaping. - Removed legacy _levelBasedRampStart / _levelBasedRampTop / _updateShiftArmed in favour of the inline state machine. Adapter (nodeClass.js): - Pipe shiftArmPercent through to control.levelbased. Editor (pumpingStation.html + src/editor/): - Add shiftArmPercent input row (% with unit) to the mode side panel (only shown when shifted ramp is enabled). Default 95 %. - Add the horizontal arming-% line + label inside the mode SVG — this is the "% Threshold triggering shifted ramp down" line from the original drawing that had been missing. - Redraw the shifted-down curve to match the SVG geometry literally: 100 % flat from maxLevel → shiftLevel, then ramp shiftLevel → startLevel down to 0 %, OFF below startLevel. Preview shows the worst-case envelope (hold = 100 %); runtime hold is captured live. - Validation extended: 0 < shiftArmPercent ≤ 100; ordering rules preserved (start < shift ≤ max etc.). - Auto-default shiftArmPercent to 95 when shift is enabled and the current value is missing or out of range. Dashboard example (examples/basic-dashboard.flow.json): - Parser now reads `level.predicted.atequipment.default` etc. The MeasurementContainer flatten format includes the implicit 'default' childId; consumers must include it. Comment in the parser points at the documenting source in generalFunctions. Tests: - test/basic: replace old level-armed-shift tests with two new ones that exercise the hold-then-ramp arming, capture, hold, ramp-down, disarm, and the bounce case (filling→draining→filling→draining captures a fresh hold each time). - test/integration/shifted-ramp-end-to-end.test.js: new file. Drives Q_IN/Q_OUT through the full runtime tick with a controllable clock, asserting the same hysteresis path the dashboard exercises. - test/integration/basic-dashboard-flow.test.js: fixture keys updated to the .default-suffixed form so they match the real flatten output. 56/56 tests pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 11:46:46 +02:00
down.setAttribute('points', buildShiftedDown());
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
down.style.display = '';
if (downLabel) downLabel.style.display = '';
} else {
down.setAttribute('points', '');
down.style.display = 'none';
if (downLabel) downLabel.style.display = 'none';
}
}
Hold-then-ramp shift semantics + shiftArmPercent + e2e tests Runtime (specificClass.js): - Replace the "shift left both ramp ends" geometry with a true hold-then-ramp hysteresis driven by output %, not level: • Up-curve % crosses shiftArmPercent on the way up → ARM. • Filling→draining transition while armed → capture the up-curve % at that moment as _shiftHoldValue. • Draining + level ≥ shiftLevel → output stays at _shiftHoldValue (horizontal hold, matching the dashed segment in the SVG). • Draining + level in [start, shift] → output ramps holdValue → 0 % along the same curve shape (linear or log) as the up curve. • Draining + level < startLevel → 0 % AND disarm. • Returning to filling clears holdValue, stays armed; next drain transition captures a fresh hold so bouncing fills rearm cleanly. • Disarm only when level ≤ startLevel. - New _curveShape(x) helper for shared linear/log shaping. - Removed legacy _levelBasedRampStart / _levelBasedRampTop / _updateShiftArmed in favour of the inline state machine. Adapter (nodeClass.js): - Pipe shiftArmPercent through to control.levelbased. Editor (pumpingStation.html + src/editor/): - Add shiftArmPercent input row (% with unit) to the mode side panel (only shown when shifted ramp is enabled). Default 95 %. - Add the horizontal arming-% line + label inside the mode SVG — this is the "% Threshold triggering shifted ramp down" line from the original drawing that had been missing. - Redraw the shifted-down curve to match the SVG geometry literally: 100 % flat from maxLevel → shiftLevel, then ramp shiftLevel → startLevel down to 0 %, OFF below startLevel. Preview shows the worst-case envelope (hold = 100 %); runtime hold is captured live. - Validation extended: 0 < shiftArmPercent ≤ 100; ordering rules preserved (start < shift ≤ max etc.). - Auto-default shiftArmPercent to 95 when shift is enabled and the current value is missing or out of range. Dashboard example (examples/basic-dashboard.flow.json): - Parser now reads `level.predicted.atequipment.default` etc. The MeasurementContainer flatten format includes the implicit 'default' childId; consumers must include it. Comment in the parser points at the documenting source in generalFunctions. Tests: - test/basic: replace old level-armed-shift tests with two new ones that exercise the hold-then-ramp arming, capture, hold, ramp-down, disarm, and the bounce case (filling→draining→filling→draining captures a fresh hold each time). - test/integration/shifted-ramp-end-to-end.test.js: new file. Drives Q_IN/Q_OUT through the full runtime tick with a controllable clock, asserting the same hysteresis path the dashboard exercises. - test/integration/basic-dashboard-flow.test.js: fixture keys updated to the .default-suffixed form so they match the real flatten output. 56/56 tests pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 11:46:46 +02:00
// Horizontal arming-% line — only meaningful when shift enabled.
const armLine = document.getElementById('ps-mode-line-armPercent');
const armLabel = document.getElementById('ps-mode-label-armPercent');
if (armLine && armLabel) {
if (shiftEnabled) {
const yArm = yForPct(armPct);
armLine.setAttribute('y1', yArm);
armLine.setAttribute('y2', yArm);
armLabel.setAttribute('y', yArm - 2);
armLabel.textContent = `arm ${Math.round(armPct)}%`;
armLine.style.display = '';
armLabel.style.display = '';
} else {
armLine.style.display = 'none';
armLabel.style.display = 'none';
}
}
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
// Vertical level markers — line only. Axis labels were removed;
// identification comes from line colour + side-panel labels +
// hover coupling.
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
[
['dryRunLevel', dryRun],
['startLevel', start],
['stopLevel', stop],
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
['inflowLevel', inlet],
['maxLevel', max],
['overflowLevel', overflow],
].forEach(([id, level]) => {
const line = document.getElementById(`ps-mode-line-${id}`);
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 (!line) return;
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 (!Number.isFinite(level)) {
line.style.display = 'none';
return;
}
const x = xFor(level);
line.style.display = '';
line.setAttribute('x1', x); line.setAttribute('x2', x);
});
// Background zone bands.
const plotL = xFor(levelMin);
const plotR = xFor(levelMax);
const setBand = (id, a, b) => {
const r = document.getElementById(id);
if (!r) return;
if (!Number.isFinite(a) || !Number.isFinite(b) || b <= a) {
r.setAttribute('x', 0); r.setAttribute('width', 0);
return;
}
r.setAttribute('x', a);
r.setAttribute('width', b - a);
};
const xMin = Number.isFinite(dryRun) ? xFor(dryRun) : plotL;
const xStart = Number.isFinite(start) ? xFor(start) : xMin;
const xMax = Number.isFinite(max) ? xFor(max) : plotR;
const xOvf = Number.isFinite(overflow) ? xFor(overflow) : xMax;
setBand('ps-zone-dryRun', plotL, xMin);
setBand('ps-zone-safetyLow', xMin, xStart);
setBand('ps-zone-safe', xStart, xMax);
setBand('ps-zone-safetyHigh', xMax, xOvf);
setBand('ps-zone-overflow', xOvf, plotR);
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
// Shift level marker (line only).
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 shiftLine = document.getElementById('ps-mode-line-shiftLevel');
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 (shiftLine) {
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 (shiftEnabled && Number.isFinite(shift)) {
const x = xFor(shift);
shiftLine.setAttribute('x1', x); shiftLine.setAttribute('x2', x);
shiftLine.style.display = '';
} else {
shiftLine.style.display = 'none';
}
}
// Title + row visibility.
const curveLabel = document.getElementById('ps-mode-curve-label');
if (curveLabel) curveLabel.textContent = curveType === 'log' ? 'log curve: fast early response' : 'linear curve';
const shiftRow = document.getElementById('ps-shiftLevel-row');
if (shiftRow) shiftRow.style.display = shiftEnabled ? '' : 'none';
Hold-then-ramp shift semantics + shiftArmPercent + e2e tests Runtime (specificClass.js): - Replace the "shift left both ramp ends" geometry with a true hold-then-ramp hysteresis driven by output %, not level: • Up-curve % crosses shiftArmPercent on the way up → ARM. • Filling→draining transition while armed → capture the up-curve % at that moment as _shiftHoldValue. • Draining + level ≥ shiftLevel → output stays at _shiftHoldValue (horizontal hold, matching the dashed segment in the SVG). • Draining + level in [start, shift] → output ramps holdValue → 0 % along the same curve shape (linear or log) as the up curve. • Draining + level < startLevel → 0 % AND disarm. • Returning to filling clears holdValue, stays armed; next drain transition captures a fresh hold so bouncing fills rearm cleanly. • Disarm only when level ≤ startLevel. - New _curveShape(x) helper for shared linear/log shaping. - Removed legacy _levelBasedRampStart / _levelBasedRampTop / _updateShiftArmed in favour of the inline state machine. Adapter (nodeClass.js): - Pipe shiftArmPercent through to control.levelbased. Editor (pumpingStation.html + src/editor/): - Add shiftArmPercent input row (% with unit) to the mode side panel (only shown when shifted ramp is enabled). Default 95 %. - Add the horizontal arming-% line + label inside the mode SVG — this is the "% Threshold triggering shifted ramp down" line from the original drawing that had been missing. - Redraw the shifted-down curve to match the SVG geometry literally: 100 % flat from maxLevel → shiftLevel, then ramp shiftLevel → startLevel down to 0 %, OFF below startLevel. Preview shows the worst-case envelope (hold = 100 %); runtime hold is captured live. - Validation extended: 0 < shiftArmPercent ≤ 100; ordering rules preserved (start < shift ≤ max etc.). - Auto-default shiftArmPercent to 95 when shift is enabled and the current value is missing or out of range. Dashboard example (examples/basic-dashboard.flow.json): - Parser now reads `level.predicted.atequipment.default` etc. The MeasurementContainer flatten format includes the implicit 'default' childId; consumers must include it. Comment in the parser points at the documenting source in generalFunctions. Tests: - test/basic: replace old level-armed-shift tests with two new ones that exercise the hold-then-ramp arming, capture, hold, ramp-down, disarm, and the bounce case (filling→draining→filling→draining captures a fresh hold each time). - test/integration/shifted-ramp-end-to-end.test.js: new file. Drives Q_IN/Q_OUT through the full runtime tick with a controllable clock, asserting the same hysteresis path the dashboard exercises. - test/integration/basic-dashboard-flow.test.js: fixture keys updated to the .default-suffixed form so they match the real flatten output. 56/56 tests pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 11:46:46 +02:00
const armRow = document.getElementById('ps-shiftArmPercent-row');
if (armRow) armRow.style.display = shiftEnabled ? '' : '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
const logRow = document.getElementById('ps-log-factor-row');
if (logRow) logRow.style.display = curveType === 'log' ? '' : 'none';
// Auto-default shiftLevel when shift is enabled and current value
// is missing/out-of-range. Visible default avoids a hidden ramp.
const shiftInput = document.getElementById('node-input-shiftLevel');
if (shiftEnabled && shiftInput && Number.isFinite(max)) {
const cur = parseFloat(shiftInput.value);
if (!Number.isFinite(cur) || cur <= 0 || cur >= max) {
shiftInput.value = (max * 0.9).toFixed(2);
}
}
Hold-then-ramp shift semantics + shiftArmPercent + e2e tests Runtime (specificClass.js): - Replace the "shift left both ramp ends" geometry with a true hold-then-ramp hysteresis driven by output %, not level: • Up-curve % crosses shiftArmPercent on the way up → ARM. • Filling→draining transition while armed → capture the up-curve % at that moment as _shiftHoldValue. • Draining + level ≥ shiftLevel → output stays at _shiftHoldValue (horizontal hold, matching the dashed segment in the SVG). • Draining + level in [start, shift] → output ramps holdValue → 0 % along the same curve shape (linear or log) as the up curve. • Draining + level < startLevel → 0 % AND disarm. • Returning to filling clears holdValue, stays armed; next drain transition captures a fresh hold so bouncing fills rearm cleanly. • Disarm only when level ≤ startLevel. - New _curveShape(x) helper for shared linear/log shaping. - Removed legacy _levelBasedRampStart / _levelBasedRampTop / _updateShiftArmed in favour of the inline state machine. Adapter (nodeClass.js): - Pipe shiftArmPercent through to control.levelbased. Editor (pumpingStation.html + src/editor/): - Add shiftArmPercent input row (% with unit) to the mode side panel (only shown when shifted ramp is enabled). Default 95 %. - Add the horizontal arming-% line + label inside the mode SVG — this is the "% Threshold triggering shifted ramp down" line from the original drawing that had been missing. - Redraw the shifted-down curve to match the SVG geometry literally: 100 % flat from maxLevel → shiftLevel, then ramp shiftLevel → startLevel down to 0 %, OFF below startLevel. Preview shows the worst-case envelope (hold = 100 %); runtime hold is captured live. - Validation extended: 0 < shiftArmPercent ≤ 100; ordering rules preserved (start < shift ≤ max etc.). - Auto-default shiftArmPercent to 95 when shift is enabled and the current value is missing or out of range. Dashboard example (examples/basic-dashboard.flow.json): - Parser now reads `level.predicted.atequipment.default` etc. The MeasurementContainer flatten format includes the implicit 'default' childId; consumers must include it. Comment in the parser points at the documenting source in generalFunctions. Tests: - test/basic: replace old level-armed-shift tests with two new ones that exercise the hold-then-ramp arming, capture, hold, ramp-down, disarm, and the bounce case (filling→draining→filling→draining captures a fresh hold each time). - test/integration/shifted-ramp-end-to-end.test.js: new file. Drives Q_IN/Q_OUT through the full runtime tick with a controllable clock, asserting the same hysteresis path the dashboard exercises. - test/integration/basic-dashboard-flow.test.js: fixture keys updated to the .default-suffixed form so they match the real flatten output. 56/56 tests pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 11:46:46 +02:00
// Auto-default shiftArmPercent to 95 % when shift is enabled and the
// current value is missing / out of [0, 100].
const armInput = document.getElementById('node-input-shiftArmPercent');
if (shiftEnabled && armInput) {
const cur = parseFloat(armInput.value);
if (!Number.isFinite(cur) || cur < 0 || cur > 100) {
armInput.value = 95;
}
}
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
// Validation: only mode-specific (shift) ordering. Basin-level
// hierarchy (start ≤ inlet ≤ max ≤ overflow ≤ basinHeight,
// dryRun < start) is owned by basin-diagram.js so it shows in the
// basin section near the offending inputs.
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 = [];
if (shiftEnabled) {
const shiftVal = Number(shiftInput?.value);
if (Number.isFinite(shiftVal)) {
if (Number.isFinite(start) && shiftVal <= start)
issues.push('shiftLevel must be > startLevel');
if (Number.isFinite(max) && shiftVal > max)
issues.push('shiftLevel must be ≤ maxLevel');
} else {
issues.push('shiftLevel is required when shifted ramp is enabled');
}
Hold-then-ramp shift semantics + shiftArmPercent + e2e tests Runtime (specificClass.js): - Replace the "shift left both ramp ends" geometry with a true hold-then-ramp hysteresis driven by output %, not level: • Up-curve % crosses shiftArmPercent on the way up → ARM. • Filling→draining transition while armed → capture the up-curve % at that moment as _shiftHoldValue. • Draining + level ≥ shiftLevel → output stays at _shiftHoldValue (horizontal hold, matching the dashed segment in the SVG). • Draining + level in [start, shift] → output ramps holdValue → 0 % along the same curve shape (linear or log) as the up curve. • Draining + level < startLevel → 0 % AND disarm. • Returning to filling clears holdValue, stays armed; next drain transition captures a fresh hold so bouncing fills rearm cleanly. • Disarm only when level ≤ startLevel. - New _curveShape(x) helper for shared linear/log shaping. - Removed legacy _levelBasedRampStart / _levelBasedRampTop / _updateShiftArmed in favour of the inline state machine. Adapter (nodeClass.js): - Pipe shiftArmPercent through to control.levelbased. Editor (pumpingStation.html + src/editor/): - Add shiftArmPercent input row (% with unit) to the mode side panel (only shown when shifted ramp is enabled). Default 95 %. - Add the horizontal arming-% line + label inside the mode SVG — this is the "% Threshold triggering shifted ramp down" line from the original drawing that had been missing. - Redraw the shifted-down curve to match the SVG geometry literally: 100 % flat from maxLevel → shiftLevel, then ramp shiftLevel → startLevel down to 0 %, OFF below startLevel. Preview shows the worst-case envelope (hold = 100 %); runtime hold is captured live. - Validation extended: 0 < shiftArmPercent ≤ 100; ordering rules preserved (start < shift ≤ max etc.). - Auto-default shiftArmPercent to 95 when shift is enabled and the current value is missing or out of range. Dashboard example (examples/basic-dashboard.flow.json): - Parser now reads `level.predicted.atequipment.default` etc. The MeasurementContainer flatten format includes the implicit 'default' childId; consumers must include it. Comment in the parser points at the documenting source in generalFunctions. Tests: - test/basic: replace old level-armed-shift tests with two new ones that exercise the hold-then-ramp arming, capture, hold, ramp-down, disarm, and the bounce case (filling→draining→filling→draining captures a fresh hold each time). - test/integration/shifted-ramp-end-to-end.test.js: new file. Drives Q_IN/Q_OUT through the full runtime tick with a controllable clock, asserting the same hysteresis path the dashboard exercises. - test/integration/basic-dashboard-flow.test.js: fixture keys updated to the .default-suffixed form so they match the real flatten output. 56/56 tests pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 11:46:46 +02:00
const armVal = Number(armInput?.value);
if (!Number.isFinite(armVal) || armVal <= 0 || armVal > 100)
issues.push('shiftArmPercent must be in (0, 100]');
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 warnBox = document.getElementById('ps-mode-validation');
if (warnBox) {
if (issues.length) {
warnBox.innerHTML = '⚠ Fix before deploy:<ul style="margin:4px 0 0 18px;padding:0;">'
+ issues.map((i) => `<li>${i}</li>`).join('') + '</ul>';
warnBox.style.display = '';
} else {
warnBox.style.display = 'none';
}
}
window._psModeValidationIssues = issues;
// Read-only readouts in the side panel — number only; the row's
// .ps-unit span already shows "m".
const fmt = (v) => Number.isFinite(v) ? v.toFixed(2) : '—';
const setText = (id, val) => {
const el = document.getElementById(id);
if (el) el.textContent = fmt(val);
};
setText('ps-mode-readout-dryRun', dryRun);
setText('ps-mode-readout-inflow', inlet);
setText('ps-mode-readout-overflow', overflow);
},
};
})();