Corrige l'inversion plancher de demarrage / plancher de maintien (EVCS)

Incident du 24/08 12:24-12:28 : bascule de profil vers midday_hc_or_solar
avec SOC 58 %. Plancher de demarrage = min(midday_soc_min 75, abondance P6
40) = 40 ; plancher de maintien = max(soc_stop_min 29, 75-15) = 60. Le
systeme avait donc le droit de DEMARRER ce qu'il n'avait pas le droit de
MAINTENIR : cycle limite a la periode de min_run (3 min) sur toute la bande
[40 %, 60 %], et ~2,6 kW de PV ecretes a chaque creux (PV 10,24 kW -> 7,64 kW
quand la borne s'arrete, ESS sans injection).

Deux volets indissociables dans BATTERY_CLOUD_LOCKS.js :
- effectiveSocHoldFloor = min(regle du profil, plancher de demarrage
  effectif). Sans abondance, comportement inchange.
- l'abondance P6 se prouve au demarrage (batterie >= +20 A) mais se maintient
  en charge (batterie >= 0) : sinon la charge du vehicule, qui detourne
  precisement le surplus, detruit sa propre condition d'autorisation.

Golden 39 -> 40. Preuve par mutation sur midday-inversion-start-hold : code
d'avant -> stop / "Conditions de maintien perdues", apres -> step_up 19 A.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KKzkkVTG6KXnGXFFC7X9XJ
This commit is contained in:
Tallagrand Thierry
2026-08-24 12:46:18 +02:00
parent 522f47e821
commit 057e0f7d8f
5 changed files with 148 additions and 39 deletions

View File

@@ -405,12 +405,25 @@ const profileSocStartFloor =
const PV_ABUNDANCE_AVG_MIN_W = 4000;
const ABUNDANCE_BATTERY_CHARGE_MIN_A = 20;
const ABUNDANCE_SOC_FLOOR_PCT = 40;
const solarAbundance =
// 24/08/2026 — L'abondance se PROUVE au démarrage (batterie qui monte franchement,
// >= +20 A, avant qu'on touche à quoi que ce soit) mais elle se MAINTIENT tant que
// le PV reste fort ET que la batterie n'est pas devenue fournisseuse. Sans cette
// distinction, la charge du véhicule — qui détourne précisément le surplus — fait
// tomber le courant batterie sous +20 A et détruit sa propre condition
// d'autorisation : système bistable, oscillation à la période de min_run.
const solarAbundanceStart =
parsed.pvPowerAvg10mW >= PV_ABUNDANCE_AVG_MIN_W &&
parsed.batteryCurrentA >= ABUNDANCE_BATTERY_CHARGE_MIN_A;
const effectiveSocStartFloor = solarAbundance
? Math.min(profileSocStartFloor, ABUNDANCE_SOC_FLOOR_PCT)
: profileSocStartFloor;
const solarAbundanceHold =
parsed.pvPowerAvg10mW >= PV_ABUNDANCE_AVG_MIN_W &&
parsed.batteryCurrentA >= 0;
const solarAbundance = solarAbundanceStart; // conservé pour la traçabilité/debug
const abundanceSocFloor = (abundant) =>
abundant ? Math.min(profileSocStartFloor, ABUNDANCE_SOC_FLOOR_PCT) : profileSocStartFloor;
const effectiveSocStartFloor = abundanceSocFloor(solarAbundanceStart);
const profileSocHoldFloor =
context.profile === 'morning_solar' ? Math.max(params.socStopMin, params.morningSocMin - 10) :
@@ -418,6 +431,19 @@ const profileSocHoldFloor =
context.profile === 'evening_high_soc' ? params.eveningSocStopMin :
params.socStopMin;
// INVARIANT (24/08/2026) : on ne doit JAMAIS avoir le droit de DÉMARRER une charge
// qu'on n'a pas le droit de MAINTENIR. Le plancher de maintien doit donc rester
// <= au plancher de démarrage effectif.
// Incident du 24/08 midi : bascule de profil vers midday_hc_or_solar à 12:24 avec
// SOC 58 % => plancher de démarrage 40 % (abondance P6) mais plancher de maintien
// max(29, 75-15) = 60 % => démarrage autorisé, maintien refusé, cycle limite de
// 3 min (min_run) sur toute la bande [40 %, 60 %], et ~2,6 kW de PV écrêtés à
// chaque creux faute de preneur (ESS sans injection).
const effectiveSocHoldFloor = Math.min(
profileSocHoldFloor,
abundanceSocFloor(solarAbundanceHold)
);
const profileSpecificDischargeFloorA =
context.profile === 'morning_solar' ? params.morningBatteryMaxDischargeA :
context.profile === 'midday_hc_or_solar' ? params.middayBatteryMaxDischargeA :
@@ -553,7 +579,7 @@ const solarStartOk =
const rawSolarHoldOk =
(context.sourceMode === 'solar_only' || context.sourceMode === 'solar_potential') &&
commonSafetyHoldOk &&
parsed.soc >= profileSocHoldFloor &&
parsed.soc >= effectiveSocHoldFloor &&
parsed.batteryCurrentA >= profileBatteryDischargeFloorA &&
(
parsed.pvPowerW >= params.pvHoldMinW ||
@@ -862,7 +888,9 @@ return [{
profileSocStartFloor,
effectiveSocStartFloor,
solarAbundance,
solarAbundanceHold,
profileSocHoldFloor,
effectiveSocHoldFloor,
profileBatteryDischargeFloorA,
batteryCurrentHardExceeded,
mustStopHard,