DEUX APPORTS DU BRIEF APP f2bd07d, intégrés au design. §9 — LE PLANCHER SE DÉRIVE, et ça corrige mon étape D. Pour une borne, la valeur n'a besoin d'aucun champ : maxChargingCurrent.minValue × phaseCount × 230 est déjà sur la box, dans la classe du Thing. Écrire cette valeur dans needs.minPowerWAlways à l'appairage, comme je le proposais, créerait une COPIE QUI SE PÉRIME — un recâblage change le réglage phaseCount et la valeur stockée resterait fausse sans que rien ne le dise. C'est « une valeur dérivable est illégitime quand on la publie », appliqué au stockage. Le champ garde sa raison d'être pour les charges en watts, qui n'ont pas de plancher matériel à dériver. Étape D révisée : le mode marque que la charge veut un minimum, la passe éco dérive la valeur au cycle. Une seule source, jamais périmée. §10 — LA PISTE MONOPHASÉE EST TRANCHÉE, et par la carte Modbus elle-même. Il n'existe AUCUN registre de phases sur la Trydan : la campagne de portage a exploré 0x0BC2-0x0BD5 et 0x177A-0x1783, et le plugin le documente. La borne ne sait pas dire son câblage, donc n'offre rien pour le changer. phaseCount est un RÉGLAGE, déclaration de l'installateur recopiée dans un état. Et le détourner serait dangereux : déclarer 1 phase sur une borne câblée en 3 ferait budgéter 1 380 W et commander 6 A SUR TROIS PHASES — 4 140 W tirés. La borne démarrerait, au mauvais courant, sans erreur. Le réglage décrit le câblage, il n'est pas un bouton de réglage du plancher. Ce qui reste vrai : une borne CÂBLÉE en monophasé a un plancher de 1 380 W et se sert bien mieux sur une installation à petit surplus. C'est une décision de câblage à l'installation, à dire au dimensionnement — pas un réglage logiciel.
291 lines
16 KiB
Markdown
291 lines
16 KiB
Markdown
# Minimum réseau — design
|
||
|
||
> Ouvert le 2026-09-06. **Option 2 retenue** : livrer le minimum réseau maintenant, avec la
|
||
> machinerie du §10 ; le renommage l'accompagne. L'arbitrage au prix (option 3) reste l'horizon,
|
||
> il demande une source tarifaire et une prévision.
|
||
|
||
## 1. Le constat, et il est double
|
||
|
||
**Le mode `ChargingModeEcoWithMinCurrent` n'a jamais eu de minimum réseau** — non pas parce que
|
||
le code manquait, mais parce qu'il n'a jamais été atteint. Établi dans `DESIGN_3g.md` §5 : mort
|
||
par réécriture chez l'amont, puis mort par exclusion depuis 3g-2.
|
||
|
||
**Et son nom promet l'inverse de ce qu'il fait.** `MinCurrent` désigne **l'exigence de la borne**
|
||
— les 6 A en dessous desquels elle refuse de charger — **pas une garantie du mode**. Un lecteur y
|
||
voit « le mode maintient un courant minimum » ; le code, lui, ne fait que nommer un seuil
|
||
matériel.
|
||
|
||
> **Trois modes annoncés, deux implémentés — et celui qui manque est celui que l'utilisateur
|
||
> croyait utiliser.** C'est la formulation à retenir : le défaut n'est pas qu'une fonction
|
||
> manque, c'est qu'elle est *annoncée*.
|
||
|
||
## 2. Pourquoi livrer le minimum maintenant, sans attendre le prix
|
||
|
||
Chiffrage retenu, tarif de rachat récent à **0,01 €/kWh** :
|
||
|
||
| | coût effectif |
|
||
|---|---|
|
||
| charger en journée, 30 % au réseau | **0,073 €/kWh** |
|
||
| charger la nuit, 100 % au réseau | **0,180 €/kWh** |
|
||
|
||
Sur ces installations — **la majorité du parc neuf** — le minimum réseau en journée est
|
||
simplement le bon choix, et **aucun arbitrage n'est nécessaire pour le savoir**. Le prix ne
|
||
devient décisif que sur les anciens contrats à **0,1296 €**, où l'écart tombe à deux centimes et
|
||
**s'inverse sous 62 % de couverture solaire**.
|
||
|
||
**Conclusion de conception** : l'option 3 (comparer deux scénarios au prix) est le bon horizon,
|
||
mais elle exige une source tarifaire et une prévision. L'option 2 livre l'utile aujourd'hui, et
|
||
elle ne ferme pas la porte : un arbitrage au prix choisira plus tard *s'il faut* garantir, pas
|
||
*comment* garantir.
|
||
|
||
## 3. Condition 1 — la machinerie du §10, pas un second chemin d'achat
|
||
|
||
Le minimum réseau **passe par la passe éco**, celle qui achète déjà :
|
||
|
||
- **borné par l'autorisation de soutirage** (`DrawCap` / `DrawMargin`, trois sources) — comme
|
||
tout achat ;
|
||
- **`funding: Grid`** quand le surplus ne couvre pas ;
|
||
- **le motif dit combien est acheté**, comme `ECO_FLOOR_GRID` le fait déjà.
|
||
|
||
**Ce que cela EXCLUT explicitement** : porter le minimum par `EV_GRID_START`. Ses trois bornes
|
||
dérivent toutes du surplus (`budgetW > 0`, seuil proportionnel, consommation de tout le budget) ;
|
||
un minimum permanent en est indépendant par définition, donc les lever revient à créer le second
|
||
mécanisme. Et `EV_GRID_START` achète **hors** du registre d'autorisation — deux mécanismes dont
|
||
un seul est borné, et c'est le second qui ferait disjoncter. Raisonnement complet dans
|
||
`DESIGN_3g.md` §5.4.
|
||
|
||
## 4. Condition 2 — une puissance permanente, portée sans dupliquer `needs`
|
||
|
||
### Le champ
|
||
|
||
Un champ de plus **dans `needs`**, frère de `minEnergyWhPerDay` — pas une structure parallèle :
|
||
|
||
```
|
||
needs : {
|
||
minEnergyWhPerDay : Uint, // énergie, avec échéance
|
||
dailyDeadline : String,
|
||
minPowerWAlways : Uint // NOUVEAU — puissance, tant que la charge est là
|
||
}
|
||
```
|
||
|
||
**À ne pas confondre avec `minPowerW`** (`LM-1201`) :
|
||
|
||
| | ce que ça dit |
|
||
|---|---|
|
||
| `minPowerW` | **SI** on sert cette charge, au moins ceci. Un plancher de **modulation**. |
|
||
| `needs.minPowerWAlways` | On sert cette charge au moins ceci, **tant qu'elle est là**. Une **demande**, pas un plancher. |
|
||
|
||
Le premier borne une consigne ; le second en crée une. Les deux coexistent sans se contredire.
|
||
|
||
### POURQUOI une puissance ici, et une énergie ailleurs — et ce n'est PAS la modulation
|
||
|
||
L'ECS multi-paliers module aussi. Ce qui sépare vraiment les deux cas :
|
||
|
||
> **On ne sait pas combien de temps la voiture reste branchée.**
|
||
|
||
Une obligation en énergie **suppose une échéance** — c'est ce qui donne un dénominateur à la
|
||
cadence. Une borne n'en a aucune tant que personne n'en déclare. **« 4 140 W tant qu'elle est
|
||
là » est la seule formulation qui ne suppose rien.**
|
||
|
||
Un chauffe-eau, lui, a une échéance naturelle et une capacité finie : l'énergie y est la bonne
|
||
grandeur, et l'obligation éco la sert déjà. La différence est de **nature du besoin**, pas de
|
||
capacité technique de la charge.
|
||
|
||
### La composition dans la passe éco
|
||
|
||
`computeEcoTarget()` rend aujourd'hui une cadence dérivée de l'obligation en énergie. Elle rendra
|
||
le **maximum des deux** :
|
||
|
||
```
|
||
watts = max( cadenceObligation , minPowerWAlways )
|
||
```
|
||
|
||
Quatre invariants, et chacun ferme un silence :
|
||
|
||
1. **Le minimum ne se « tient » JAMAIS.** `tenue` ne décrit que l'obligation en énergie ; une
|
||
puissance demandée en permanence n'a pas de cible et ne se complète pas. Donc quand
|
||
l'obligation est tenue, `watts` retombe sur le minimum — **pas sur zéro**. C'est exactement la différence de
|
||
nature, et c'est là qu'elle se voit dans le code.
|
||
2. **`progress` ne décrit que l'obligation.** Sur une charge à minimum seul, `progress` est
|
||
**absent** — jamais à zéro : un avancement nul se lirait « rien livré », alors qu'il n'y a rien
|
||
à avancer.
|
||
3. **Le motif dit LEQUEL DES DEUX MORD.** Sans ça, l'écran affiche un nombre qu'il ne peut pas
|
||
expliquer — et deux causes différentes produiraient le même chiffre. Le motif gagne
|
||
`minAlwaysW` et une marque de ce qui borne.
|
||
4. **Un minimum non servi se DIT.** Si l'autorisation de soutirage ne le permet pas, il n'est
|
||
pas servi — et c'est `DRAW_CAP` qui le dit, en le nommant. Le silence ici ferait croire à un
|
||
minimum honoré, ce qui est précisément la promesse qu'on refuse de faire.
|
||
|
||
## 4-bis. Le RANG et la PRÉCÉDENCE — ce que le minimum ne bouscule pas
|
||
|
||
**Le minimum est servi à SON rang, il ne le contourne pas.** Un minimum déclassé par une charge
|
||
mieux rangée est le comportement voulu, et il découle de `LM-1002` : **le rang est ce que
|
||
l'installateur décide.** Le contredire par un mécanisme serait exactement le doublon qu'on refuse
|
||
partout — deux autorités sur le même ordre. Si le client veut sa voiture avant l'ECS, **il la
|
||
range avant**.
|
||
|
||
> Nuance à connaître, parce qu'elle décide du mot au §5-bis : la hiérarchie éco/confort
|
||
> **surplombe** le rang — « un confort de rang 1 passe après un éco de rang 3 »
|
||
> (`rulebasedscheduler.cpp` l. 243-246). Un minimum passe donc avant **tout** le confort, mais
|
||
> le rang le départage face à un **autre besoin éco**.
|
||
|
||
**`DRAW_CAP` et la réserve batterie PRIMENT.** Un minimum qui passerait outre un plafond de
|
||
branchement ferait disjoncter l'installation. C'est un **besoin**, pas une exception aux plafonds
|
||
— et quand un plafond l'empêche, c'est `DRAW_CAP` qui le dit en le nommant (invariant 4 du §4).
|
||
|
||
## 5. Condition 3 — nommer les trois modes pour ce qu'ils font
|
||
|
||
**Le fil ne peut pas être renommé** : `ChargingMode` est une énumération publiée, et changer ses
|
||
identifiants casserait tout client déployé. Ce qui se corrige, c'est **la description au
|
||
contrat** — c'est là que vit le mensonge, et c'est de là que l'app tire ses libellés.
|
||
|
||
| valeur | ce que le contrat dit AUJOURD'HUI | ce que le code FAIT |
|
||
|---|---|---|
|
||
| `ChargingModeNormal` | « Recharge immédiate au maximum disponible » | **MANUEL.** La borne sort de l'arbitrage — *« l'utilisateur pilote, l'arbitre s'abstient »*. Le moteur ne commande **rien**. Seule la protection de surcharge la touche encore. |
|
||
| `ChargingModeEco` | « Recharge sur surplus solaire uniquement » | exact |
|
||
| `ChargingModeEcoWithMinCurrent` | « Eco + courant minimum réseau (6 A) si pas de surplus » | **rien** — le chemin n'est pas atteint |
|
||
|
||
**`Normal` n'est pas un « boost ».** Un boost commanderait le maximum ; celui-ci ne commande pas.
|
||
La seule chose que le moteur y fasse encore est de délester en surcharge — ce qui doit rester, et
|
||
doit être **dit** : un mode « manuel » qui coupe quand même mérite de l'annoncer.
|
||
|
||
## 5-bis. LE MOT — et ni « garanti » ni « prioritaire » ne tiennent
|
||
|
||
La question est la bonne, et elle est la même que celle qu'on est en train de corriger : **un nom
|
||
de mode qui promet autre chose que ce que le code fait.** Les deux candidats échouent au même
|
||
test, dans deux registres différents.
|
||
|
||
| mot | vrai quand | FAUX quand | verdict |
|
||
|---|---|---|---|
|
||
| **garanti** | jamais entièrement | plafond de soutirage atteint, réserve batterie, ou un autre éco mieux rangé épuise l'autorisation | **promet une certitude que rien ne tient** |
|
||
| **prioritaire** | face à **tout** le confort — la passe éco surplombe le rang | face à un **autre besoin éco** mieux rangé | **vrai en régime normal, faux exactement dans le cas d'échec** |
|
||
|
||
**« Prioritaire » est le moindre mensonge, et c'est ce qui le rend pire ici** : il est vrai 95 %
|
||
du temps, donc il ne se fait jamais démentir — sauf le jour où la voiture ne charge pas, où il
|
||
dit précisément l'inverse de ce qui s'est passé. Un mot qui ne se trompe que dans le cas
|
||
intéressant est un mot qui ne sera pas relu. Et il contredirait `LM-1002` dans le libellé : il
|
||
laisse croire que le mode réordonne, alors que **c'est l'installateur qui range**.
|
||
|
||
### Le mot retenu : on nomme le FINANCEMENT
|
||
|
||
C'est la seule propriété qui tienne **toujours**, et c'est aussi la vraie différence avec `Eco` :
|
||
|
||
> **`Eco` dépense le surplus. Ce mode-ci ACHÈTE le complément.**
|
||
|
||
- **Libellé du mode : « Surplus + minimum réseau ».** Il dit ce que le moteur fera — servir sur
|
||
surplus, et acheter le reste jusqu'au minimum — et **ne promet rien sur le résultat**, qui
|
||
dépend du rang et des plafonds.
|
||
- **Nom du champ : `minPowerWAlways`.** Grammaire de son frère `minEnergyWhPerDay` : les deux
|
||
**déclarent une demande**, aucun ne promet une livraison. Personne ne lit
|
||
`minEnergyWhPerDay` comme une garantie ; ce champ-ci doit se lire pareil.
|
||
- **Mots bannis dans le code, le contrat et l'app : « garanti », « prioritaire ».** Y compris
|
||
dans les commentaires — c'est là que les promesses se réintroduisent.
|
||
|
||
**Ce qui dit le résultat, c'est le motif, pas le nom.** `ECO_FLOOR_GRID` chiffre ce qui est
|
||
acheté ; `DRAW_CAP` nomme le plafond qui a empêché. Le nom déclare l'intention, le motif rapporte
|
||
les faits — et c'est la séparation qu'on tient partout ailleurs.
|
||
|
||
## 6. Les étapes, à comportement constant d'abord
|
||
|
||
**Étape A — corriger le contrat.** Les trois descriptions ci-dessus, plus la note sur `MinCurrent`
|
||
qui nomme l'exigence de la borne. **Aucun code.** Comportement constant par construction, et c'est
|
||
le pas qui rend la suite lisible.
|
||
|
||
**Étape B — porter `minPowerWAlways` dans `LoadNeeds`, le persister, le publier**, sans que la
|
||
passe éco s'en serve. Aller-retour `Get`/`SetLoadConfig`, schéma déclaré une fois. Comportement
|
||
constant : le champ existe et ne décide de rien.
|
||
|
||
**Étape C — la passe éco le prend en compte.** Le seul pas qui change le comportement, et il
|
||
arrive quand tout le reste est déjà en place et vérifiable. Avec les quatre invariants du §4, le
|
||
motif enrichi, et le `DRAW_CAP` qui nomme le minimum.
|
||
|
||
**Étape D — le mode s'y branche.** `ChargingModeEcoWithMinCurrent` posé sur une borne écrit
|
||
`minPowerWAlways = plancher de la borne`. C'est le pas qui rend le mode opérant — et il
|
||
vient en dernier, parce qu'il n'a de sens qu'une fois C livrée.
|
||
|
||
## 7. Ce qu'on ne saura pas prouver au banc, et il faut le dire
|
||
|
||
L'installation de `.75` produit **2 kWc** contre un plancher de borne triphasée à **4 140 W**. Un
|
||
minimum sur cette borne serait donc **toujours** financé au réseau, jamais au surplus — le banc
|
||
ne peut pas exercer le cas mixte, celui où le minimum complète un surplus partiel.
|
||
|
||
Il l'exercera en revanche très bien sur une charge en watts — le chauffe-eau — dès qu'elle aura un
|
||
compteur réel (`TEST_TERRAIN.md` §12). **C'est la même précondition que pour l'étape 4 de la
|
||
commande perdue**, et elle est maintenant nommée par un quatrième chemin.
|
||
|
||
## 8. Ce lot ne sera PAS vérifiable sur machine — écrit avant, pas découvert en mesurant
|
||
|
||
La voiture a été débranchée le 2026-09-06. `pluggedIn` retombe, la borne **sort de `loads[]`**
|
||
(vérifié : `['chauffe-eau', 'pac-terrain']`), et rien ne la commande plus.
|
||
|
||
**Donc `EV_MIN_GRID` tiendra sur ses tests unitaires et sa contre-épreuve, pas sur un relevé.**
|
||
C'est acceptable *à condition de le dire maintenant* : le découvrir au moment de mesurer ferait
|
||
prendre une absence de mesure pour une absence de défaut.
|
||
|
||
Ce qu'il faudra pour lever la réserve, et c'est cumulatif :
|
||
|
||
1. une voiture branchée — trivial, mais elle décide de tout le reste ;
|
||
2. un lien V2C sain — trois semaines que c'est le blocage (`PORTING_STATUS.md`) ;
|
||
3. et pour le **cas mixte** — minimum complété par un surplus partiel — une charge en watts avec
|
||
un compteur réel, parce que 2 kWc contre un plancher de 4 140 W ne le produira jamais.
|
||
|
||
Les trois sont indépendantes, et aucune n'est à notre main aujourd'hui.
|
||
|
||
## 9. Correction apportée par le brief app du 2026-09-07 — le plancher se DÉRIVE
|
||
|
||
L'agent app a raison, et ça simplifie l'étape D. Pour une borne, la valeur du minimum **n'a
|
||
besoin d'aucun champ** : elle est déjà sur la box, dans la classe du Thing.
|
||
|
||
```
|
||
maxChargingCurrent.minValue (6 A) × phaseCount (3) × 230 V = 4 140 W
|
||
```
|
||
|
||
**Conséquence de conception** : `minPowerWAlways` n'a pas à porter la valeur pour un VE — le
|
||
**mode** est déjà l'interrupteur, et la valeur se dérive au moment de décider. Écrire la valeur
|
||
dans le champ à l'appairage, comme l'étape D le proposait, créerait une **copie qui se périme** :
|
||
un recâblage change le réglage `phaseCount`, et la valeur stockée resterait fausse sans que rien
|
||
ne le dise. C'est la doctrine « une valeur dérivable est illégitime quand on la publie »,
|
||
appliquée au **stockage**.
|
||
|
||
**Le champ garde sa raison d'être pour les charges en watts** — un chauffe-eau à minimum permanent
|
||
n'a pas de plancher matériel à dériver, il faut le déclarer. Les deux cas coexistent :
|
||
|
||
| charge | d'où vient la valeur |
|
||
|---|---|
|
||
| borne VE | **dérivée** de `maxChargingCurrent.minValue × phaseCount` |
|
||
| charge en watts | **déclarée** dans `needs.minPowerWAlways` |
|
||
|
||
**Étape D révisée** : le mode `EcoWithMinCurrent` n'écrit rien ; il marque que la charge veut un
|
||
minimum, et la passe éco dérive la valeur au cycle. Une seule source, jamais périmée.
|
||
|
||
## 10. La piste « basculer en monophasé » — TRANCHÉE, et elle ne tient pas
|
||
|
||
> « En monophasé le plancher tomberait à 1 380 W, et les 2 214 W du 6 septembre auraient suffi. »
|
||
|
||
**Il n'existe aucun registre de phases dans la carte Modbus de la Trydan.** Constat de la campagne
|
||
de portage, écrit dans le plugin lui-même (`integrationpluginv2c.cpp` l. 70-77) :
|
||
|
||
> *« Il n'existe aucun registre de phases dans la carte Modbus V2C (0x0BC2-0x0BD5,
|
||
> 0x177A-0x1783) : le câblage mono/tri n'est pas lisible sur la borne. »*
|
||
|
||
La carte a été explorée entièrement. La borne ne sait pas **dire** son câblage, donc a fortiori
|
||
elle n'offre rien pour le **changer**. `phaseCount` est un **réglage** — une déclaration de
|
||
l'installateur sur le câblage physique, recopiée dans un état pour que le moteur la lise.
|
||
|
||
### Et le danger, s'il fallait une raison de plus
|
||
|
||
Baisser le réglage à 1 « pour faire tomber le plancher » serait un **mensonge sur le matériel**,
|
||
et le plugin documente déjà l'effet dans le sens inverse : le moteur convertit les watts alloués
|
||
en un courant, et un facteur de phases faux donne un courant faux.
|
||
|
||
Déclarer 1 phase sur une borne câblée en 3 ferait budgéter 1 380 W et commander 6 A **sur trois
|
||
phases** — soit **4 140 W tirés**. La borne démarrerait, au mauvais courant, **sans erreur**.
|
||
|
||
> **Le réglage décrit le câblage ; il n'est pas un bouton de réglage du plancher.** Un champ qu'on
|
||
> détourne pour son effet de bord est un champ qui ment, et celui-ci ment vers le haut, sur du
|
||
> courant réel.
|
||
|
||
**Ce qui reste vrai dans la piste** : sur une installation à petit surplus, une borne **câblée en
|
||
monophasé** a un plancher de 1 380 W et se sert bien mieux. C'est une décision de **câblage à
|
||
l'installation**, pas un réglage logiciel — et elle mérite d'être dite au dimensionnement.
|