La cible du matin devient une energie a couvrir, plus un palier
La regle classait le lendemain en trois paliers a partir de son TOTAL (>= 18 kWh -> 35 %, >= 10 -> 55 %, sinon 85 %) et ignorait donc l heure d arrivee de l energie. Le 03/09 demain annoncait 33,9 kWh — donc "fort", donc 35 % — avec un matin creux (0,03 kWh a 7 h, 0,77 a 8 h) et un croisement a 9 h. Meme defaut que le plancher du soir, a l autre bout de la nuit. Releve de la nuit 02-03/09, requis contre reel : 00:30 62,4 / 53 ; 03:00 50,4 / 45 ; 04:30 41,7 / 37 ; 06:00 37,1 / 22 — cible fixe a 35 tout du long. Le required_soc decroit proprement avec la nuit qui se consomme (6,1 -> 0 kWh a couvrir), la batterie est restee sous la ligne toute la nuit, et le bilan le savait des 00:30. A 03:00 EDF a ete relache cinq points sous la ligne ; le reseau est revenu de 07:24 a 10:33 au plein tarif, 1,49 kWh. La cible vaut desormais min(90, max(palier ou advisor, required_soc)). Sens unique delibere : la direction corrigee est celle de l echec observe, et le principe unificateur des deux cablages est que le bilan ne pousse jamais dans le sens "couvre moins bien la nuit". Plafond 90 parce qu au-dela la batterie cesse d etre un puits et evince la production du matin, et parce que le bilan publie sans ecretage des valeurs superieures a 100 — sans ce plafond la cible serait inatteignable et EDF resterait allume toute la nuit. C est deja la borne du chemin advisor, aucune constante nouvelle. Pas d oscillateur : required_soc decroit de facon monotone pendant la nuit et la passe unique de recovery reste en place. Gardes et repli identiques au plancher du soir, kill-switch existant. Golden 70 -> 75. Quatre mutations font tomber chacune exactement un scenario different. Un cinquieme scenario fige une LIMITE CONNUE, mesuree par sonde : la passe unique de recovery latche le relachement pour toute la session, donc la cible dynamique n ameliore que la premiere passe. §26 avait deja designe le bon critere de re-engagement — un budget d energie restant a couvrir, qui appartient au bilan ; la suite touche la machine a etats de la nuit et fera l objet d un chantier propre. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KKzkkVTG6KXnGXFFC7X9XJ
This commit is contained in:
@@ -361,6 +361,7 @@ const batteryMorningTargetPct =
|
||||
? nightPrev.batteryMorningTargetPct
|
||||
: params.evcsNightTargetWeakForecastPct;
|
||||
const advisorNightTargetApplied = !!nightPrev.advisorNightTargetApplied;
|
||||
const balanceMorningTargetApplied = !!nightPrev.balanceMorningTargetApplied;
|
||||
const maxBatteryDischargePoints =
|
||||
Number.isFinite(nightPrev.maxBatteryDischargePoints) ? nightPrev.maxBatteryDischargePoints : 0;
|
||||
const gridTakeoverTriggerPct =
|
||||
@@ -1080,6 +1081,7 @@ return [{
|
||||
batteryHardFloorPct,
|
||||
batteryMorningTargetPct,
|
||||
advisorNightTargetApplied,
|
||||
balanceMorningTargetApplied,
|
||||
maxBatteryDischargePoints,
|
||||
gridTakeoverTriggerPct,
|
||||
|
||||
|
||||
@@ -1319,7 +1319,7 @@ else {
|
||||
// =====================================================
|
||||
if (nightEvcs?.nightSessionActive) {
|
||||
reason.push(
|
||||
`Nuit EVCS: target=${round1(nightEvcs.batteryMorningTargetPct)}%${nightEvcs.advisorNightTargetApplied ? ' (ADVISOR)' : ''} | trigger réseau=${round1(nightEvcs.gridTakeoverTriggerPct)}%`
|
||||
`Nuit EVCS: target=${round1(nightEvcs.batteryMorningTargetPct)}%${nightEvcs.advisorNightTargetApplied ? ' (ADVISOR)' : ''}${nightEvcs.balanceMorningTargetApplied ? ' (BILAN)' : ''} | trigger réseau=${round1(nightEvcs.gridTakeoverTriggerPct)}%`
|
||||
);
|
||||
|
||||
if (Number.isFinite(nightEvcs.nightSessionStartSocPct)) {
|
||||
|
||||
@@ -285,7 +285,7 @@ const advisorNightTargetApplied =
|
||||
advisorTargetAgeMin !== null &&
|
||||
advisorTargetAgeMin <= ADVISOR_TARGET_MAX_AGE_MIN;
|
||||
|
||||
const batteryMorningTargetPct =
|
||||
const batteryMorningTargetBasePct =
|
||||
advisorNightTargetApplied
|
||||
? clamp(
|
||||
params.advisorNightTargetSocPct,
|
||||
@@ -294,6 +294,65 @@ const batteryMorningTargetPct =
|
||||
)
|
||||
: batteryMorningTargetRulePct;
|
||||
|
||||
// =====================================================
|
||||
// CIBLE DU MATIN : UNE ENERGIE A COUVRIR, PAS UN PALIER (03/09/2026)
|
||||
// =====================================================
|
||||
// La regle historique classe la journee du lendemain en trois paliers
|
||||
// (>= 18 kWh -> 35 %, >= 10 -> 55 %, sinon 85 %) a partir de son TOTAL. Elle
|
||||
// est donc aveugle a l'heure d'arrivee de l'energie : le 03/09, demain
|
||||
// annoncait 33,9 kWh — « fort », donc 35 % — mais avec un matin creux
|
||||
// (0,03 kWh a 7 h, 0,77 a 8 h, 1,81 a 9 h) et un croisement a 9 h.
|
||||
//
|
||||
// C'est le meme defaut que le plancher du soir (cf. le bloc BILAN de
|
||||
// BATTERY_CLOUD_LOCKS), a l'autre bout de la nuit : un palier fige contre une
|
||||
// energie mesurable. Le bilan publie la seconde, instant par instant.
|
||||
//
|
||||
// RELEVE DE LA NUIT 02-03/09, qui a motive ce cablage :
|
||||
//
|
||||
// heure requis SOC reel cible fixe
|
||||
// 00:30 62,4 53 35
|
||||
// 03:00 50,4 45 35 <- EDF relache ici, 5 points sous la ligne
|
||||
// 04:30 41,7 37 35
|
||||
// 06:00 37,1 22 35 <- hysteresis, puis reseau PLEIN TARIF
|
||||
// de 07:24 a 10:33 (1,49 kWh)
|
||||
//
|
||||
// La batterie est restee sous la ligne toute la nuit et le bilan le savait des
|
||||
// 00:30. Le `required_soc` decroit proprement avec la nuit qui se consomme
|
||||
// (6,1 -> 0 kWh a couvrir) : c'est bien une cible instantanee, pas une
|
||||
// constante deguisee.
|
||||
//
|
||||
// SENS UNIQUE, comme pour le plancher du soir : le bilan ne peut que RELEVER
|
||||
// la cible, jamais l'abaisser. La direction corrigee est celle de l'echec
|
||||
// observe (cible trop basse -> batterie a plat avant le croisement -> reseau
|
||||
// au plein tarif le matin). L'abaisser — la regle dit 85 % l'hiver quand le
|
||||
// bilan dira parfois moins — ouvrirait un risque neuf que rien n'a encore
|
||||
// valide. Le principe unificateur : le bilan ne pousse JAMAIS dans le sens
|
||||
// « couvre moins bien la nuit ».
|
||||
//
|
||||
// PLAFOND 90 % : au-dela, la batterie cesse d'etre un puits et la production
|
||||
// du matin se fait evincer (cf. mesures du 31/08). Charger au reseau au-dela
|
||||
// de 90 echangerait un kWh bon marche cette nuit contre plusieurs kWh censures
|
||||
// demain. C'est deja la borne du chemin advisor : aucune constante nouvelle.
|
||||
//
|
||||
// PAS D'OSCILLATEUR : `required_soc` decroit de facon monotone pendant la nuit
|
||||
// (l'energie restant a couvrir ne fait que diminuer), et la passe unique de
|
||||
// recovery (`nightRecoveryDonePrev`, 27/08) reste en place.
|
||||
//
|
||||
// REPLI identique au plancher du soir : fraicheur, statut, valeur finie —
|
||||
// sinon regle historique. Kill-switch existant : `evcs_balance_enabled` OFF
|
||||
// fige `balance_last_run` et la cible retombe seule sur les paliers.
|
||||
const BALANCE_TARGET_MAX_AGE_MIN = 30;
|
||||
const balanceTargetAgeMin = minutesSince(params.balanceLastRunMs, nowMs);
|
||||
const balanceMorningTargetApplied =
|
||||
params.balanceStatusOk &&
|
||||
Number.isFinite(params.balanceRequiredSocPct) &&
|
||||
balanceTargetAgeMin !== null &&
|
||||
balanceTargetAgeMin <= BALANCE_TARGET_MAX_AGE_MIN;
|
||||
|
||||
const batteryMorningTargetPct = balanceMorningTargetApplied
|
||||
? Math.min(90, Math.max(batteryMorningTargetBasePct, params.balanceRequiredSocPct))
|
||||
: batteryMorningTargetBasePct;
|
||||
|
||||
const maxBatteryDischargePoints =
|
||||
nightSessionActive && Number.isFinite(nightSessionStartSocPct)
|
||||
? Math.max(0, nightSessionStartSocPct - batteryMorningTargetPct)
|
||||
@@ -898,8 +957,13 @@ return [{
|
||||
|
||||
batteryHardFloorPct,
|
||||
batteryMorningTargetPct,
|
||||
batteryMorningTargetBasePct,
|
||||
balanceMorningTargetApplied,
|
||||
balanceTargetAgeMin:
|
||||
balanceTargetAgeMin === null ? null : Math.round(balanceTargetAgeMin * 10) / 10,
|
||||
batteryMorningTargetRulePct,
|
||||
advisorNightTargetApplied,
|
||||
balanceMorningTargetApplied,
|
||||
maxBatteryDischargePoints,
|
||||
gridTakeoverTriggerPct,
|
||||
|
||||
|
||||
Reference in New Issue
Block a user