fix: serialize per-pump shutdown + cancel deferred dispatch in turnOffAllMachines

PS calls turnOffAllMachines on every tick once level < stopLevel. Two
ways the pump could re-engage after we shut it down:

1. _delayedCall: a 1% dead-zone keep-alive parked in MGC's deferred
   dispatch fires from the in-flight handleInput's finally block AFTER
   the shutdown completes, dispatching flow + startup to a fresh pump.
   Clear _delayedCall at the top of turnOff.

2. Concurrent shutdown calls on the same pump interrupt each other
   before the sequence can transition past stopping. Track shutdown-
   in-flight per pump and skip if one is already underway.

Together with the rotatingMachine delayedMove-clearing fix, this lets
the level-based hysteresis cycle complete: pumps shut off cleanly at
stopLevel, basin reverses direction, refills to startLevel, repeat.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Rene De Ren
2026-05-09 18:17:55 +02:00
parent 2651aaf409
commit ea2857fb25
2 changed files with 152 additions and 1 deletions

View File

@@ -1421,8 +1421,28 @@ class MachineGroup {
}
async turnOffAllMachines(){
// Cancel any deferred dispatch — turnOff is the latest user intent,
// a stale 1% keep-alive must not re-engage a pump after we shut down.
this._delayedCall = null;
// Per-pump shutdown serialization: PS calls turnOffAllMachines on
// every tick (every 2 s) once level < stopLevel. Without this guard,
// each new shutdown call hits the still-running prior shutdown's
// movement transitions and triggers abortCurrentMovement, which
// bounces the pump back to 'operational' before the sequence loop
// can reach stopping/coolingdown/idle. Net effect: pump never
// actually shuts down. Track shutdown-in-flight per pump and skip
// if already underway.
if (!this._shutdownInFlight) this._shutdownInFlight = new Set();
await Promise.all(Object.entries(this.machines).map(async ([machineId, machine]) => {
if (this.isMachineActive(machineId)) { await machine.handleInput("parent", "execsequence", "shutdown"); }
if (this._shutdownInFlight.has(machineId)) return;
if (this.isMachineActive(machineId)) {
this._shutdownInFlight.add(machineId);
try {
await machine.handleInput("parent", "execsequence", "shutdown");
} finally {
this._shutdownInFlight.delete(machineId);
}
}
}));
// Update measurements to zero so the parent (PS) sees the
// outflow drop immediately — without this the PS keeps the