Fixes A/B/C/D incident rebond EDF nuit 07-08/08 + GRAFCET doc

- A: état collant night_recovery_done_idle + bande cible−2 (anti-rebond takeover)
- B: snapshot relit night_session_start_soc/at (fin des réécritures 30s)
- C: hystérésis grid — turn_off_vehicle_unplugged (coupure immédiate, bypass min_on)
- D: evcs_status_raw==3 (charged) = fin de charge immédiate
- golden 19→23 scénarios (2 attendus mis à jour, 4 nouveaux)
- MANUEL_COMPLET §11.6 GRAFCET cycle nuit, HANDOFF §17, vision-notes incident
This commit is contained in:
Tallagrand Thierry
2026-08-08 14:20:21 +02:00
parent b20b3b19fc
commit 9bc4af0ea1
11 changed files with 874 additions and 15 deletions

View File

@@ -317,11 +317,42 @@ const nightTakeoverPrev =
// → DECISION émet night_session_done_idle → le flag s'auto-entretient jusqu'au
// débranchement (state vehicle_not_connected) ou à la fin de la fenêtre nuit.
const prevSurplusState = (input.prev_surplus_state ?? '').toString().trim().toLowerCase();
// Fix D (08/08/2026) : le statut natif EVCS "charged" (3) vaut aussi fin de
// charge, sans attendre le VehicleDemandFilter (désarmé si la session a été
// stoppée par une garde, ex battery_current_hard_stop → détection tardive
// observée la nuit du 07-08/08).
const evcsStatusCharged = Number.parseFloat(parsed.evcsStatusRaw) === 3;
const vehicleChargeCompleted =
nightSessionActive &&
(
prevSurplusState === 'vehicle_full_or_paused' ||
prevSurplusState === 'night_session_done_idle'
prevSurplusState === 'night_session_done_idle' ||
prevSurplusState === 'night_recovery_done_idle' ||
evcsStatusCharged
);
// Fix A (08/08/2026) : recovery batterie post-charge = UNE SEULE passe.
// Sans flag collant, SOC==cible est un point dégénéré (engage soc<=cible,
// release soc>cible, l'ACM recharge pile à la cible) → rebond EDF ON/OFF
// période ~10 min observé la nuit du 07-08/08. Une fois la cible atteinte,
// l'état 'night_recovery_done_idle' (auto-entretenu via prev_surplus_state)
// coupe le takeover. Ré-engagement uniquement sous cible − 2 pts (bande),
// hard floor et fenêtre/débranchement restent prioritaires.
// ⚠ 'night_recovery_done_idle' ne doit matcher AUCUN pattern no-demand de
// l'hystérésis (full_or_paused / not_requesting_power / charge_complete /
// no_demand) — même contrainte que 'night_session_done_idle'.
const NIGHT_RECOVERY_REARM_BAND_PCT = 2;
const nightRecoveryDonePrev = prevSurplusState === 'night_recovery_done_idle';
const nightRecoveryDone =
vehicleChargeCompleted &&
(
parsed.soc >= batteryMorningTargetPct ||
(
nightRecoveryDonePrev &&
parsed.soc > batteryMorningTargetPct - NIGHT_RECOVERY_REARM_BAND_PCT
)
);
// P8 : correction du fix 04/08 (logique inversée : "|| vehicleChargeCompleted"
@@ -350,9 +381,17 @@ const nightGridTakeoverNeeded =
(
nightGridHcPreferred ||
batteryBelowHardFloor ||
batteryAtOrBelowGridTakeoverTrigger ||
batteryRecoveryUrgent ||
nightTakeoverLatched
(
// Fix A : après fin de charge, le seul motif réseau restant est la
// recovery batterie, et elle ne se ré-engage pas une fois done.
vehicleChargeCompleted
? !nightRecoveryDone
: (
batteryAtOrBelowGridTakeoverTrigger ||
batteryRecoveryUrgent ||
nightTakeoverLatched
)
)
);
const nightPreferredSourceMode =
@@ -679,6 +718,7 @@ return [{
nightTakeoverLatched,
nightGridHcPreferred,
vehicleChargeCompleted,
nightRecoveryDone,
nightPreferredSourceMode,
}
}