# Relevé machine — 3g-2 sur le banc `.75`, 2026-08-27 **Paquets** : `1.15.2+etm23` puis `1.15.2+etm24` (correctif du §3.1), construits arm64 dans `build-cross-arm64`, posés par `etm-powersync-deploy/deploy.sh` (rollback : `/var/lib/etm-deploy/archive/`). **Compteur** : Fronius SunSpec. **Bornes** : Terra AC réelle (absente du réseau) + wallbox simulée. > **Méthode.** Ce qui suit distingue ce qui a été **observé sur machine**, ce qui vient d'un > **test unitaire**, et ce qui n'est qu'une **lecture de code** (`docs/RELEVE_LOTD.md`). > Tout ce qui est numéroté ci-dessous est **machine**, journal ou RPC à l'appui. --- ## 1. La Terra AC — toujours absente, et c'est physique | Vérification | Résultat | |---|---| | `ping` depuis le banc `.75`, même segment L2 | `Destination Host Unreachable` | | Table ARP du banc pour `.132` | **`INCOMPLETE`** — aucune réponse ARP | | TCP 502 depuis le banc | `No route to host` | | Balayage complet de `192.168.1.0/24` depuis le banc | rien en `.132` ; aucun autre Modbus TCP inconnu | | `Integrations.GetThings` | `Terra AC Charger (TCP)` en **`ThingSetupStatusInProgress`**, en boucle | Une machine qui ne répond pas à l'ARP est hors du réseau. **LM-1204-b garde sa moitié matérielle non franchie** — il faut deux bornes RÉELLES, et il n'y en a qu'une. --- ## 2. Ce qui est FRANCHI ### 2.1 Une charge, un commandeur — le critère central du lot **Avant** (`+etm22`, même banc, wallbox en Eco avec voiture assignée) : la borne était arbitrée, et `adjustEvChargers()` reprenait la main derrière. **Après** (`+etm23`), chaque cycle porte, dans cet ordre : ``` [Arbitre] "{09e520fd…}" → "Surplus PV 2350 W — Simulated wallbox consigne 2350 W" | activé: true | phases: 1 Executing action Simulated wallbox to power: ON, Carging current: 10A, Switch phases to: 1, Issuer: Surplus ---------------- Adjusting chargers --------------- "Simulated wallbox" is commanded by the ETM waterfall this cycle. Not re-deciding here. ``` Et dans `planSurplusCharging`, un cycle plus haut : `"Simulated wallbox" surplus is planned by the ETM waterfall. Skipping proxy planning.` **Comptage sur la fenêtre d'observation** : 9 lignes `Executing action Simulated wallbox`, **9 avant** `Adjusting chargers` (donc émises par le dispatch de l'arbitre), **0 après** (donc aucune dérivée par le proxy). 6 cycles sur 6 portent la ligne de saut. **Le doublet a disparu, et quelqu'un commande.** C'est le critère du lot, et il est franchi. ### 2.2 Le plancher publié est l'exigence réelle Sur la MÊME borne, dans la même journée : | | motif publié | |---|---| | `+etm22` | `BELOW_MIN_POWER {budgetW: 84, minPowerW: **2070**}` | | `+etm23` | `BELOW_MIN_POWER {budgetW: 395, minPowerW: **4140**}` | 2 070 W était `6 A × 0,5 × 230 × 3` — le plancher multiplié par la tolérance d'acquisition. 4 140 W est `6 A × 230 × 3`, ce que la borne exige réellement. Le défaut du relevé 3g-1 §3.4 est fermé, et le chiffre le montre. **Et le plancher suit le nombre de phases VISÉ**, cycle à cycle : au cycle suivant, la référence de bascule ayant changé, le journal publie `BELOW_MIN_POWER {budgetW: 385, minPowerW: **1380**}` — soit `6 A × 230 × 1`. C'est le piège le plus silencieux de `DESIGN_3g` §3.3, observé en train de fonctionner. ### 2.3 L'entrée `evcharger` auto-provisionnée Au premier cycle après la bascule, une ligne par borne, **à la création seulement** : ``` [EnergyArbitrator] Borne "Simulated wallbox" ajoutée à la configuration des charges (rang 1 , domaine ev) — entrée créée automatiquement, modifiable par SetLoadConfig. [EnergyArbitrator] Borne "Terra AC Charger (TCP)" ajoutée à la configuration des charges (…) ``` `GetLoadConfig` passe de 2 à 4 entrées. Les deux entrées de borne portent `adapter: evcharger`, `mode: dynamic`, `domain: ev`, et **aucune charge utile** — vérifié champ par champ (`relays`, `sgReady`, `powerLevels`, `maxPowerW`, `minPowerW`, `minOnS`, `minOffS` tous vides ou nuls). **Aller-retour NEUTRE, par la frontière RPC** : `GetLoadConfig` → `SetLoadConfig` verbatim → `GetLoadConfig` rend un objet **identique**, et le journal du cycle suivant dit `0 créée(s), 0 mise(s) à jour en place, 2 inchangée(s), 0 retirée(s)` — **aucun adaptateur reconstruit, aucun verrou réarmé.** **`loads[] ⊆ GetLoadConfig` est rétabli** : les trois charges publiées ont leur entrée. La Terra AC est dans `GetLoadConfig` sans être dans `loads[]` — c'est le sens autorisé de l'inclusion : sans voiture assignée, elle est **hors arbitrage**, et le journal le dit (`[Arbitre] Borne "Terra AC Charger (TCP)" hors waterfall`). ### 2.4 La bascule elle-même `nymead` arrêté 10:03:24, démarré 10:03:35, premier cycle d'arbitrage 10:04:00 — **36 s sans arbitrage**. L'ECS a repris son palier par relecture des contacts (`reprise au démarrage : palier 0 W [correspondance exacte]`, ECS-411). ⚠️ **Le cas de transition que je voulais éprouver ne l'a PAS été** : au moment de la bascule, **aucune borne n'était sous commande** (surplus négatif, wallbox à l'arrêt). « Une borne commandée par un chemin qui n'existe plus » reste donc non observé. Ce qui EST observé et qui en couvre la moitié : l'arbitre émet activement des `power: OFF` (4 fois sur la fenêtre), donc le mécanisme qui rattraperait une borne laissée allumée est en place et tourne. --- ## 3. CE QUE LA MACHINE A TROUVÉ, ET QUI N'EST PAS DANS LE LOT ### 3.1 GRAVE — une borne SANS VÉHICULE BRANCHÉ absorbe le budget de la cascade Trois cycles consécutifs, wallbox en Eco avec voiture assignée mais **`pluggedIn: false`** : ``` [Arbitre] "chauffe-eau" → "Surplus PV 8353 W — chauffe-eau consigne 3500 W (palier)" [Arbitre] "{09e520fd…}" → "Surplus PV 4853 W — Simulated wallbox consigne 4853 W" ← 4 853 W [Arbitre] "pac-terrain" → "Surplus PV 1500 W — PAC banc recommandée (état 3, ~1500 W)" ``` Et dans la même charge utile de télémétrie, pour cette borne : ``` allocatedW: 4853 mécanisme: { chargingEnabled: false, pluggedIn: false, currentA: 7, phaseCount: 1 } ``` La borne est commandée `power: ON`, elle ne tire **rien** (`currentPower: 0`), et **4 853 W ont été retirés du budget et donnés à personne**. La PAC derrière elle n'a survécu que sur son propre recrédit. Les deux cycles voisins donnent 2 350 W puis 5 464 W au même vide. **C'est le défaut du relevé 3g-1 §3.4 qui revient par une autre porte** : là c'était le plancher qui mentait, ici c'est le filtre d'entrée. **Cause** : `EnergyArbitrator::evParticipatesInWaterfall()` reprend le filtre d'entrée 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, gaspiller ne privait personne. En cascade, si. L'asymétrie est visible dans le code même : la boucle EV du scheduler, elle, teste bien `if (!ev->available() || !ev->pluggedIn()) continue;` — seul le chemin waterfall ne le fait pas. **Défaut de 3g-1**, révélé par 3g-2 parce que la décision atteint enfin le matériel. **CORRIGÉ** (commit dédié, après ce relevé) : `pluggedIn` rejoint le filtre d'entrée. Une borne sans véhicule est **hors arbitrage**, comme une borne sans voiture assignée ou en mode manuel (`DESIGN_3g` §4) — absente du contexte, pas servie à 0 : sa consommation est nulle et déjà au compteur. Le journal l'annonce comme les deux autres cas. Éprouvé par `testUnpluggedChargerTakesNoBudget`, **vérifié échouant sans le correctif** : la borne débranchée disparaît du contexte, la charge suivante reçoit les 3 000 W entiers, et la seconde moitié du test rebranche le véhicule pour prouver que c'est bien `pluggedIn` qui décide et non une exclusion fortuite. **VÉRIFIÉ SUR MACHINE avec `1.15.2+etm24`**, en rejouant exactement la condition — wallbox en Eco, voiture assignée, `pluggedIn: false` : ``` [Arbitre] Borne "Simulated wallbox" hors waterfall (pas de voiture assignée, mode manuel, ou aucun véhicule branché). ``` `loads[]` ne contient plus que `chauffe-eau` et `pac-terrain`, et le budget leur va entier. Sous `+etm23`, la même condition donnait 4 853 W à la borne. > **Conséquence à connaître : « hors arbitrage » ne veut pas dire « non commandée ».** Sortie du > waterfall, la borne retombe sur le PROXY, qui la planifie et la commande — > `### Surplus would adjust evcharger "Simulated wallbox" to total 12,35 A (2 841 W)` puis > `Executing action … to power: ON, Carging current: 6A, Issuer: Surplus`. C'est le repli prévu > (tout ce que le waterfall ne prend pas reste au proxy) et **l'invariant du lot tient : un seul > commandeur**. Aucun watt n'est retiré de la cascade, et la borne ne tire rien puisque rien > n'est branché — c'est le comportement de l'amont, inchangé. > > À noter tout de même l'asymétrie avec les deux autres exclusions : « mode manuel » fait sauter > la borne dans `adjustEvChargers()`, « pas de voiture » la fait finir à `power OFF`, tandis que > « débranchée » la fait commander ON au courant minimum. Sans effet tant que rien n'est > branché ; à revoir le jour où le §10 fera du proxy autre chose qu'un repli. ### 3.2 Mineur — le rang par défaut retombe à ÉGALITÉ, pas « en tête » Les deux bornes ont reçu **rang 1**, à égalité avec `chauffe-eau` qui l'occupait déjà. La règle implémentée est « le plus petit rang existant moins un, borné à 1 ». Quand une charge occupe déjà le rang 1, « moins un » vaut 0, le plancher le ramène à 1, et l'intention — mettre la borne **en tête sans bousculer les charges déjà classées** — dégénère en égalité. Sans conséquence sur la reproductibilité : le tri est **total**, l'identifiant départage, et l'ordre est stable d'un cycle à l'autre. Mais ce n'est pas ce qui était demandé. Trois issues possibles, aucune tranchée : autoriser le rang 0, renuméroter les autres (exclu par « sans bousculer »), ou assumer l'égalité et laisser l'app poser les vrais rangs à la mise en service. ### 3.3 Pour mémoire — deux écritures de configuration dans le même cycle Deux bornes détectées ⇒ deux `setConfigs()` ⇒ deux `changed()` ⇒ deux `rebuildLoadAdapters()` dans le cycle de démarrage. Les deux sont incrémentaux (`2 inchangée(s)`), donc **aucun adaptateur n'est détruit** et ECS-412 tient. Sans conséquence, mais à savoir si le nombre de bornes grandit. --- ## 4. CE QUI N'EST PAS PROUVÉ - **`EV_GRID_START` : NON observé.** Guetté six cycles durant, comme demandé — jamais fabriqué. Le budget de la borne est passé de 385 W à 2 350 W sans s'arrêter dans la fenêtre `[plancher × 0,5 ; plancher[` (690–1 380 W en monophasé). Le motif reste donc éprouvé par le seul test de simulation. **À guetter, comme `BELOW_MIN_POWER` l'a été.** - **`BATTERY_RESERVE` avec un surplus réel sous le seuil** : toujours pas. Le SoC est à 50 %, le seuil à 0,4 — la réserve n'est pas active. - **LM-1204-b, moitié matérielle** : deux bornes réelles (§1). - **Le cas de transition** « borne commandée par un chemin qui n'existe plus » (§2.4). - **La charge réelle d'un véhicule** : aucune voiture n'a été branchée — la wallbox simulée n'expose aucune action `pluggedIn`, seulement `connect`/`disconnect` (interface `connectable`, état `connected`). C'est d'ailleurs ce qui a rendu §3.1 visible. --- ## 5. État où le banc a été rendu | | valeur | pourquoi | |---|---|---| | Paquet | **`1.15.2+etm24`** | posé par `deploy.sh` ; `+etm22` et `+etm23` archivés | | `ChargingInfos` | **identiques à l'origine** — les deux bornes en `ChargingModeNormal`, sans voiture, `targetPercentage: 0` | comparées champ par champ au relevé d'avant | | Voiture de test | **retirée** (`3G2-CAR-A-SUPPRIMER`) | | | Wallbox simulée | **éteinte** (`power: false`) | l'arbitre ne la commande plus, elle est hors arbitrage | | `LoadConfig` | **4 entrées** : chauffe-eau, PAC banc, + les 2 bornes auto-provisionnées | les entrées de borne RESTENT — elles seraient recréées au prochain démarrage | | `batteryLevelConsideration` | 0,4 · `acquisitionTolerance` 0,5 · `phasePowerLimit` 0 | inchangés | Dernier cycle rendu : surplus 5 634 W, `chauffe-eau` 3 500 W, `pac-terrain` 3 000 W, aucune borne dans `loads[]`.