Itère slot.actions, dispatche les kind==Stage vers m_ecsAdapters (position 7, avant adjustEvChargers). EV (Setpoint) reste sur le proxy amont jusqu'à 3g. Build 0 erreur / 0 warning. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
131 lines
5.4 KiB
C++
131 lines
5.4 KiB
C++
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
// Copyright (C) 2025 - 2026, Patrick Schurig / ETM PowerSync
|
|
#pragma once
|
|
|
|
#include "../smartchargingmanager.h"
|
|
#include "scheduler/ischeduler.h"
|
|
#include "types/surpluscontext.h"
|
|
#include "types/plan.h"
|
|
|
|
class EvAdapter;
|
|
class EcsRelayAdapter;
|
|
class RuleBasedScheduler;
|
|
|
|
/*!
|
|
* \brief Arbitre central ETM — remplace SmartChargingManager::update() (ETM_ARBITRATOR).
|
|
*
|
|
* Hérite de SmartChargingManager pour conserver la compatibilité API complète avec
|
|
* NymeaEnergyJsonHandler sans modifier le code amont.
|
|
* Seul update() est surchargé : préparation → sécurité → planificateur → adapters.
|
|
*
|
|
* \invariant UN seul arbitre : EnergyArbitrator décide, les EvAdapter exécutent (règle 1).
|
|
* \invariant verifyOverloadProtection() est toujours appelée avant la planification (règle 4).
|
|
* \invariant Toute LoadAction transmise aux adapters a un \c reason non vide (règle 7).
|
|
* \invariant L'absence du root meter n'empêche pas le démarrage — cycle ignoré silencieusement.
|
|
*/
|
|
class EnergyArbitrator : public SmartChargingManager
|
|
{
|
|
Q_OBJECT
|
|
public:
|
|
explicit EnergyArbitrator(EnergyManager *energyManager, ThingManager *thingManager,
|
|
SpotMarketManager *spotMarketManager,
|
|
EnergyManagerConfiguration *configuration,
|
|
QObject *parent = nullptr);
|
|
|
|
/*!
|
|
* \brief Déclenche planSurplusCharging() (protégée) — appelé par RuleBasedScheduler.
|
|
* \param now Instant courant du cycle.
|
|
*/
|
|
void runSurplusPlanning(const QDateTime &now);
|
|
|
|
/*!
|
|
* \brief Déclenche planSpotMarketCharging() (protégée) — appelé par RuleBasedScheduler.
|
|
* \param now Instant courant du cycle.
|
|
*/
|
|
void runSpotMarketPlanning(const QDateTime &now);
|
|
|
|
/*!
|
|
* \brief Actions planifiées (résultat de runSurplus/SpotMarket).
|
|
* \return Référence constante vers la table EvCharger* → ChargingActions.
|
|
* \note Valide seulement après runSurplusPlanning() / runSpotMarketPlanning().
|
|
*/
|
|
const QHash<EvCharger *, ChargingActions> &scheduledActions() const;
|
|
|
|
/*!
|
|
* \brief Pont d'exécution pour EvAdapter — délègue à executeChargingAction() protégée.
|
|
* \param charger Borne EV cible.
|
|
* \param action ChargingAction à appliquer.
|
|
* \param now Instant de l'action (pour les locks anti-rebond).
|
|
*/
|
|
void doExecuteChargingAction(EvCharger *charger, const ChargingAction &action, const QDateTime &now);
|
|
|
|
/*!
|
|
* \brief Liste des EvCharger enregistrés (lecture seule).
|
|
* \return Table ThingId → EvCharger*.
|
|
*/
|
|
const QHash<ThingId, EvCharger *> ®isteredEvChargers() const;
|
|
|
|
/*!
|
|
* \brief Root meter courant.
|
|
* \return Pointeur ou nullptr si aucun compteur principal n'est enregistré.
|
|
*/
|
|
RootMeter *registeredRootMeter() const;
|
|
|
|
/*!
|
|
* \brief Enregistre un EcsRelayAdapter pour inclusion dans le contexte et le dispatch.
|
|
*
|
|
* Appelé par le test (setup) ou la configuration de production.
|
|
* L'adaptateur est adopté comme enfant Qt de l'arbitre.
|
|
* \param adapter Adaptateur à enregistrer. Son \c descriptor().id doit être unique.
|
|
*/
|
|
void registerEcsAdapter(EcsRelayAdapter *adapter);
|
|
|
|
protected:
|
|
/*!
|
|
* \brief Boucle principale ETM — surcharge SmartChargingManager::update().
|
|
*
|
|
* Ordre garanti :
|
|
* 1. updateManualSoCsWithoutMeter()
|
|
* 2. prepareInformation()
|
|
* 3. verifyOverloadProtection() + verifyOverloadProtectionRecovery()
|
|
* 4. m_scheduler->getPlan() → log des decisionReason
|
|
* 5. applyActionsToAdapters() (ECS Stage) + adjustEvChargers() (EV) → dispatch matériel
|
|
*
|
|
* \param currentDateTime Instant courant (timer ou simulation).
|
|
*/
|
|
void update(const QDateTime ¤tDateTime) override;
|
|
|
|
private:
|
|
/*!
|
|
* \brief Construit le SurplusContext §5 : meter brut + loads EV + loads ECS.
|
|
*
|
|
* \c ctx.meter.exportW = mesure brute du compteur (AGENTS invariant 8 — aucune
|
|
* déduction interne). La déduction evReservedW est faite dans le scheduler.
|
|
*/
|
|
SurplusContext buildContext(const QDateTime &now) const;
|
|
|
|
/*!
|
|
* \brief Synchronise m_adapters avec les EvCharger actuellement enregistrés.
|
|
* Crée les adapters manquants, supprime les adapters obsolètes.
|
|
* \note Découverte ECS via interface 'ecsrelay' ThingManager — déféré 3g config.
|
|
* En beta, les EcsRelayAdapters sont enregistrés via registerEcsAdapter().
|
|
*/
|
|
void syncAdapters();
|
|
|
|
/*!
|
|
* \brief Applique les actions d'un Slot aux LoadAdapters non-EV (ECS en 3c).
|
|
*
|
|
* Itère \c slot.actions et dispatche chaque action \c kind==Stage vers le
|
|
* EcsRelayAdapter correspondant (\c m_ecsAdapters[action.loadId]). Les actions EV
|
|
* (\c kind==Setpoint) restent dispatchées par \c adjustEvChargers() amont jusqu'à 3g.
|
|
* L'adaptateur écrête/verrouille lui-même (anti-rebond) et ignore toute action sans
|
|
* \c reason ou de kind non supporté — aucune décision ici (règle 2).
|
|
* \param slot Créneau courant retourné par le scheduler.
|
|
*/
|
|
void applyActionsToAdapters(const Slot &slot);
|
|
|
|
RuleBasedScheduler *m_scheduler = nullptr;
|
|
QHash<QString, EvAdapter *> m_adapters; //!< loadId (ThingId string) → EvAdapter*.
|
|
QHash<QString, EcsRelayAdapter *> m_ecsAdapters; //!< loadId → EcsRelayAdapter*.
|
|
};
|