Mesuré AVANT d'implanter, et la mesure a corrigé le diagnostic. Le constat disait « un budget de surplus qui ignore ces watts ». Ce n'est pas ce qui se passe : quand une charge consomme et OBÉIT, la comptabilité est juste — sa conso est recréditée puis réallouée à elle-même, et les suivantes ne reçoivent que le surplus réel. Export 500 W, A à 4 400 W obéissante, B reçoit 500 W. Corriger cela aurait été corriger un non-défaut. Le vrai défaut est plus grave, et l'hypothèse est écrite dans le code : « le budget est une mesure dont la conso de la charge a déjà été retranchée », c'est-à-dire « je peux rendre ces watts parce que je peux les reprendre ». C'est un prêt, et rien ne vérifie que l'emprunteur rembourse. Mesuré, le cas où la promesse est engagée : A commandée à 0 par son plancher, ses 4 400 W redistribués à B. Si A obéit, tout est juste. Si A n'obéit pas — commande perdue, mode manuel, véhicule qui limite — la maison tire 8 400 W pour 4 900 de production : 3 500 W importés que personne n'a décidés. Et l'identité surplus + recrédit − alloué == restant TIENT dans les deux cas. C'est ce qui distingue un prêt d'une erreur : la comptabilité est juste, c'est l'hypothèse sur laquelle elle repose qui n'est pas vérifiée — donc aucun contrôle de cohérence ne pouvait l'attraper. Le test épingle le comportement ACTUEL, correct SOUS HYPOTHÈSE, et dit à quelle condition il devient faux : le jour où le recrédit sera conditionné à l'obéissance constatée, c'est l'allocation de B qu'il faudra revoir, pas le test. Écrit pour qu'on ne le « corrige » pas par réflexe. Ce que le design proposait — retrancher la conso subie du budget — est SANS OBJET : elle en est déjà retranchée par le compteur. La note est conservée pour que personne ne la réimplante en la croyant oubliée. Suite : simulation 60/60, loadmodel 22/22, charging 48/48 identique à la référence, doxygen 0 avertissement. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015F7G5VeaPVSMeVNjiGj36p
145 lines
8.6 KiB
Markdown
145 lines
8.6 KiB
Markdown
# DESIGN — la commande perdue, et l'écart que personne ne mesure
|
||
|
||
*(Ouvert le 2026-09-01, sur le premier constat de `RELEVE_APP_20260831.md`. Le design avant le
|
||
code, comme pour le délestage et le §10.)*
|
||
|
||
## 1. Le fait, tel qu'il a été mesuré
|
||
|
||
Coupure Modbus. La box journalise `Restoring manual values of false 6 A` trois secondes après.
|
||
La borne, elle, reste à `power: true`, **7 A** — la consigne que l'arbitre lui avait donnée au
|
||
cycle d'avant. Le lien revient : **rien ne réapplique**. En mode manuel personne ne commande,
|
||
donc l'écart est **permanent**.
|
||
|
||
**4,4 kW réellement tirés, `draw.committedW` à 0, un budget de surplus de 2 349 W qui ignore ces
|
||
watts.**
|
||
|
||
## 2. Ce que le fait dit vraiment — et il est plus large que son titre
|
||
|
||
« Réappliquer la commande de l'arbitre » ne décrit pas le cas : **en mode manuel, l'arbitre ne
|
||
commande pas**. Ce qui s'est passé est autre chose, et c'est pire :
|
||
|
||
1. Le proxy amont a voulu restaurer des valeurs manuelles (`false 6 A`) ;
|
||
2. l'écriture a échoué pendant l'indisponibilité ;
|
||
3. **personne ne l'a rejouée** ;
|
||
4. l'organe est resté sur une consigne **dont plus aucun commandeur ne se réclame** — ni
|
||
l'arbitre, qui ne commande plus, ni le mode manuel, dont la consigne n'a pas atterri.
|
||
|
||
> **La troisième forme de la même famille.** Après la télémétrie qui ne distinguait pas une
|
||
> issue de son contraire (`ECS-111`) et la valeur de retour qui faisait passer un refus pour un
|
||
> succès (`ECS-415`), voici **l'exécution** : une écriture perdue, un état sain en apparence,
|
||
> `available` à vrai, et personne pour savoir que la commande n'a jamais atterri.
|
||
|
||
## 3. Ce que le moteur a déjà — et ce qui manque
|
||
|
||
| Pièce | État |
|
||
|---|---|
|
||
| Dernière consigne écrite, charges en watts | **existe** — `m_currentSetpointW`, `m_currentStage` |
|
||
| Dernière consigne écrite, **bornes VE** | **MANQUE** — `EvAdapter` ne garde que `m_lastActionAt` |
|
||
| Ce que l'organe FAIT | **existe** — `EvCharger::maxChargingCurrent()`, états du Thing |
|
||
| Ce que l'organe MESURE, et ce que ça vaut | **existe** — `deviceMeasurement()`, `measurement.source` (`LM-1105`/`LM-1106`) |
|
||
| Détection de la FIN d'une indisponibilité | **MANQUE** — le seul chemin est `clearFault()`, actionné par l'opérateur |
|
||
| Comparaison commandé / constaté | **MANQUE** — nulle part |
|
||
|
||
**Le constat porte sur les bornes, et c'est précisément là que la mémoire de la commande manque.**
|
||
Ce n'est pas un hasard : les adaptateurs de charges en watts gardent leur consigne parce qu'ils
|
||
en ont besoin pour l'écrêtage de verrou ; la borne, elle, était pilotée par le proxy amont, qui
|
||
n'a jamais eu à se relire.
|
||
|
||
## 4. LES DÉCISIONS
|
||
|
||
**4.1 — Réappliquer, ou signaler ?**
|
||
Réappliquer automatiquement une consigne vieille de plusieurs minutes est un geste **dangereux** :
|
||
le monde a changé pendant l'indisponibilité — le surplus, l'heure, l'échéance. Rejouer une
|
||
décision périmée, c'est décider avec des données mortes, exactement ce que le mode dégradé L2
|
||
refuse de faire.
|
||
|
||
**Recommandation : SIGNALER toujours, RÉAPPLIQUER jamais telle quelle.** Au retour de
|
||
disponibilité, le cycle normal reprend la main et décide sur des données fraîches — c'est déjà
|
||
le bon comportement. Ce qui manque n'est pas le rejeu, c'est que **l'écart soit dit tant qu'il
|
||
dure**. Et il dure indéfiniment quand personne ne reprend la main, ce qui est le cas du relevé.
|
||
|
||
**4.2 — Que publie-t-on, et sous quelle forme ?**
|
||
Un écart n'a de sens que rapporté à ce qu'on sait de l'organe. Proposition : un objet `command`
|
||
au niveau de la charge, publié **seulement quand la comparaison est possible** —
|
||
|
||
```jsonc
|
||
"command": { "expectedW": 1380, "observedW": 4400, "divergent": true, "since": "…" }
|
||
```
|
||
|
||
**`measurement.source` conditionne ce que vaut cette comparaison**, et doit donc la gouverner :
|
||
contre `none`, il n'y a rien à comparer et l'objet est **absent** — pas `divergent: false`, qui
|
||
affirmerait la concordance qu'on ne peut pas constater. Même règle que partout ailleurs ici.
|
||
|
||
**4.3 — La COMPTABILITÉ : le diagnostic était à corriger, et le vrai défaut est plus grave.**
|
||
|
||
> **MESURÉ le 2026-09-01, avant d'implanter.** Le constat disait « un budget de surplus qui
|
||
> ignore ces watts ». **Ce n'est pas ce qui se passe.** Quand une charge consomme et **obéit**,
|
||
> la comptabilité est juste : sa conso est recréditée puis réallouée à elle-même, et les
|
||
> suivantes ne reçoivent que le surplus réel (mesuré : export 500 W, A à 4 400 W obéissante → B
|
||
> reçoit 500 W). **Corriger cela aurait été corriger un non-défaut.**
|
||
|
||
**Le vrai défaut : le recrédit est un PRÊT consenti contre l'obéissance.** Son hypothèse est
|
||
écrite dans le code — *« le budget est une mesure dont la conso de la charge a déjà été
|
||
retranchée »* — c'est-à-dire **« je peux rendre ces watts parce que je peux les reprendre »**.
|
||
Rien ne vérifie que l'emprunteur rembourse.
|
||
|
||
Mesuré, le cas où la promesse est engagée : export 500 W, A consomme 4 400 W, plancher de A à
|
||
5 000 W. A est commandée à **0**, et ses 4 400 W sont **redistribués à B**.
|
||
|
||
| | si A obéit | si A n'obéit pas |
|
||
|---|---|---|
|
||
| A | tombe à 0 | reste à 4 400 W |
|
||
| B | 4 000 W réels | 4 000 W **fictifs** |
|
||
| maison | 4 000 W pour 4 900 de production | **8 400 W pour 4 900** → 3 500 W importés |
|
||
|
||
**Et l'identité de réconciliation TIENT dans les deux cas** — `surplus + recrédit − alloué ==
|
||
restant`. C'est ce qui distingue un prêt d'une erreur : la comptabilité est juste, c'est
|
||
l'**hypothèse sur laquelle elle repose** qui n'est pas vérifiée. Aucun contrôle de cohérence ne
|
||
pouvait donc l'attraper.
|
||
|
||
**Ce qui change maintenant : l'obéissance est CONSTATABLE.** `command.divergent` (étape 2) dit
|
||
si la charge a suivi au cycle précédent. Le recrédit peut donc être **conditionné** — c'est la
|
||
proposition, et elle demande une décision parce qu'elle a son propre risque : l'anti-clignotement
|
||
existe pour une raison, et le suspendre trop vite ferait osciller une charge saine sur un écart
|
||
transitoire. `since` est précisément ce qui permet de distinguer les deux.
|
||
|
||
**Ce que le §4.3 d'origine proposait — retrancher la conso subie du budget — est SANS OBJET** :
|
||
elle en est déjà retranchée, par le compteur. La note est conservée ici pour que personne ne la
|
||
réimplante en la croyant oubliée.
|
||
|
||
**4.3-bis — l'ancienne formulation, conservée pour mémoire.**
|
||
`draw.committedW` à 0 pendant que 4,4 kW sont tirés n'est pas un défaut d'affichage : le budget
|
||
de surplus est calculé sans ces watts, donc **le moteur alloue de l'énergie qui n'existe pas**.
|
||
Une charge que l'arbitre ne commande plus mais qui consomme est de la **consommation fixe**, au
|
||
même titre que la base de la maison — c'est exactement le raisonnement d'`ECS-410` pour une
|
||
charge gelée, appliqué à une cause nouvelle.
|
||
|
||
**Recommandation : la retrancher du budget, sans l'allouer.** Elle n'est pas servie, elle est
|
||
subie. La compter comme allouée serait prétendre l'avoir décidée.
|
||
|
||
> **Sur 18 kVA c'est invisible ; sur 6 kVA c'est le disjoncteur.** Et le délestage ne rattrape
|
||
> pas ce cas : il agit sur la marge MESURÉE, qui inclut bien ces 4,4 kW — donc il protège. Ce
|
||
> qui reste faux est le **budget**, qui promet un surplus déjà consommé.
|
||
|
||
**4.4 — Faut-il un état « commande en vol perdue » distinct d'un défaut ?**
|
||
`ECS-410`/`ECS-414` couvrent l'écriture qui échoue trois fois → défaut. Ici l'écriture a échoué
|
||
**pendant une indisponibilité**, et la charge est ensuite redevenue disponible : le défaut se
|
||
lève, la divergence reste. À trancher : est-ce un `faultCode` de plus, ou un état orthogonal ?
|
||
|
||
## 5. ORDRE DE TRAVAIL — deux étapes à comportement constant d'abord
|
||
|
||
**Étape 1 — la MÉMOIRE de la commande, sans rien en faire.** `EvAdapter` retient sa dernière
|
||
consigne appliquée, comme les autres adaptateurs le font déjà. Publication seule, comportement
|
||
constant. Sans cette pièce, aucune des suivantes n'est calculable.
|
||
|
||
**Étape 2 — l'ÉCART publié, sans décider dessus.** L'objet `command`, gouverné par
|
||
`measurement.source`. C'est la moitié qui rend le défaut **visible**, et elle ne change aucune
|
||
décision — même stratégie qu'à l'étape C du §10, où publier le régime a précédé de décider avec.
|
||
|
||
**Étape 3 — la COMPTABILITÉ corrigée.** La conso subie sort du budget. C'est ici que le
|
||
comportement change, et c'est la seule étape qui le fasse.
|
||
|
||
**Étape 4 — la reprise en main**, si elle est jugée nécessaire après avoir vu les traces des
|
||
étapes 2 et 3. **Délibérément en dernier** : c'est la seule qui commande du matériel, et on
|
||
saura alors combien de fois le cas se produit réellement.
|