diff --git a/energyplugin/etm/adapters/evadapter.h b/energyplugin/etm/adapters/evadapter.h index 53e5831..51bde1d 100644 --- a/energyplugin/etm/adapters/evadapter.h +++ b/energyplugin/etm/adapters/evadapter.h @@ -46,6 +46,8 @@ public: /*! * \brief Construit l'entrée loads[] §5 du SurplusContext. + * \param now Temps de cycle (\c ctx.timestamp). Inutilisé ici : l'EV n'a pas de verrou + * de palier (hors waterfall ECS/SG-Ready). Présent pour l'uniformité de \c ILoadAdapter. * \return LoadContext incluant declared, limits, needs et télémétrie EV. */ LoadContext toLoadContext(const QDateTime &now) const override; @@ -59,6 +61,8 @@ public: * \c descriptor() et \c telemetry() sont eux actifs dès maintenant pour le SurplusContext. * * \param action LoadAction de kind Setpoint. Autres kinds : retour sans effet. + * \param now Temps de cycle (\c ctx.timestamp) — passé à \c doExecuteChargingAction() + * (locks anti-rebond de la borne). MÊME source que toLoadContext() (contrat ILoadAdapter). * \return L'action après écrêtage matériel (currentA, phaseCount bornés). * * \invariant action.reason non vide requis — log warning et retour sans effet sinon. diff --git a/energyplugin/etm/energyarbitrator.h b/energyplugin/etm/energyarbitrator.h index fb7c48f..e84903e 100644 --- a/energyplugin/etm/energyarbitrator.h +++ b/energyplugin/etm/energyarbitrator.h @@ -141,10 +141,13 @@ protected: private: /*! - * \brief Construit le SurplusContext §5 : meter brut + loads EV + loads ECS. + * \brief Construit le SurplusContext §5 : meter brut + loads EV + ECS + SG-Ready. * * \c ctx.meter.exportW = mesure brute du compteur (AGENTS invariant 8 — aucune * déduction interne). La déduction evReservedW est faite dans le scheduler. + * \param now Temps de cycle (\c ctx.timestamp) : pose \c ctx.timestamp et sert de SOURCE + * UNIQUE aux fenêtres de verrou (\c minStage/maxStage, \c minState/maxState) calculées + * par chaque adaptateur — cohérence décision (scheduler) / exécution (applyAction). */ SurplusContext buildContext(const QDateTime &now) const; @@ -157,11 +160,11 @@ private: void syncAdapters(); /*! - * \brief Applique les actions d'un Slot aux LoadAdapters non-EV (ECS en 3c). + * \brief Applique les actions d'un Slot aux LoadAdapters non-EV (ECS + SG-Ready). * - * 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. + * Itère \c slot.actions et dispatche selon le kind : \c Stage → \c m_ecsAdapters + * (EcsRelayAdapter) ; \c State → \c m_sgReadyAdapters (SgReadyAdapter). Les actions EV + * (\c 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. @@ -171,13 +174,14 @@ private: void applyActionsToAdapters(const Slot &slot, const QDateTime &now); /*! - * \brief Tick du watchdog L2 (SAFETY.md §L2) — piloté par \c m_meterWatchdog (QTimer 30 s). + * \brief Déclencheur RÉEL du watchdog L2 (SAFETY.md §L2) — slot de \c m_meterWatchdog + * (QTimer 30 s, horloge murale ; câblé sous \c \#ifndef ENERGY_SIMULATION). * - * Si \c QDateTime::currentDateTime() − \c m_lastMeterUpdate dépasse 90 s, déclenche - * \c applyDegradedMode(). Indépendant des signaux compteur : reste actif précisément - * quand le compteur est muet (le signal \c powerBalanceChanged ne fire plus). - * \note Tant que \c m_lastMeterUpdate est invalide (aucune mesure reçue depuis le - * démarrage), aucun mode dégradé n'est déclenché (invariant root meter absent). + * Délègue simplement à \c evaluateMeterFreshness(QDateTime::currentDateTime()) : la + * LOGIQUE (seuil 90 s, bascule en dégradé) est dans cette méthode injectable, le QTimer + * n'est que le battement. Indépendant des signaux compteur : reste actif précisément + * quand le compteur est muet (le signal \c powerBalanceChanged ne fire plus, et + * \c update() — piloté par le compteur — s'arrête aussi). Voir \c evaluateMeterFreshness(). */ void onMeterWatchdogTick(); diff --git a/energyplugin/etm/scheduler/rulebasedscheduler.h b/energyplugin/etm/scheduler/rulebasedscheduler.h index 8d65177..5a0f8c3 100644 --- a/energyplugin/etm/scheduler/rulebasedscheduler.h +++ b/energyplugin/etm/scheduler/rulebasedscheduler.h @@ -10,23 +10,30 @@ class ChargingAction; class EnergyArbitrator; /*! - * \brief Planificateur basé sur les règles GPL (EV surplus + aWATTar). + * \brief Planificateur règles GPL : EV (proxy amont) + waterfall surplus ECS / SG-Ready. * - * **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. + * \c getPlan() produit un plan à 1 créneau en DEUX temps : * - * À 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é". + * 1. **EV — proxy amont (beta, jusqu'à 3g)** : délègue la planification EV à + * \c planSurplusCharging() / \c planSpotMarketCharging() héritées de SmartChargingManager, + * relit \c m_chargingActions et les reformate en LoadAction(Setpoint) annotées d'un + * \c reason français. Le dispatch EV réel reste dans \c adjustEvChargers() amont. * - * \invariant getPlan() retourne IMMÉDIATEMENT (AGENTS invariant 5). - * \invariant getPlan() retourne toujours un Plan valide (isValid() == true). + * 2. **Waterfall non-EV (3c/3e)** : un budget de surplus UNIQUE — net SIGNÉ + * \c (exportW − importW) − evReservedW — cascade par **priorité ASC** (rang, 1 = premier + * servi) à travers les charges \c relay-stages (ECS, \c buildEcsStageAction) ET + * \c sg-ready (PAC, \c buildSgReadyStateAction). Anti-clignotement par **recrédit** de la + * conso allouée ; **clamp lock-aware** \c minStage/maxStage et \c minState/maxState (verrous + * minOn/minOff/minStateHold, protection compresseur) — la fenêtre est calculée au MÊME + * \c ctx.timestamp que l'exécution (cf. \c ILoadAdapter). Ces LoadAction sont RÉELLEMENT + * dispatchées par \c EnergyArbitrator::applyActionsToAdapters(). + * + * À partir de **3g** : l'EV rejoindra le waterfall unifié (toutes charges classables ensemble). + * + * \invariant getPlan() retourne IMMÉDIATEMENT (AGENTS invariant 5) et toujours un Plan valide. * \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. + * \invariant EV (étape 1) : priorité Deadline VE > Surplus PV > aWATTar > Min courant > Idle + * (iso-fonctionnel amont 3b). Non-EV (étape 2) : ordre = \c priority croissant. */ class RuleBasedScheduler : public QObject, public IScheduler { @@ -40,15 +47,15 @@ public: explicit RuleBasedScheduler(EnergyArbitrator *arbitrator, QObject *parent = nullptr); /*! - * \brief Retourne le plan pour le slot courant. + * \brief Retourne le plan pour le slot courant (EV proxy + waterfall non-EV). * - * **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. + * Étape 1 (EV) : \c runSpotMarketPlanning() + \c runSurplusPlanning() puis reformatage + * de \c scheduledActions() en LoadAction(Setpoint) — log [Arbitre], dispatch amont. + * Étape 2 (non-EV) : waterfall surplus sur les charges \c relay-stages / \c sg-ready + * triées par priorité — LoadAction(Stage/State) réellement dispatchées. * - * \param ctx SurplusContext courant. En 3b : seul \c ctx.timestamp est utilisé. + * \param ctx SurplusContext courant. Utilisé : \c ctx.timestamp (temps de cycle / verrous), + * \c ctx.meter (surplus net signé), \c ctx.loads (déclarés, télémétrie, fenêtres de verrou). * \return Plan à 1 créneau couvrant \c ctx.timestamp + 60 s. */ Plan getPlan(const SurplusContext &ctx) override;