fix(3g-1): la bascule de phases se décide sur le courant POSSIBLE

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) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017TVywtayEeoRJ2yWUW4iZj
This commit is contained in:
Patrick Schurig 2026-08-27 10:21:08 +02:00
parent 99ad9a04cb
commit 4f987e5b1d
3 changed files with 39 additions and 5 deletions

View File

@ -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;

View File

@ -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)

View File

@ -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É.
*