2 Commits

Author SHA1 Message Date
Patrick Schurig
93b257172c docs(relevé): 3g-2 sur machine — le doublet a disparu, et le banc a trouvé un trou
`1.15.2+etm23` construit arm64, posé sur `.75` par deploy.sh, exercé.

FRANCHI, journal et RPC à l'appui :

  - Une charge, un commandeur. 9 lignes « Executing action Simulated wallbox »
    sur la fenêtre, 9 AVANT « Adjusting chargers » (dispatch de l'arbitre), 0
    après (proxy). 6 cycles sur 6 portent « is commanded by the ETM waterfall
    this cycle. Not re-deciding here. » Le doublet du relevé 3g-1 §3.3 a
    disparu, et quelqu'un commande — l'arbitre a bien allumé la borne à 10 A
    sur sa propre décision.
  - Le plancher publié est l'exigence réelle : 2 070 W sous +etm22, 4 140 W
    sous +etm23, MÊME borne, MÊME journée. Et il suit le nombre de phases visé
    d'un cycle à l'autre (4 140 en triphasé, 1 380 en monophasé).
  - L'entrée evcharger est créée d'office, journalisée une fois par borne,
    sans charge utile ; l'aller-retour GetLoadConfig → SetLoadConfig verbatim
    est neutre et ne reconstruit aucun adaptateur ; loads[] ⊆ GetLoadConfig
    est rétabli.

TROUVÉ, et c'est le point du passage machine :

  - GRAVE — une borne SANS VÉHICULE BRANCHÉ absorbe le budget de la cascade.
    4 853 W alloués à une wallbox `pluggedIn: false` qui tire 0 W, la PAC
    derrière ne survivant que sur son propre recrédit. Trois cycles
    consécutifs, 2 350 / 4 853 / 5 464 W donnés à personne. C'est le défaut du
    relevé 3g-1 §3.4 par une autre porte : là le plancher mentait, ici c'est le
    filtre d'entrée. evParticipatesInWaterfall() reprend le filtre du proxy —
    voiture assignée, mode non manuel — mais pas pluggedIn. Le proxy n'en avait
    pas besoin : son allocation n'était pas une cascade. Défaut de 3g-1,
    correction PROPOSÉE ET NON APPLIQUÉE, à traiter dans son propre commit.
  - Mineur — le rang par défaut des bornes auto-provisionnées retombe à
    ÉGALITÉ avec le rang 1 déjà occupé, au lieu de « en tête ». Le tri reste
    total et reproductible, mais ce n'est pas ce qui était demandé.

NON OBSERVÉ, et pas fabriqué : EV_GRID_START (le budget est passé de 385 W à
2 350 W sans s'arrêter dans la fenêtre de tolérance), BATTERY_RESERVE avec un
surplus réel, LM-1204-b matériel (la Terra AC ne répond pas à l'ARP depuis le
banc lui-même), et le cas de transition — aucune borne n'était sous commande au
moment de la bascule.

Banc rendu à son état d'origine : ChargingInfos identiques champ par champ,
voiture de test retirée, wallbox éteinte. Les entrées evcharger restent — elles
seraient recréées au prochain démarrage.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017TVywtayEeoRJ2yWUW4iZj
2026-08-27 12:44:19 +02:00
Patrick Schurig
baf267a963 feat(3g-2): une charge, un commandeur
Le relevé machine du 2026-08-26 l'énonçait sans détour : « tant que ceci n'est
pas traité, la transplantation ne commande rien ». L'arbitre décidait,
publiait un motif juste, et adjustEvChargers() commandait autre chose une
milliseconde plus tard. Ce lot ferme les trois points que le banc a ouverts.

1. UNE CHARGE, UN COMMANDEUR

Les actions EV du waterfall sont enfin DISPATCHÉES — elles ne l'étaient pas du
tout : m_loadAdapters ne contient pas les bornes, et applyActionsToAdapters()
s'arrêtait là. Et les bornes ainsi commandées sortent du chemin proxy par deux
crochets virtuels posés dans l'amont, interrogés à deux moments du cycle où la
réponse n'est pas la même : evSurplusPlannedByWaterfall() avant le plan (à qui
la borne appartient-elle), evCommandedByWaterfall() après (qu'a-t-il commandé).
Sans cette distinction, une borne servie par l'échéance ne serait plus
commandée du tout.

Le jeu des bornes commandées est NOMMÉ par le scheduler (Plan::waterfallEvIds).
Le redéduire ailleurs — « motif différent d'EV_DEADLINE », « financement au
surplus » — dériverait du vrai au premier motif ajouté, et EV_GRID_START porte
justement funding=Grid tout en venant du waterfall. Il est vidé à l'entrée du
mode dégradé : le waterfall ne commandant plus, il ne peut plus revendiquer.

chargingState, que posait adjustEvChargers(), est repris par l'arbitre pour
les bornes qui n'y passent plus — sinon l'app afficherait « en charge » une
borne que l'arbitre vient d'éteindre.

2. evFloorW() REND L'EXIGENCE RÉELLE, ET LA TOLÉRANCE DEVIENT VISIBLE

Le plancher valait `min × acquisitionTolerance × 230 × phases` : 2 070 W
annoncés pour une borne qui en exige 4 140. Le waterfall allouait 2 828 W,
l'adaptateur refusait d'enclencher sous 6 A, et 2 828 W étaient retirés du
budget pour personne. Ce sont deux grandeurs différentes : le plancher est ce
que la borne EXIGE, la tolérance est la part qu'on accepte de ne pas couvrir.

La tolérance n'est pas supprimée pour autant — un réglage documenté (défaut
0,5) qui devient inerte sans le dire est un silence ambigu, et tout le parc
verrait son VE démarrer deux fois plus tard sans explication. Elle devient un
seuil de démarrage EXPLICITE : motif EV_GRID_START {budgetW, floorW, gridW},
funding=Grid, et une comptabilité qui se partage — budgetW dans
budget.allocatedW, gridW dans budget.evReservedW, budgetW + gridW ==
allocatedW.

C'est une brèche assumée dans le financement réseau que DESIGN_3g §3.1 déférait
à 3f. Elle est bornée par construction, pas par discipline : surplus réel exigé
(budgetW > 0), tout le budget restant consommé donc AU PLUS UNE borne par
cycle, et le plafond de la protection de surcharge s'applique avant. Écrite en
toutes lettres dans DESIGN_3g §3.1-bis.

3. LE RANG D'UNE BORNE EST CONFIGURABLE

Il vit là où vivent tous les rangs : LoadConfig.priority (LM-1206/LM-1207). Pas
de second système de priorité — un classement propre aux bornes ne saurait pas
exprimer « VE1 > ECS > VE2 », et deux classements qui ne peuvent pas
s'interclasser sont un défaut, pas deux fonctionnalités. ChargingInfo ne porte
donc aucun champ de rang.

Toute borne détectée reçoit d'office une entrée « evcharger », portant le rang
et RIEN d'autre : ses limites viennent du Thing et changent avec la voiture
branchée. Création journalisée, une ligne par borne, à la création seulement —
une écriture que personne n'a demandée doit laisser une trace. Le tri devient
TOTAL : à rang égal, l'identifiant départage, donc laquelle démarre est
reproductible même avant tout réglage.

L'entrée SURVIT à la disparition de son Thing : borne remplacée, ThingId
changé, appareil déposé. Elle reste publiée available: false / faultCode:
THING_MISSING / motif LOAD_UNAVAILABLE, et ne retient aucun budget. La
supprimer d'office rendrait « borne remplacée » indistinguable de « borne
jamais configurée ».

CORRIGE UNE RUPTURE D'INVARIANT DE 3g-1, non signalée à l'époque : en faisant
entrer les bornes dans loads[], 3g-1 publiait des charges qui ne figuraient
dans AUCUNE GetLoadConfig — la charge fantôme que le lot B-bis avait
supprimée, réapparue par l'autre bout. L'app s'appuie sur cet invariant pour
résoudre libellé, domaine et rang. L'auto-provisionnement le rétablit par
construction.

ÉPROUVÉ

  - testEvChargerRankDecidesWhoCharges : deux bornes, un budget qui ne paie
    qu'un plancher, la seconde refusée avec BELOW_MIN_POWER {budgetW: 620,
    minPowerW: 1380}, et l'inversion du rang change la borne servie. C'est la
    moitié ARBITRAGE de LM-1204-b. Couvre aussi l'aller-retour neutre par la
    frontière RPC et l'inclusion loads[] ⊆ GetLoadConfig.
  - testEvGridStartIsAnnouncedAndBounded : le motif chiffre le soutirage, la
    comptabilité se réconcilie, une seule borne soutire par cycle.
  - testEvChargerConfigOutlivesItsThing : l'entrée survit, publiée en défaut.
  - testEvChargerConfigIsRankOnly / RefusesPayloads / ClaimsItsThing.

Deux points de contrôle de `run` bougent, dans un seul scénario et par une
cause unique : la conversion watts→ampères se fait désormais après
l'allocation, et l'arrondi tombe parfois de l'autre côté. 14 des 16 scénarios
sont identiques au bit près, et ce qui compte est inchangé dans les deux —
la voiture est à 100 % à l'échéance.

Suite complète : simulation 27/27, charging 16/16, loadmodel 16/16,
spotmarket 7/7. Build amd64 0/0.

RIEN N'A TOURNÉ SUR MACHINE. docs/RECAP_3g2.md dit ce qui reste à éprouver au
banc — dont LM-1204-b avec deux bornes réelles, la Terra AC étant vérifiée
absente du réseau (ARP INCOMPLETE depuis le banc lui-même).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017TVywtayEeoRJ2yWUW4iZj
2026-08-27 10:43:25 +02:00