diff --git a/energyplugin/etm/adapters/evadapter.cpp b/energyplugin/etm/adapters/evadapter.cpp index 78e89c1..096baa7 100644 --- a/energyplugin/etm/adapters/evadapter.cpp +++ b/energyplugin/etm/adapters/evadapter.cpp @@ -63,10 +63,12 @@ LoadContext EvAdapter::toLoadContext(const QDateTime &now) const // // 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, + // 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_charger->canSetPhaseCount() - ? m_parent->evTargetPhases(m_charger, courantActuelA) - : m_charger->phaseCount(); + m_targetPhases = m_parent->evTargetPhases(m_charger, courantActuelA); if (m_targetPhases == 0) m_targetPhases = 1; @@ -123,8 +125,13 @@ LoadAction EvAdapter::applyAction(const LoadAction &action, const QDateTime &now // // Le discriminant est `currentA` : non nul, l'action vient du proxy et s'exécute telle // quelle ; nul avec une puissance, elle vient de la cascade et se convertit. + // 3g-2 — le nombre de phases VISÉ, pas le nombre courant. Le qBound bornait la cible par + // m_charger->phaseCount(), qui est l'état COURANT de la borne : une borne en monophasé ne + // pouvait donc jamais être commandée en triphasé, et le plancher publié pour 3 phases + // n'aurait jamais pu être servi. Le plafond véhicule est déjà appliqué en amont, par + // EnergyArbitrator::evTargetPhases(). const uint phases = (m_charger->canSetPhaseCount() && action.phaseCount > 0) - ? qBound(1u, action.phaseCount, m_charger->phaseCount()) + ? qBound(1u, action.phaseCount, 3u) : (m_targetPhases > 0 ? m_targetPhases : m_charger->phaseCount()); double demandeA = action.currentA; diff --git a/energyplugin/etm/energyarbitrator.cpp b/energyplugin/etm/energyarbitrator.cpp index 4d953f5..b075139 100644 --- a/energyplugin/etm/energyarbitrator.cpp +++ b/energyplugin/etm/energyarbitrator.cpp @@ -435,6 +435,25 @@ double EnergyArbitrator::evFloorW(EvCharger *ev, uint phaseCount) const return minA * acquisitionTolerance() * 230.0 * phaseCount; } +uint EnergyArbitrator::evTargetPhases(EvCharger *ev, double referenceA) +{ + if (!ev) + return 1; + + // Les bornes de ce qui est POSSIBLE viennent du manager, qui les a établies ce cycle avec + // la voiture branchée (canSwitchPhaseCount, min/maxPossiblePhaseCount). Les redériver ici + // en ferait une seconde version — et c'est exactement ce qui a divergé en 3g-1 : viser + // 3 phases sur une combinaison qui n'en permet qu'une triplait le plancher publié, et sous + // surcharge commandait 3 × 6 A là où le proxy commandait 1 × 6 A. + const ChargingProcessInfo pi = internalProcessInfo(ev); + if (!pi.canSwitchPhaseCount) + return qMax(1u, pi.maxPossiblePhaseCount); + + const uint phases = bestPhaseCountFor(ev, referenceA); + return qMax(1u, qBound(pi.minPossiblePhaseCount, phases, pi.maxPossiblePhaseCount)); +} + + SurplusContext EnergyArbitrator::buildContext(const QDateTime &now) const { SurplusContext ctx; diff --git a/energyplugin/etm/energyarbitrator.h b/energyplugin/etm/energyarbitrator.h index c03603c..78e7c06 100644 --- a/energyplugin/etm/energyarbitrator.h +++ b/energyplugin/etm/energyarbitrator.h @@ -215,12 +215,23 @@ public: /*! * \brief [3g-1] Nombre de phases visé pour une borne, à un courant donné. * - * Simple passe-plat vers la règle de bascule 1↔3 du manager. L'arbitre est la porte - * d'entrée de l'adaptateur vers cette règle : la réécrire dans l'adaptateur en ferait - * deux versions, et elles divergeraient au premier ajustement. + * Passe-plat vers la règle de bascule 1↔3 du manager, PLUS le plafond du véhicule. + * L'arbitre est la porte d'entrée de l'adaptateur vers cette règle : la réécrire dans + * l'adaptateur en ferait deux versions, et elles divergeraient au premier ajustement. + * + * \note **[3g-2] Le plafond véhicule fait partie de la règle.** \c getBestPhaseCount() + * ne le connaît pas : le proxy appliquait `qMin(carPhaseCount, …)` juste après l'appel. + * 3g-1 n'a repris que l'appel, si bien qu'une voiture monophasée pouvait se voir viser + * 3 phases — et le plancher publié triplait avec elle, sans qu'aucune allocation ne + * puisse jamais la démarrer. Le plafond est donc rendu ici, au seul endroit qui parle + * de phases pour le waterfall. + * + * \param ev Borne concernée. + * \param referenceA Courant de référence, en ampères TOTAUX toutes phases confondues — + * voir \c evReferenceCurrentA(). Peut être négatif en soutirage. + * \return Nombre de phases visé, dans [1, 3], jamais 0. */ - uint evTargetPhases(EvCharger *ev, double referenceA) - { return bestPhaseCountFor(ev, referenceA); } + uint evTargetPhases(EvCharger *ev, double referenceA); /*! * \brief [3g-1] Plancher de modulation d'une borne, en watts, pour le nombre de phases VISÉ. diff --git a/energyplugin/smartchargingmanager.h b/energyplugin/smartchargingmanager.h index 6d373ce..849b9f3 100644 --- a/energyplugin/smartchargingmanager.h +++ b/energyplugin/smartchargingmanager.h @@ -122,8 +122,8 @@ signals: // — visibilité seule (private → protected). // Sémantique amont inchangée : NON documentés // ici, la doc appartient à l'amont. -// 4. internalEvChargers(), internalChargingActions(), internalRootMeter() -// — accesseurs ETM neufs, documentés ci-dessous. +// 4. internalEvChargers(), internalChargingActions(), internalRootMeter(), +// internalProcessInfo() — accesseurs ETM neufs, documentés ci-dessous. protected slots: virtual void update(const QDateTime ¤tDateTime); // [ETM] virtual added void prepareInformation(const QDateTime ¤tDateTime); // [ETM] private → protected @@ -168,6 +168,23 @@ protected: * configuré — tout appelant doit le tester. */ RootMeter *internalRootMeter() const { return m_rootMeter; } // [ETM] new + + /*! + * \brief [ETM 3g-2] Informations de processus d'une borne pour le cycle courant. + * + * \param evCharger Borne. \return Copie de son \c ChargingProcessInfo ; valeurs par + * défaut si la borne n'a pas été préparée ce cycle. + * + * \note N'a de sens qu'APRÈS \c prepareInformation() dans le même cycle — position 2 de + * \c update(), donc avant toute construction de contexte. + * \note Existe pour que l'arbitre n'ait pas à REDÉRIVER le nombre de phases possible. + * \c EvCharger::canSetPhaseCount() dit seulement que la borne PORTE l'état + * \c desiredPhaseCount ; la vraie question — la borne ET la voiture acceptent-elles 1 et + * 3 phases — est tranchée ici, une fois, avec le véhicule branché. Les deux prédicats ont + * divergé en 3g-1 : une borne monophasée sans voiture triphasée s'est vue viser 3 phases, + * et sous surcharge cela commandait 3 × 6 A là où le proxy commandait 1 × 6 A. + */ + ChargingProcessInfo internalProcessInfo(EvCharger *evCharger) const { return m_processInfos.value(evCharger); } // [ETM] new // [ETM] END private slots: