stopLevel Schmitt-trigger hysteresis + dead-zone keep-alive

Levelbased control now distinguishes startLevel (rising-edge engage,
ramp foot) from stopLevel (falling-edge disengage). _stopHystRunning
flag flips TRUE crossing startLevel up, FALSE crossing stopLevel down.
While engaged AND level inside [stopLevel, startLevel] (basin draining
through the dead band), emit a configurable keep-alive percControl
(default 1 %) so MGC keeps a single pump running for a full drain
stroke instead of oscillating at startLevel.

Hard turn-off the moment level <= stopLevel — independent of ramp
scaling. Manual-mode demand=0 now also issues explicit turnOff to
keep parity with the new MGC handleInput semantics where demand<=0
means "off".

Editor preview shades the new hysteresis band; admin endpoint
exposes runtime engaged state.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Rene De Ren
2026-05-08 11:20:36 +02:00
parent 6ab585bcc2
commit e2ebb31816
5 changed files with 110 additions and 7 deletions

View File

@@ -25,6 +25,11 @@
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;
// 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
@@ -85,11 +90,14 @@
return pts.join(' ');
};
// Up curve: same as before.
// 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.
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, inlet, max));
if (up) up.setAttribute('points', buildPath(start, start, max));
// Shifted-DOWN curve (only when shift enabled): represents the
// worst-case held-then-ramp path drawn for hold=100 % (the SVG
@@ -152,6 +160,7 @@
[
['dryRunLevel', dryRun],
['startLevel', start],
['stopLevel', stop],
['inflowLevel', inlet],
['maxLevel', max],
['overflowLevel', overflow],