// SPDX-License-Identifier: GPL-3.0-or-later // Copyright (C) 2025 - 2026, Patrick Schurig / ETM PowerSync #pragma once #include #include "ischeduler.h" class EvCharger; class ChargingAction; class EnergyArbitrator; /*! * \brief Planificateur basé sur les règles GPL (EV surplus + aWATTar). * * **Rôle en beta (jusqu'à 3g)** : PROXY AMONT POUR L'EV — délègue toute la * planification EV à \c planSurplusCharging() et \c planSpotMarketCharging() * héritées de SmartChargingManager. Ces méthodes décident (acquisitionTolerance, * batteryLevelConsideration, waterfall, spot market) ; ce scheduler relit leurs * sorties (m_chargingActions) et les reformate en LoadAction annotées d'un * \c reason français — pour le log [Arbitre] uniquement. * * À partir de **3g** : la logique de surplus sera transplantée ici (waterfall * unifié sur toutes les charges, EV inclus). Voir AGENTS.md § "3b révisé". * * \invariant getPlan() retourne IMMÉDIATEMENT (AGENTS invariant 5). * \invariant getPlan() retourne toujours un Plan valide (isValid() == true). * \invariant Toute LoadAction a un \c reason non vide, en français. * \invariant Priorité : Deadline VE > Surplus PV > aWATTar > Min courant > Idle. * Identique à adjustEvChargers() amont — iso-fonctionnel 3b. */ class RuleBasedScheduler : public QObject, public IScheduler { Q_OBJECT public: /*! * \brief Constructeur. * \param arbitrator Arbitre propriétaire — fournit l'accès à la planification et à l'état. * \param parent Propriétaire Qt. */ explicit RuleBasedScheduler(EnergyArbitrator *arbitrator, QObject *parent = nullptr); /*! * \brief Retourne le plan pour le slot courant. * * **Beta (proxy)** : appelle \c runSpotMarketPlanning() puis \c runSurplusPlanning() * (qui écrivent dans m_chargingActions via les méthodes parent), lit le résultat * via \c scheduledActions(), et construit les LoadAction correspondantes. * Les LoadAction servent uniquement au log [Arbitre] — le dispatch réel reste dans * \c adjustEvChargers() amont. * * \param ctx SurplusContext courant. En 3b : seul \c ctx.timestamp est utilisé. * \return Plan à 1 créneau couvrant \c ctx.timestamp + 60 s. */ Plan getPlan(const SurplusContext &ctx) override; private: /*! * \brief Construit un LoadAction pour le cas "délai requis" (TimeRequirement). * \param ev EvCharger concerné. * \param ca ChargingAction planifiée (courant et phases déjà calculés par planSurplusCharging). * \return LoadAction avec funding=Grid et reason "Deadline VE". */ LoadAction buildTimeRequirementAction(EvCharger *ev, const ChargingAction &ca) const; /*! * \brief Construit un LoadAction "courant minimum" pour les modes EcoMin. * \param ev EvCharger concerné. * \return LoadAction avec funding=Surplus, chargingEnabled=true, currentA=min. */ LoadAction buildMinCurrentAction(EvCharger *ev) const; /*! * \brief Construit un LoadAction "idle" (recharge désactivée, aucun surplus). * \param ev EvCharger concerné. * \return LoadAction avec chargingEnabled=false et reason appropriée. */ LoadAction buildIdleAction(EvCharger *ev) const; EnergyArbitrator *m_arbitrator; };