Fenetre d etablissement : ne plus decider sur les mesures du transitoire

A la fermeture du contacteur, PV, import et courant batterie ne decrivent aucun
regime permanent pendant ~150 s (mesure du 31/08). La boucle de 30 s y prenait
cinq decisions, et la moyenne PV 10 min en gardait la trace dix minutes de plus
-- le systeme lisait "plus de soleil", en concluait qu il avait besoin du
reseau, et confirmait sa propre premisse.

Aucun seuil nouveau : le systeme publiait deja l horodatage de la transition et
personne ne le lisait. Pendant la fenetre, on ne demarre pas et on ne laisse pas
une charge s arreter sur ces mesures ; les step-down restent libres.

Deux asymetries voulues : la fenetre ne s ouvre qu a l ARRIVEE du reseau (a sa
disparition les onduleurs reprennent tout, les gardes doivent agir plus vite),
et elle suspend le confort, jamais le risque.

Golden 42 -> 48, chaque cas apparie a son contrefactuel hors fenetre. Mutation :
gridSettling force a false -> 2 echecs cibles, restauration -> 48/48.

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-31 18:18:03 +02:00
parent f07d4cb0f1
commit f0479945c4
8 changed files with 359 additions and 6 deletions

View File

@@ -14,6 +14,11 @@ const MIDDAY = '2026-08-08T13:00:00+02:00';
const AFTERNOON = '2026-08-08T15:30:00+02:00';
const EVENING = '2026-08-08T18:00:00+02:00';
// Fenêtre d'établissement du réseau (31/08/2026) : horodatage de la dernière
// transition d'AC-IN, en secondes epoch. Le harnais fige l'horloge sur `time`,
// donc on exprime l'âge de la transition par rapport à cet instant simulé.
const MORNING_TS = Date.parse(MORNING) / 1000;
const VEHICLE_NIGHT = {
evcs_vehicle_connected: 'on',
evcs_device_connected: 'on',
@@ -1019,4 +1024,183 @@ export const scenarios = [
takeoverHelperService: 'turn_off',
},
},
// ======= FENÊTRE D'ÉTABLISSEMENT DU RÉSEAU (31/08/2026) =======
// Mesuré ce jour-là : à la fermeture du contacteur, le réseau reprend toute la
// charge de la maison, le solaire bascule vers la batterie (ou se fait brider
// s'il n'y a plus de tampon : 2663 -> 559 W), puis l'import redescend à la
// consigne en ~150 s. Pendant ce temps la boucle de 30 s prend cinq décisions
// sur des mesures qui ne décrivent aucun régime permanent.
// Chaque cas est apparié à son contrefactuel hors fenêtre : sans cet appariement
// le test ne prouverait rien.
{
id: 'etablissement-maintien-charge',
label: 'Réseau fermé il y a 30 s, PV effondrée à 600 W et batterie à -15 A : la charge en cours NE DOIT PAS être arrêtée',
time: MORNING,
overrides: {
evcs_vehicle_connected: 'on', evcs_device_connected: 'on',
evcs_charging_switch: 'on', control_latch: 'on', charge_permission_now: 'on',
last_start_at: '2026-08-08 08:00:00',
soc_percent: '60',
evcs_current_a: '16', evcs_set_current_a: '16', evcs_command_current_a: '16',
evcs_ac_power_w: '3680',
house_ac_power_w: '4400', vebus_ac_out_l1_power_w: '4400',
pv_power_w: '600', pv_power_10m_avg_w: '3900', pv_trend_w: '-2000',
grid_state: 'on', victron_acin1_available: '1', grid_power_w: '1800',
battery_current_a: '-15',
acin1_last_changed_ts: String(MORNING_TS - 30),
grid_switch_last_changed_ts: String(MORNING_TS - 30),
},
expect: {
profile: "morning_solar",
sourceMode: "solar_only",
state: "charging_step_down_battery",
decision: "step_down",
desiredCharging: true,
currentA: 14,
takeoverNeeded: false,
edfOnAction: false,
takeoverHelperService: "turn_off",
},
},
{
id: 'etablissement-termine-arret-normal',
label: 'CONTREFACTUEL : mêmes mesures, transition vieille de 400 s → fenêtre fermée, la garde de maintien reprend la main et arrête',
time: MORNING,
overrides: {
evcs_vehicle_connected: 'on', evcs_device_connected: 'on',
evcs_charging_switch: 'on', control_latch: 'on', charge_permission_now: 'on',
last_start_at: '2026-08-08 08:00:00',
soc_percent: '60',
evcs_current_a: '16', evcs_set_current_a: '16', evcs_command_current_a: '16',
evcs_ac_power_w: '3680',
house_ac_power_w: '4400', vebus_ac_out_l1_power_w: '4400',
pv_power_w: '600', pv_power_10m_avg_w: '3900', pv_trend_w: '-2000',
grid_state: 'on', victron_acin1_available: '1', grid_power_w: '1800',
battery_current_a: '-15',
acin1_last_changed_ts: String(MORNING_TS - 400),
grid_switch_last_changed_ts: String(MORNING_TS - 400),
},
expect: {
profile: "morning_solar",
sourceMode: "solar_only",
state: "stop_conditions_lost",
decision: "stop",
desiredCharging: false,
currentA: 6,
takeoverNeeded: false,
edfOnAction: false,
takeoverHelperService: "turn_off",
},
},
{
id: 'etablissement-securite-non-suspendue',
label: "SÉCURITÉ : dans la fenêtre, batterie à -40 A → l'arrêt dur reste actif. La fenêtre suspend le confort, jamais le risque",
time: MORNING,
overrides: {
evcs_vehicle_connected: 'on', evcs_device_connected: 'on',
evcs_charging_switch: 'on', control_latch: 'on', charge_permission_now: 'on',
last_start_at: '2026-08-08 08:00:00',
soc_percent: '60',
evcs_current_a: '16', evcs_set_current_a: '16', evcs_command_current_a: '16',
evcs_ac_power_w: '3680',
house_ac_power_w: '4400', vebus_ac_out_l1_power_w: '4400',
pv_power_w: '600', pv_power_10m_avg_w: '3900', pv_trend_w: '-2000',
grid_state: 'on', victron_acin1_available: '1', grid_power_w: '1800',
battery_current_a: '-40',
acin1_last_changed_ts: String(MORNING_TS - 30),
grid_switch_last_changed_ts: String(MORNING_TS - 30),
},
expect: {
profile: "morning_solar",
sourceMode: "solar_only",
state: "battery_current_hard_stop",
decision: "stop_hard",
desiredCharging: false,
currentA: 6,
takeoverNeeded: false,
edfOnAction: false,
takeoverHelperService: "turn_off",
},
},
{
id: 'etablissement-ouverture-aucune-fenetre',
label: "ASYMÉTRIE : réseau DISPARU il y a 30 s → aucune fenêtre. Les onduleurs reprennent tout, les gardes doivent agir plus vite, pas moins",
time: MORNING,
overrides: {
evcs_vehicle_connected: 'on', evcs_device_connected: 'on',
evcs_charging_switch: 'on', control_latch: 'on', charge_permission_now: 'on',
last_start_at: '2026-08-08 08:00:00',
soc_percent: '60',
evcs_current_a: '16', evcs_set_current_a: '16', evcs_command_current_a: '16',
evcs_ac_power_w: '3680',
house_ac_power_w: '4400', vebus_ac_out_l1_power_w: '4400',
pv_power_w: '600', pv_power_10m_avg_w: '3900', pv_trend_w: '-2000',
grid_state: 'on', victron_acin1_available: '1', grid_power_w: '1800',
battery_current_a: '-15',
grid_state: 'off', victron_acin1_available: '0', grid_power_w: '0',
acin1_last_changed_ts: String(MORNING_TS - 30),
grid_switch_last_changed_ts: String(MORNING_TS - 30),
},
expect: {
profile: "morning_solar",
sourceMode: "solar_only",
state: "stop_conditions_lost",
decision: "stop",
desiredCharging: false,
currentA: 6,
takeoverNeeded: false,
edfOnAction: false,
takeoverHelperService: "turn_off",
},
},
{
id: 'etablissement-pas-de-demarrage',
label: "Réseau fermé il y a 30 s, rien en charge : on ne démarre pas non plus sur des mesures d'établissement",
time: MORNING,
overrides: {
evcs_vehicle_connected: 'on', evcs_device_connected: 'on',
soc_percent: '60', pv_power_w: '4500', pv_power_10m_avg_w: '4200',
pv_trend_w: '100', battery_current_a: '10',
house_ac_power_w: '900', vebus_ac_out_l1_power_w: '900',
grid_state: 'on', victron_acin1_available: '1', grid_power_w: '1800',
acin1_last_changed_ts: String(MORNING_TS - 30),
grid_switch_last_changed_ts: String(MORNING_TS - 30),
},
expect: {
profile: "morning_solar",
sourceMode: "solar_only",
state: "idle_wait_conditions",
decision: "hold",
desiredCharging: false,
currentA: 6,
takeoverNeeded: false,
edfOnAction: false,
takeoverHelperService: "turn_off",
},
},
{
id: 'etablissement-demarrage-hors-fenetre',
label: 'CONTREFACTUEL : mêmes mesures, transition vieille de 400 s → le démarrage solaire a bien lieu',
time: MORNING,
overrides: {
evcs_vehicle_connected: 'on', evcs_device_connected: 'on',
soc_percent: '60', pv_power_w: '4500', pv_power_10m_avg_w: '4200',
pv_trend_w: '100', battery_current_a: '10',
house_ac_power_w: '900', vebus_ac_out_l1_power_w: '900',
grid_state: 'on', victron_acin1_available: '1', grid_power_w: '1800',
acin1_last_changed_ts: String(MORNING_TS - 400),
grid_switch_last_changed_ts: String(MORNING_TS - 400),
},
expect: {
profile: "morning_solar",
sourceMode: "solar_only",
state: "start_min_current",
decision: "start",
desiredCharging: true,
currentA: 6,
takeoverNeeded: false,
edfOnAction: false,
takeoverHelperService: "turn_off",
},
},
];