La protection regardait la maison, pas ce qu on demande au parc
Deux fermetures ce dimanche midi. Mesurees a l instant de la decision, contacteur encore ouvert donc production non censuree : maison 4025 et 4981 W, mais parc reellement sollicite 1132 et 772 W pour un plafond de 5100. La decharge batterie mesuree aux memes instants — 1345 et 1223 W — colle a maison moins PV, pas a la maison brute. Le solaire couvrait la cuisson ; la regle a ferme le contacteur pour rien, 0,50 kWh importes en plein soleil. Elle n avait bien fonctionne hier soir que parce qu il n y avait plus de PV. Les deux seuils portent desormais sur max(0, maison - PV). Contacteur ouvert cette grandeur EST la decharge batterie ; ferme, un bridage du MPPT la fait surestimer, donc l erreur pousse a rester ferme et jamais a relacher tot. Golden 20 -> 24, dont les deux releves de midi et le contrefactuel du nuage. Le test de mutation a montre que la premiere serie ne couvrait que le seuil de fermeture : il a fallu ecrire le cas du soleil revenu pour qu elle morde. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KKzkkVTG6KXnGXFFC7X9XJ
This commit is contained in:
76
HANDOFF.md
76
HANDOFF.md
@@ -4520,6 +4520,82 @@ qu'apres 12:26.
|
|||||||
4. si la tension batterie se remet a disparaitre, `plafond_decharge_w` doit
|
4. si la tension batterie se remet a disparaitre, `plafond_decharge_w` doit
|
||||||
rester a ~5100 et non tomber a 0.
|
rester a ~5100 et non tomber a 0.
|
||||||
|
|
||||||
|
### MEME JOUR, DEFAUT PLUS PROFOND : la regle comptait la maison BRUTE
|
||||||
|
Dimanche 06/09, deux fermetures du contacteur : **12:07 -> 12:22** (four +
|
||||||
|
plaque) et **13:34 -> 13:42** (cuisson + JEEP branchee a 13:31). Journee
|
||||||
|
nuageuse, parc reduit de moitie. Mesure a la resolution native **a l'instant ou
|
||||||
|
chaque decision a ete prise, contacteur encore ouvert** — donc sur une
|
||||||
|
production non censuree :
|
||||||
|
|
||||||
|
| fermeture | maison | PV | **parc reellement sollicite** | decharge batterie mesuree |
|
||||||
|
|---|---:|---:|---:|---:|
|
||||||
|
| 12:07, cuisson seule | 4 025 W | ~2 890 W | **1 132 W** | 1 345 W |
|
||||||
|
| 13:34, cuisson + JEEP | 4 981 W | ~4 210 W | **772 W** | 1 223 W |
|
||||||
|
|
||||||
|
La decharge batterie colle a `maison - PV`, pas a la maison brute : le controle
|
||||||
|
croise est net. Le plafond du parc est de 5 100 W — **il etait a 22 % et 15 % de
|
||||||
|
sa limite, et la regle a ferme le contacteur pour rien.** Meme en integrant les
|
||||||
|
periodes fermees (ou le PV peut etre bride, donc chiffres pessimistes), la
|
||||||
|
demande maximale sur le parc n'a atteint que 3 046 et 2 437 W. Cout : 0,50 kWh
|
||||||
|
importes en plein soleil, pour rien.
|
||||||
|
|
||||||
|
Cause : la regle du 05/09 compare la MAISON BRUTE au plafond de decharge du
|
||||||
|
parc. Elle n'avait bien fonctionne la veille au soir que parce qu'il n'y avait
|
||||||
|
plus de PV — la maison brute valait alors la demande sur le parc, et le pic de
|
||||||
|
-97,4 A etait reel. Des qu'il y a du soleil, elle ferme sur une charge que le
|
||||||
|
solaire couvre deja.
|
||||||
|
|
||||||
|
Correctif (deploye 06/09 13:49,
|
||||||
|
`tools/patch-20260906b-demande-sur-le-parc.mjs`) : les deux seuils portent sur
|
||||||
|
**`max(0, maison - PV)`**. `acOverSafeLimit` n'est PAS touche — il compare la
|
||||||
|
charge au debit des onduleurs, qui portent la maison entiere quelle qu'en soit
|
||||||
|
la source.
|
||||||
|
|
||||||
|
⚠️ **Pourquoi ce n'est pas un oscillateur**, alors que la grandeur n'est pas
|
||||||
|
invariante a la decision (fermer peut brider le MPPT, §28) : contacteur OUVERT,
|
||||||
|
`maison - PV` EST la decharge batterie, grandeur physique exacte ; contacteur
|
||||||
|
FERME, un bridage du MPPT fait BAISSER PV donc MONTER `maison - PV` — l'erreur
|
||||||
|
pousse a rester ferme, jamais a relacher tot ; et le relachement exige de toute
|
||||||
|
facon le dwell (6 a 20 min), tres au-dela du transitoire d'etablissement mesure
|
||||||
|
a ~150 s. Erreur unilaterale et bornee, dans la direction qui menage le parc.
|
||||||
|
|
||||||
|
Golden 20 -> 24 : les deux releves de ce midi (aucune fermeture attendue), le
|
||||||
|
contrefactuel du nuage (meme cuisson a 4 500 W avec PV 200 W -> on ferme), et
|
||||||
|
`h-midi-soleil-revenu-relache` (contacteur ferme, cuisson a 3 000 W mais PV
|
||||||
|
2 000 -> le parc n'est plus sollicite qu'a 1 000 W, on rend). Les scenarios de
|
||||||
|
protection existants portent desormais `pv_power_w: '0'` explicitement : ils
|
||||||
|
decrivent le soir, ou le parc est seul.
|
||||||
|
|
||||||
|
⚠️ Piege evite grace au test de mutation : la premiere serie de scenarios ne
|
||||||
|
couvrait que le seuil de FERMETURE. Remettre le relachement sur la maison brute
|
||||||
|
ne faisait echouer aucun test — il a fallu ecrire `h-midi-soleil-revenu-relache`
|
||||||
|
pour que la mutation morde.
|
||||||
|
|
||||||
|
### Ce que la JEEP n'a PAS cause, et la question qu'elle pose quand meme
|
||||||
|
Le propriétaire attribuait la fermeture de midi a la conjonction cuisson +
|
||||||
|
voiture. Les faits : **la voiture n'etait pas branchee** pendant l'episode de
|
||||||
|
12:07 (EVCS a 0 W de bout en bout) ; elle n'a joue que dans celui de 13:34, qui
|
||||||
|
n'aurait pas du fermer non plus. Les deux fermetures viennent de la regle, pas
|
||||||
|
de la charge.
|
||||||
|
|
||||||
|
Reste que la question de fond est juste, et elle n'est pas traitee : `houseAcPowerW`
|
||||||
|
inclut l'EVCS, donc **la voiture est comptee comme une charge menagere
|
||||||
|
incompressible** alors qu'elle est la seule charge entierement compressible du
|
||||||
|
foyer — le controleur EVCS peut la ramener a 6 A ou l'arreter en un cycle. Le
|
||||||
|
jour ou la demande sur le parc depassera vraiment le plafond avec la voiture
|
||||||
|
dedans, la reponse economique est de brider la voiture d'abord et de ne fermer
|
||||||
|
le contacteur que pour le reste. Aucun des deux workflows ne sait faire cela
|
||||||
|
aujourd'hui : le controleur EVCS ignore le plafond de decharge du BMS, et
|
||||||
|
l'hysteresis ne sait pas commander la borne. A instruire.
|
||||||
|
|
||||||
|
### Anomalie annexe : une session de charge comptee deux fois
|
||||||
|
`Session de charge terminee : JEEP, 4.20 kWh` a ete emis **deux fois** — a
|
||||||
|
02:52:31 (fin de la charge HC reelle) et a 13:42:32 (debranchement apres onze
|
||||||
|
minutes de charge diurne), avec la MEME valeur au centieme. Le compteur de
|
||||||
|
session de `tools/historise-evcs.mjs` semble rejouer la session de la nuit au
|
||||||
|
lieu d'en ouvrir une nouvelle. A corriger, sans quoi la table `session_charge`
|
||||||
|
double-compte.
|
||||||
|
|
||||||
### Reste ouvert
|
### Reste ouvert
|
||||||
- Detecteur de gel dans le service d'historisation : une grandeur qui ne bouge
|
- Detecteur de gel dans le service d'historisation : une grandeur qui ne bouge
|
||||||
pas alors qu'une autre du meme bilan bouge = alerte. Non fait.
|
pas alors qu'une autre du meme bilan bouge = alerte. Non fait.
|
||||||
|
|||||||
@@ -990,6 +990,36 @@ protection jusqu'a la fin du dwell.
|
|||||||
Golden-hysteresis 11 -> 20, six mutations tombant chacune sur un jeu different.
|
Golden-hysteresis 11 -> 20, six mutations tombant chacune sur un jeu different.
|
||||||
Detail : HANDOFF §57.
|
Detail : HANDOFF §57.
|
||||||
|
|
||||||
|
#### D-ter. La protection comptait la maison brute, pas la demande sur le parc (06/09/2026)
|
||||||
|
|
||||||
|
La regle de fermeture comparait `sensor.victron_consumption_power_l1` au
|
||||||
|
plafond de decharge du BMS. Or le parc n'est sollicite que de ce que le solaire
|
||||||
|
ne couvre pas. Dimanche 06/09, deux fermetures, mesurees a l'instant de la
|
||||||
|
decision (contacteur encore ouvert, donc production non censuree) :
|
||||||
|
|
||||||
|
| fermeture | maison | PV | parc sollicite | decharge batterie |
|
||||||
|
|---|---:|---:|---:|---:|
|
||||||
|
| 12:07, cuisson | 4 025 W | ~2 890 W | **1 132 W** | 1 345 W |
|
||||||
|
| 13:34, cuisson + JEEP | 4 981 W | ~4 210 W | **772 W** | 1 223 W |
|
||||||
|
|
||||||
|
Plafond du parc : 5 100 W. Il etait a 22 % et 15 % de sa limite — deux
|
||||||
|
fermetures pour rien, 0,50 kWh importes en plein soleil. La regle n'avait bien
|
||||||
|
fonctionne la veille au soir que parce qu'il n'y avait plus de PV.
|
||||||
|
|
||||||
|
Les deux seuils portent desormais sur `max(0, maison - PV)`. Contacteur ouvert,
|
||||||
|
cette grandeur EST la decharge batterie ; ferme, elle est le contrefactuel
|
||||||
|
« ce que le parc devrait fournir si on ouvrait », qu'un eventuel bridage du MPPT
|
||||||
|
fait SURESTIMER — erreur unilaterale, qui pousse a rester ferme. Golden 20 -> 24.
|
||||||
|
Detail : HANDOFF §57.
|
||||||
|
|
||||||
|
⚠️ Question ouverte que cet episode souleve : `houseAcPowerW` inclut l'EVCS,
|
||||||
|
donc la voiture est comptee comme une charge incompressible alors qu'elle est la
|
||||||
|
seule charge entierement compressible du foyer. Le jour ou la demande sur le
|
||||||
|
parc depassera vraiment le plafond avec la voiture dedans, il faudrait brider la
|
||||||
|
voiture avant de fermer le contacteur — ce qu'aucun des deux workflows ne sait
|
||||||
|
faire : le controleur EVCS ignore le plafond de decharge du BMS, et
|
||||||
|
l'hysteresis ne commande pas la borne.
|
||||||
|
|
||||||
#### E. Le plancher du soir etait un niveau, pas une energie (03/09/2026)
|
#### E. Le plancher du soir etait un niveau, pas une energie (03/09/2026)
|
||||||
|
|
||||||
`evening_soc_start_min` = 85 % tente de dire « garde de quoi passer la nuit ».
|
`evening_soc_start_min` = 85 % tente de dire « garde de quoi passer la nuit ».
|
||||||
|
|||||||
@@ -388,3 +388,19 @@ heure_reelle,etat,courant_mA,c1_mV,c2_mV,c3_mV,c4_mV,c5_mV,c6_mV,c7_mV,c8_mV,c9_
|
|||||||
2026-09-06T11:01:04.050Z,Y230202C30371426,,3393,3390,3390,3391,3392,3393,3390,3390,3390,3391,3393,3391,3390,3390,3393,3
|
2026-09-06T11:01:04.050Z,Y230202C30371426,,3393,3390,3390,3391,3392,3393,3390,3390,3390,3391,3393,3391,3390,3390,3393,3
|
||||||
2026-09-06T11:01:04.145Z,Y230202C30371425,,3392,3390,3390,3390,3391,3391,3391,3389,3389,3391,3393,3392,3391,3391,3391,4
|
2026-09-06T11:01:04.145Z,Y230202C30371425,,3392,3390,3390,3390,3391,3391,3391,3389,3389,3391,3393,3392,3391,3391,3391,4
|
||||||
2026-09-06T11:01:04.362Z,Y230202C30371427,,3392,3390,3390,3389,3391,3391,3388,3389,3389,3390,3392,3389,3389,3389,3391,4
|
2026-09-06T11:01:04.362Z,Y230202C30371427,,3392,3390,3390,3389,3391,3391,3388,3389,3389,3390,3392,3389,3389,3389,3391,4
|
||||||
|
2026-09-06T11:11:04.465Z,Y230202C30371426,,3391,3389,3389,3389,3391,3391,3388,3389,3389,3389,3391,3389,3390,3390,3391,3392,3390,3390,3391,3392,3392,3390,3390,3390,3391,3392,3391,3390,3390,3393,5
|
||||||
|
2026-09-06T11:11:04.593Z,Y230202C30371425,,3390,3389,3389,3390,3389,3390,3389,3388,3388,3390,3391,3390,3390,3390,3390,3
|
||||||
|
2026-09-06T11:11:04.688Z,Y230202C30371431,,3390,3388,3389,3388,3389,3389,3386,3387,3387,3388,3390,3387,3388,3388,3389,4
|
||||||
|
2026-09-06T11:11:04.813Z,Y230202C30371427,,3389,3388,3387,3388,3389,3390,3387,3387,3387,3388,3390,3388,3388,3388,3390,3
|
||||||
|
2026-09-06T11:21:04.908Z,Y230202C30371426,,3391,3389,3389,3390,3390,3391,3389,3389,3388,3389,3392,3390,3389,3388,3391,4
|
||||||
|
2026-09-06T11:21:05.006Z,Y230202C30371425,,3384,3383,3383,3384,3382,3383,3384,3382,3383,3383,3385,3384,3385,3384,3383,3
|
||||||
|
2026-09-06T11:21:05.132Z,Y230202C30371431,,3383,3382,3383,3381,3382,3382,3380,3381,3381,3381,3384,3382,3382,3381,3381,4
|
||||||
|
2026-09-06T11:21:05.257Z,Y230202C30371427,,3383,3382,3382,3383,3384,3383,3382,3382,3383,3382,3383,3383,3383,3384,3384,2
|
||||||
|
2026-09-06T11:31:05.360Z,Y230202C30371426,,3385,3384,3385,3386,3386,3386,3384,3385,3383,3385,3386,3385,3385,3384,3386,3
|
||||||
|
2026-09-06T11:31:05.460Z,Y230202C30371425,,3393,3391,3391,3392,3392,3392,3391,3390,3391,3392,3394,3393,3393,3392,3393,4
|
||||||
|
2026-09-06T11:31:05.587Z,Y230202C30371431,,3393,3391,3391,3390,3393,3392,3389,3390,3390,3391,3394,3390,3390,3390,3391,5
|
||||||
|
2026-09-06T11:31:05.714Z,Y230202C30371427,,3391,3389,3388,3389,3391,3391,3389,3388,3389,3389,3391,3389,3390,3390,3390,3
|
||||||
|
2026-09-06T11:41:05.817Z,Y230202C30371426,,3391,3389,3389,3390,3390,3392,3389,3389,3388,3389,3391,3391,3389,3389,3390,4
|
||||||
|
2026-09-06T11:41:05.944Z,Y230202C30371425,,3350,3350,3350,3350,3350,3349,3350,3349,3349,3350,3350,3350,3350,3350,3350,1
|
||||||
|
2026-09-06T11:41:06.039Z,Y230202C30371431,,3349,3348,3348,3348,3348,3349,3347,3348,3347,3347,3349,3348,3348,3348,3347,2
|
||||||
|
2026-09-06T11:41:06.226Z,Y230202C30371427,,3342,3342,3343,3344,3343,3342,3343,3343,3343,3343,3342,3343,3343,3344,3344,2
|
||||||
|
|||||||
|
@@ -66,6 +66,7 @@ function summarize(out) {
|
|||||||
shouldNotify: !!d.should_notify,
|
shouldNotify: !!d.should_notify,
|
||||||
evcsGridOverride: !!c.evcs_grid_override,
|
evcsGridOverride: !!c.evcs_grid_override,
|
||||||
plafondDechargeW: c.plafond_decharge_w,
|
plafondDechargeW: c.plafond_decharge_w,
|
||||||
|
demandeParcW: c.demande_parc_w,
|
||||||
dwellMinutes: c.dwell_minutes,
|
dwellMinutes: c.dwell_minutes,
|
||||||
dwellEcoule: c.dwell_ecoule,
|
dwellEcoule: c.dwell_ecoule,
|
||||||
reserveDejaCondamnee: !!c.reserve_deja_condamnee,
|
reserveDejaCondamnee: !!c.reserve_deja_condamnee,
|
||||||
|
|||||||
@@ -63,6 +63,13 @@ const recoveryBase = {
|
|||||||
const parcQuatreModules = {
|
const parcQuatreModules = {
|
||||||
battery_max_discharge_a: '100',
|
battery_max_discharge_a: '100',
|
||||||
battery_voltage_v: '51',
|
battery_voltage_v: '51',
|
||||||
|
// ⚠️ PV NULLE, ET C'EST STRUCTUREL. Depuis le 06/09 les deux seuils portent
|
||||||
|
// sur `max(0, maison - PV)`, ce que le parc est REELLEMENT sollicite de
|
||||||
|
// fournir, et non sur la maison brute. Les scenarios de protection decrivent
|
||||||
|
// le soir : le parc est seul, la maison brute EST la demande sur le parc. Les
|
||||||
|
// scenarios `h-midi-*` couvrent le cas diurne, ou le solaire couvre la
|
||||||
|
// cuisson et ou la regle ne doit PAS fermer.
|
||||||
|
pv_power_w: '0',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const scenarios = [
|
export const scenarios = [
|
||||||
@@ -405,6 +412,7 @@ export const scenarios = [
|
|||||||
...recoveryBase,
|
...recoveryBase,
|
||||||
battery_max_discharge_a: '100',
|
battery_max_discharge_a: '100',
|
||||||
battery_voltage_v: 'unavailable',
|
battery_voltage_v: 'unavailable',
|
||||||
|
pv_power_w: '0',
|
||||||
soc_percent: '65',
|
soc_percent: '65',
|
||||||
house_ac_power_w: '2000',
|
house_ac_power_w: '2000',
|
||||||
last_on_at: minutesAgo(T_MS, 40),
|
last_on_at: minutesAgo(T_MS, 40),
|
||||||
@@ -426,6 +434,7 @@ export const scenarios = [
|
|||||||
...recoveryBase,
|
...recoveryBase,
|
||||||
battery_max_discharge_a: 'unavailable',
|
battery_max_discharge_a: 'unavailable',
|
||||||
battery_voltage_v: 'unavailable',
|
battery_voltage_v: 'unavailable',
|
||||||
|
pv_power_w: '0',
|
||||||
soc_percent: '65',
|
soc_percent: '65',
|
||||||
house_ac_power_w: '2000',
|
house_ac_power_w: '2000',
|
||||||
last_on_at: minutesAgo(T_MS, 40),
|
last_on_at: minutesAgo(T_MS, 40),
|
||||||
@@ -447,6 +456,7 @@ export const scenarios = [
|
|||||||
...recoveryBase,
|
...recoveryBase,
|
||||||
battery_max_discharge_a: 'unavailable',
|
battery_max_discharge_a: 'unavailable',
|
||||||
battery_voltage_v: 'unavailable',
|
battery_voltage_v: 'unavailable',
|
||||||
|
pv_power_w: '0',
|
||||||
soc_percent: '65',
|
soc_percent: '65',
|
||||||
house_ac_power_w: '2000',
|
house_ac_power_w: '2000',
|
||||||
last_on_at: minutesAgo(T_MS, 40),
|
last_on_at: minutesAgo(T_MS, 40),
|
||||||
@@ -459,4 +469,102 @@ export const scenarios = [
|
|||||||
gridAction: 'turn_off',
|
gridAction: 'turn_off',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
// =====================================================
|
||||||
|
// MIDI ENSOLEILLE : LE SOLAIRE COUVRE LA CUISSON (06/09/2026)
|
||||||
|
// =====================================================
|
||||||
|
// Deux fermetures ce dimanche, mesurees a la resolution native A L'INSTANT
|
||||||
|
// DE LA DECISION, contacteur encore ouvert donc production non censuree :
|
||||||
|
//
|
||||||
|
// 12:07, cuisson seule maison 4 025 W parc sollicite 1 132 W
|
||||||
|
// 13:34, cuisson + JEEP maison 4 981 W parc sollicite 772 W
|
||||||
|
//
|
||||||
|
// Controle croise : la decharge batterie valait 1 345 et 1 223 W aux memes
|
||||||
|
// instants — elle colle a `maison - PV`, pas a la maison brute. Le plafond du
|
||||||
|
// parc est de 5 100 W : il etait a 22 % et 15 % de sa limite, et la regle a
|
||||||
|
// ferme le contacteur pour rien.
|
||||||
|
{
|
||||||
|
id: 'h-midi-solaire-couvre-la-cuisson',
|
||||||
|
label: 'RELEVE 06/09 12:07 : maison 4025 W mais PV 2893 W, le parc ne fournit que 1132 W — aucune fermeture',
|
||||||
|
time: T,
|
||||||
|
overrides: {
|
||||||
|
...recoveryBase,
|
||||||
|
...parcQuatreModules,
|
||||||
|
grid_latch: 'off',
|
||||||
|
grid_state: 'off',
|
||||||
|
soc_percent: '69',
|
||||||
|
house_ac_power_w: '4025',
|
||||||
|
pv_power_w: '2893',
|
||||||
|
},
|
||||||
|
expect: {
|
||||||
|
state: 'observe_only',
|
||||||
|
gridAction: 'none',
|
||||||
|
latchAction: 'none',
|
||||||
|
demandeParcW: 1132,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'h-midi-jeep-en-plus-solaire-couvre-encore',
|
||||||
|
label: 'RELEVE 06/09 13:34 : maison 4981 W dont la JEEP, PV 4209 W, parc a 772 W — aucune fermeture non plus',
|
||||||
|
time: T,
|
||||||
|
overrides: {
|
||||||
|
...recoveryBase,
|
||||||
|
...parcQuatreModules,
|
||||||
|
grid_latch: 'off',
|
||||||
|
grid_state: 'off',
|
||||||
|
soc_percent: '86',
|
||||||
|
house_ac_power_w: '4981',
|
||||||
|
pv_power_w: '4209',
|
||||||
|
evcs_ac_power_w: '1621',
|
||||||
|
evcs_charging: 'on',
|
||||||
|
evcs_surplus_state: 'charging_step_up_battery',
|
||||||
|
evcs_source_mode: 'Solaire',
|
||||||
|
},
|
||||||
|
expect: {
|
||||||
|
state: 'observe_only',
|
||||||
|
gridAction: 'none',
|
||||||
|
demandeParcW: 772,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'h-midi-nuage-le-parc-encaisse-tout',
|
||||||
|
label: 'CONTREFACTUEL : meme cuisson a 4500 W mais le nuage passe, PV 200 W — le parc encaisse 4300 W, on ferme',
|
||||||
|
time: T,
|
||||||
|
overrides: {
|
||||||
|
...recoveryBase,
|
||||||
|
...parcQuatreModules,
|
||||||
|
grid_latch: 'off',
|
||||||
|
grid_state: 'off',
|
||||||
|
soc_percent: '69',
|
||||||
|
house_ac_power_w: '4500',
|
||||||
|
pv_power_w: '200',
|
||||||
|
},
|
||||||
|
expect: {
|
||||||
|
state: 'turn_on_charge_au_dessus_du_parc',
|
||||||
|
gridAction: 'turn_on',
|
||||||
|
latchAction: 'turn_on',
|
||||||
|
demandeParcW: 4300,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'h-midi-soleil-revenu-relache',
|
||||||
|
label: "Le nuage est passe, contacteur ferme : la cuisson continue a 3000 W mais le PV en couvre 2000 — le parc n'est plus sollicite qu'a 1000 W, on rend le reseau",
|
||||||
|
time: T,
|
||||||
|
overrides: {
|
||||||
|
...recoveryBase,
|
||||||
|
...parcQuatreModules,
|
||||||
|
soc_percent: '69',
|
||||||
|
house_ac_power_w: '3000',
|
||||||
|
pv_power_w: '2000',
|
||||||
|
last_on_at: minutesAgo(T_MS, 40),
|
||||||
|
charge_high_last_at: minutesAgo(T_MS, 25),
|
||||||
|
balance_required_soc: '92',
|
||||||
|
balance_last_run: bilanRunAgo(T_MS, 3),
|
||||||
|
},
|
||||||
|
expect: {
|
||||||
|
state: 'turn_off_vehicle_unplugged',
|
||||||
|
gridAction: 'turn_off',
|
||||||
|
demandeParcW: 1000,
|
||||||
|
setChargeHighLastAt: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
109
tools/patch-20260906b-demande-sur-le-parc.mjs
Normal file
109
tools/patch-20260906b-demande-sur-le-parc.mjs
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
// La regle de protection comptait la maison BRUTE, pas ce que le parc est
|
||||||
|
// reellement sollicite de fournir.
|
||||||
|
//
|
||||||
|
// Dimanche 06/09, deux fermetures du contacteur, mesurees a la resolution
|
||||||
|
// native (InfluxDB) A L'INSTANT OU LA DECISION A ETE PRISE, contacteur encore
|
||||||
|
// ouvert — donc sur une production non censurée :
|
||||||
|
//
|
||||||
|
// fermeture 1 (12:07, cuisson seule) maison 4 025 W parc sollicite 1 132 W
|
||||||
|
// fermeture 2 (13:34, cuisson + JEEP) maison 4 981 W parc sollicite 772 W
|
||||||
|
//
|
||||||
|
// Controle croise : la decharge batterie mesuree aux memes instants valait
|
||||||
|
// 1 345 et 1 223 W — elle colle a `maison - PV`, pas a la maison brute.
|
||||||
|
//
|
||||||
|
// Le plafond du parc est de 5 100 W. Le parc etait donc a 22 % et 15 % de sa
|
||||||
|
// limite, et la regle a ferme le contacteur pour rien : le solaire (3 a 4 kW)
|
||||||
|
// couvrait la cuisson. Elle n'avait bien fonctionne hier soir que parce qu'il
|
||||||
|
// n'y avait plus de PV — la maison brute valait alors la demande sur le parc.
|
||||||
|
//
|
||||||
|
// Correctif : les deux seuils portent sur `max(0, maison - PV)`.
|
||||||
|
//
|
||||||
|
// ⚠️ POURQUOI CE N'EST PAS UN OSCILLATEUR, alors que la grandeur n'est pas
|
||||||
|
// invariante a la decision (fermer le contacteur peut brider le MPPT, §28) :
|
||||||
|
// - contacteur OUVERT, `maison - PV` EST la decharge batterie, la grandeur
|
||||||
|
// physique exacte ;
|
||||||
|
// - contacteur FERME, brider le MPPT fait BAISSER PV, donc MONTER
|
||||||
|
// `maison - PV` : l'erreur pousse a rester ferme, jamais a relacher tot ;
|
||||||
|
// - et le relachement exige de toute facon le dwell (6 a 20 min), tres
|
||||||
|
// au-dela du transitoire d'etablissement mesure a ~150 s.
|
||||||
|
// L'erreur est donc unilaterale et bornee, dans la direction qui menage le parc.
|
||||||
|
//
|
||||||
|
// `acOverSafeLimit` n'est PAS touche : il compare la charge au debit des
|
||||||
|
// onduleurs, qui portent la maison entiere quelle qu'en soit la source.
|
||||||
|
//
|
||||||
|
// node tools/patch-20260906b-demande-sur-le-parc.mjs
|
||||||
|
|
||||||
|
import fs from 'node:fs';
|
||||||
|
|
||||||
|
const F = 'workflows/victron-grid-hysteresis-v1.json';
|
||||||
|
const wf = JSON.parse(fs.readFileSync(F, 'utf8'));
|
||||||
|
const calc = wf.nodes.find((n) => n.name === 'Compute Grid Hysteresis');
|
||||||
|
if (!calc) throw new Error('noeud introuvable');
|
||||||
|
|
||||||
|
function remplace(t, a, b, quoi) {
|
||||||
|
if (!t.includes(a)) throw new Error(`ancre introuvable (${quoi}) : ${a.slice(0, 70)}`);
|
||||||
|
return t.replace(a, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
let c = calc.parameters.jsCode;
|
||||||
|
|
||||||
|
if (c.includes('demandeParcW')) {
|
||||||
|
console.log(' deja fait');
|
||||||
|
} else {
|
||||||
|
// 1. la grandeur
|
||||||
|
c = remplace(c,
|
||||||
|
'const chargeAuDessusDuParc = plafondDechargeW > 0 && houseAcPowerW >= seuilFermetureW;',
|
||||||
|
`// CE QUE LE PARC EST REELLEMENT SOLLICITE DE FOURNIR (06/09/2026)
|
||||||
|
// La maison brute n'est pas la demande sur la batterie : le solaire en couvre
|
||||||
|
// une partie. Mesure du 06/09 a l'instant des deux fermetures, contacteur
|
||||||
|
// encore ouvert : maison 4 025 et 4 981 W, parc sollicite 1 132 et 772 W pour
|
||||||
|
// un plafond de 5 100 W — deux fermetures pour rien. Controle croise : la
|
||||||
|
// decharge batterie valait alors 1 345 et 1 223 W.
|
||||||
|
//
|
||||||
|
// Contacteur ouvert, cette grandeur EST la decharge batterie. Ferme, elle est
|
||||||
|
// le contrefactuel « ce que le parc devrait fournir si on ouvrait », et un
|
||||||
|
// eventuel bridage du MPPT par la presence du reseau la fait SURESTIMER —
|
||||||
|
// erreur unilaterale, qui pousse a rester ferme, jamais a relacher tot.
|
||||||
|
const demandeParcW = Math.max(0, houseAcPowerW - pvPowerW);
|
||||||
|
|
||||||
|
const chargeAuDessusDuParc = plafondDechargeW > 0 && demandeParcW >= seuilFermetureW;`,
|
||||||
|
'fermeture');
|
||||||
|
|
||||||
|
c = remplace(c,
|
||||||
|
'const chargeSousSeuilBas = plafondDechargeW > 0 && houseAcPowerW < seuilRelachementW;',
|
||||||
|
'const chargeSousSeuilBas = plafondDechargeW > 0 && demandeParcW < seuilRelachementW;',
|
||||||
|
'relachement');
|
||||||
|
|
||||||
|
c = remplace(c,
|
||||||
|
"const setChargeHighLastAt = plafondDechargeW > 0 && !chargeSousSeuilBas;",
|
||||||
|
"const setChargeHighLastAt = plafondDechargeW > 0 && !chargeSousSeuilBas;",
|
||||||
|
'ecriture'); // inchange : suit desormais demandeParcW via chargeSousSeuilBas
|
||||||
|
|
||||||
|
// 2. les raisons
|
||||||
|
c = remplace(c,
|
||||||
|
' `Charge maison ${Math.round(houseAcPowerW)}W au-dessus de ${Math.round(seuilFermetureW)}W `\n + `(80% du plafond de decharge BMS ${Math.round(plafondDechargeW)}W) : le reseau prend la difference`);',
|
||||||
|
' `Demande sur le parc ${Math.round(demandeParcW)}W (maison ${Math.round(houseAcPowerW)}W - PV ${Math.round(pvPowerW)}W) `\n + `au-dessus de ${Math.round(seuilFermetureW)}W, soit 80% du plafond de decharge BMS ${Math.round(plafondDechargeW)}W : le reseau prend la difference`);',
|
||||||
|
'raison fermeture');
|
||||||
|
|
||||||
|
c = remplace(c,
|
||||||
|
" ? `Charge maison retombee a ${Math.round(houseAcPowerW)}W depuis ${round1(minutesDepuisChargeHaute || 0)} min : `",
|
||||||
|
" ? `Demande sur le parc retombee a ${Math.round(demandeParcW)}W depuis ${round1(minutesDepuisChargeHaute || 0)} min : `",
|
||||||
|
'raison dwell');
|
||||||
|
|
||||||
|
c = remplace(c,
|
||||||
|
" : `Charge maison ${Math.round(houseAcPowerW)}W encore au-dessus de ${Math.round(seuilRelachementW)}W : on garde le reseau`);",
|
||||||
|
" : `Demande sur le parc ${Math.round(demandeParcW)}W (maison ${Math.round(houseAcPowerW)}W - PV ${Math.round(pvPowerW)}W) encore au-dessus de ${Math.round(seuilRelachementW)}W : on garde le reseau`);",
|
||||||
|
'raison maintien');
|
||||||
|
|
||||||
|
// 3. le debug
|
||||||
|
c = remplace(c, ' plafond_decharge_w: Math.round(plafondDechargeW),',
|
||||||
|
' plafond_decharge_w: Math.round(plafondDechargeW),\n demande_parc_w: Math.round(demandeParcW),',
|
||||||
|
'debug');
|
||||||
|
|
||||||
|
calc.parameters.jsCode = c;
|
||||||
|
console.log(' calcul : les deux seuils portent sur maison - PV');
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.writeFileSync(F, JSON.stringify(wf, null, 2) + '\n');
|
||||||
|
console.log(`\n${F} ecrit.`);
|
||||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user