From 4f987e5b1ddb8bfee7310f688cc106480c2652ce Mon Sep 17 00:00:00 2001 From: Patrick Schurig Date: Thu, 27 Aug 2026 10:21:08 +0200 Subject: [PATCH] =?UTF-8?q?fix(3g-1):=20la=20bascule=20de=20phases=20se=20?= =?UTF-8?q?d=C3=A9cide=20sur=20le=20courant=20POSSIBLE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Deuxième défaut latent de 3g-1, corrigé à part du lot 3g-2 pour la même raison que le précédent. EvAdapter passait `currentPower / 230` comme courant de référence à la règle de bascule 1↔3 phases, au motif — écrit dans le commentaire — que l'allocation n'est pas encore connue à cet instant. Mais la bascule ne se décide pas sur ce que la borne TIRE : elle se décide sur ce qu'elle POURRAIT tirer. C'est la formule du proxy, `currentPower/230 + allowance`, et c'est la seule qui puisse répondre « redescends en monophasé, il n'y a plus que 800 W ». Avec le seul courant courant, une borne passée en triphasé y restait quel que soit l'effondrement du surplus — et son plancher publié restait à 4,1 kW, donc plus rien ne pouvait la servir. Le surplus, lui, EST connu avant l'allocation : c'est la mesure du compteur. Le terme de stockage de planSurplusCharging() (tampon de 500 W, réinjection au-dessus du seuil) n'entre volontairement pas dans cette référence : il la déplace à la marge et n'a d'effet que sur le choix 1↔3, jamais sur l'allocation — laquelle est faite par le waterfall, avec un budget qui tient compte du stockage. Suite simulation `run` 16/16, charging vert. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_017TVywtayEeoRJ2yWUW4iZj --- energyplugin/etm/adapters/evadapter.cpp | 11 ++++++----- energyplugin/etm/energyarbitrator.cpp | 10 ++++++++++ energyplugin/etm/energyarbitrator.h | 23 +++++++++++++++++++++++ 3 files changed, 39 insertions(+), 5 deletions(-) diff --git a/energyplugin/etm/adapters/evadapter.cpp b/energyplugin/etm/adapters/evadapter.cpp index 096baa7..0febc8a 100644 --- a/energyplugin/etm/adapters/evadapter.cpp +++ b/energyplugin/etm/adapters/evadapter.cpp @@ -61,14 +61,15 @@ LoadContext EvAdapter::toLoadContext(const QDateTime &now) const // vers un autre produirait un défaut silencieux — le waterfall allouerait 2 kW en croyant // démarrer une borne qui en exige 4,1, et se croirait servi. // - // Le courant de référence pour ce choix est ce que la borne tire DÉJÀ : c'est la seule - // grandeur connue avant l'allocation, et c'est celle que le proxy utilisait aussi. - // Plus de test canSetPhaseCount() ici : la question « cette borne peut-elle basculer, + // 3g-2 — la référence est ce que la borne POURRAIT tirer : son courant actuel PLUS le + // surplus disponible. C'est la formule du proxy, et c'est la seule qui sache redescendre + // en monophasé quand le surplus s'effondre (voir evReferenceCurrentA()). + // 3g-2 — plus de test canSetPhaseCount() ici : la question « cette borne peut-elle basculer, // avec CETTE voiture » est tranchée une fois par le manager, et evTargetPhases() la lit. // Le prédicat local n'en voyait qu'une moitié (l'état existe-t-il) et retombait sur le // nombre de phases COURANT, sans la voiture. - const double courantActuelA = m_charger->currentPower() / 230.0; - m_targetPhases = m_parent->evTargetPhases(m_charger, courantActuelA); + const double courantReferenceA = m_parent->evReferenceCurrentA(m_charger); + m_targetPhases = m_parent->evTargetPhases(m_charger, courantReferenceA); if (m_targetPhases == 0) m_targetPhases = 1; diff --git a/energyplugin/etm/energyarbitrator.cpp b/energyplugin/etm/energyarbitrator.cpp index b075139..8ca2b0e 100644 --- a/energyplugin/etm/energyarbitrator.cpp +++ b/energyplugin/etm/energyarbitrator.cpp @@ -435,6 +435,16 @@ double EnergyArbitrator::evFloorW(EvCharger *ev, uint phaseCount) const return minA * acquisitionTolerance() * 230.0 * phaseCount; } +double EnergyArbitrator::evReferenceCurrentA(EvCharger *ev) const +{ + if (!ev) + return 0; + double a = ev->currentPower() / 230.0; + if (RootMeter *meter = internalRootMeter()) + a += -meter->currentPower() / 230.0; // < 0 au compteur = export = surplus + return a; +} + uint EnergyArbitrator::evTargetPhases(EvCharger *ev, double referenceA) { if (!ev) diff --git a/energyplugin/etm/energyarbitrator.h b/energyplugin/etm/energyarbitrator.h index 78e7c06..f24e004 100644 --- a/energyplugin/etm/energyarbitrator.h +++ b/energyplugin/etm/energyarbitrator.h @@ -233,6 +233,29 @@ public: */ uint evTargetPhases(EvCharger *ev, double referenceA); + /*! + * \brief [3g-2] Courant de référence (A) pour choisir le nombre de phases d'une borne. + * + * \param ev Borne. \return Somme du courant qu'elle tire DÉJÀ et du surplus disponible, + * en ampères totaux (toutes phases confondues). Négatif possible : en soutirage, la + * référence descend, et la règle de bascule vise le monophasé. + * + * \par Pourquoi la référence n'est pas le seul courant courant + * 3g-1 passait \c currentPower / 230, au motif que l'allocation n'est pas encore connue. + * Mais la bascule 1↔3 phases ne se décide pas sur ce que la borne tire : elle se décide + * sur ce qu'elle POURRAIT tirer — c'est la formule du proxy (`currentPower/230 + + * allowance`), et c'est la seule qui puisse répondre « redescends en monophasé, il n'y a + * plus que 800 W ». Avec le seul courant courant, une borne en triphasé restait en + * triphasé quel que soit l'effondrement du surplus, et son plancher publié restait à + * 4,1 kW — donc plus rien ne pouvait la servir. + * + * \note Le terme de stockage de \c planSurplusCharging() (tampon 500 W, réinjection au- + * dessus du seuil) n'entre PAS ici : il déplace la référence à la marge et n'a d'effet + * que sur le choix 1↔3, jamais sur l'allocation, qui est faite par le waterfall avec un + * budget qui, lui, tient compte du stockage. + */ + double evReferenceCurrentA(EvCharger *ev) const; + /*! * \brief [3g-1] Plancher de modulation d'une borne, en watts, pour le nombre de phases VISÉ. *