Patrick Schurig 093fa09b5e [3e-3] mapping sémantique SG-Ready + waterfall unifié ECS/SG-Ready
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>
2026-06-09 23:19:54 +02:00

111 lines
5.2 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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;
};