getPlan : cascade unique sur charges non-EV (relay-stages + sg-ready) triées par priorité — budget de surplus partagé. buildSgReadyStateAction : mapping qualitatif (4 forcé hyst. 1,2/1,0 ; 3 reco ≥P3 ; 2 normal mains off ; 1 jamais via surplus), recrédit sur puissance allouée déclarée, clamp lock-aware minState/maxState. AGENTS.md : ROADMAP config priorités utilisateur (acquis tri unifié ; manque 3g VE + couche config JSON-RPC/UI Flutter pour drag-and-drop à chaud). Build 0/0. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
111 lines
5.2 KiB
C++
111 lines
5.2 KiB
C++
// SPDX-License-Identifier: GPL-3.0-or-later
|
||
// Copyright (C) 2025 - 2026, Patrick Schurig / ETM PowerSync
|
||
#pragma once
|
||
|
||
#include <QObject>
|
||
#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;
|
||
|
||
/*!
|
||
* \brief Construit un LoadAction "stage" ECS par cascade de surplus (waterfall §6).
|
||
*
|
||
* Retient le palier déclaré le plus haut dont la puissance tient dans le budget courant.
|
||
* Correction B (anti-clignotement) : recrédite d'abord \c lc.telemetry.currentPowerW au
|
||
* budget (la conso actuelle de l'ECS est déjà soustraite de l'export mesuré), puis
|
||
* décrémente \c remainingSurplusW de la puissance du palier retenu.
|
||
*
|
||
* \param lc Charge ECS (adapter == "relay-stages") du SurplusContext.
|
||
* \param[in,out] remainingSurplusW Budget de surplus restant (W) ; mis à jour pour la
|
||
* charge ECS suivante (priorité inférieure / rang supérieur).
|
||
* \return LoadAction kind=Stage, funding=Surplus, \c reason français non vide.
|
||
*/
|
||
LoadAction buildEcsStageAction(const LoadContext &lc, double &remainingSurplusW) const;
|
||
|
||
/*!
|
||
* \brief Construit un LoadAction "state" SG-Ready (PAC) par mapping SÉMANTIQUE du surplus.
|
||
*
|
||
* 4 états normés (qualitatifs, pas des paliers) : surplus abondant stable → 4 (forcé,
|
||
* hystérésis P4×1,2 entrée / P4×1,0 sortie) ; surplus durable → 3 (recommandation, ≥P3) ;
|
||
* sinon → 2 (normal, mains off). L'état 1 (effacement) n'est PAS déclenché par le surplus
|
||
* seul (déféré : signal tarif/réseau). Recrédit (correction B) sur la puissance allouée
|
||
* (déclaré, 0 pour 1/2). Clamp lock-aware via \c minState/maxState (court-cycling PAC).
|
||
*
|
||
* \param lc Charge SG-Ready (adapter == "sg-ready") du SurplusContext.
|
||
* \param[in,out] remainingSurplusW Budget de surplus restant (W), mis à jour pour la suite.
|
||
* \return LoadAction kind=State, funding=Surplus, \c reason français non vide.
|
||
*/
|
||
LoadAction buildSgReadyStateAction(const LoadContext &lc, double &remainingSurplusW) const;
|
||
|
||
EnergyArbitrator *m_arbitrator;
|
||
};
|