etm-powersync-energy-plugin.../docs/DESIGN_COMMANDE_PERDUE.md
Patrick Schurig 4d73ff2482 docs: §10 déployé sur .75, et le design de la commande perdue
Déploiement vérifié SUR MACHINE et non déduit du script : version relue,
nymead actif, arbitre initialisé, et les quatre motifs du §10 présents dans
le binaire posé.

Piège de vérification noté au passage : authorisedW, perPhaseBound et
binding sont des QStringLiteral, donc stockés en UTF-16 et invisibles à
`strings` par défaut. Un premier relevé les donnait absents — j'ai failli
les rapporter manquants. `strings -el` les trouve. Vérifier une chaîne dans
un binaire demande de savoir comment elle y est encodée.

Et le design du premier constat de l'app, ouvert plutôt qu'attaqué au code.
Trois choses qu'il établit, toutes mesurées :

Le titre est trop étroit. « 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
qu'une écriture du proxy a échoué pendant l'indisponibilité, que personne ne
l'a rejouée, et que l'organe est resté sur une consigne dont plus aucun
commandeur ne se réclame.

La pièce qui manque est là où le constat porte : l'EvAdapter ne garde PAS sa
dernière consigne, là où les adaptateurs en watts la gardent. Ils en ont
besoin pour l'écrêtage de verrou ; la borne était pilotée par le proxy, qui
n'a jamais eu à se relire.

Le vrai coût n'est pas l'affichage. draw.committedW à 0 pendant que 4,4 kW
sont tirés veut dire que le budget de surplus est calculé sans ces watts :
le moteur alloue de l'énergie qui n'existe pas. Le délestage, lui, protège —
il agit sur la marge MESURÉE, qui les inclut. C'est le budget qui ment.

Quatre décisions posées, dont la principale : signaler toujours, réappliquer
jamais telle quelle. Rejouer une consigne vieille de plusieurs minutes, c'est
décider avec des données mortes — ce que le mode dégradé L2 refuse déjà de
faire. 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.

Ordre de travail : mémoire de la commande, puis écart publié sans décider
dessus, puis comptabilité corrigée, et la reprise en main EN DERNIER — c'est
la seule étape qui commande du matériel, et on saura alors combien de fois
le cas se produit vraiment.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015F7G5VeaPVSMeVNjiGj36p
2026-09-01 07:12:05 +02:00

6.3 KiB

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 —

"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 — Et la COMPTABILITÉ, qui est le vrai coût. 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.