[doc] audit Doxygen : \param 'now' + docs périmées après refactors 3c/3e

Audit manuel (doxygen non installé, pas de Doxyfile). 5 findings corrigés :
- EvAdapter::applyAction : \param now manquant (param partiellement documenté → warning) ;
  toLoadContext : \param now ajouté.
- EnergyArbitrator::buildContext : mention SG-Ready + \param now (source unique verrous).
- applyActionsToAdapters : dispatch State→SG-Ready documenté (était ECS/Stage seul).
- onMeterWatchdogTick : doc alignée sur le refactor 7c (délègue à evaluateMeterFreshness,
  QTimer sous #ifndef ENERGY_SIMULATION).
- RuleBasedScheduler (classe + getPlan) : décrivait seulement le proxy EV → ajout du
  waterfall non-EV (budget net signé, priorité ASC, recrédit, clamp lock-aware) et
  correction "seul ctx.timestamp utilisé" (faux : meter + loads aussi).

Concepts 3c/3e vérifiés documentés : seam de temps/lockWindow, minStage/maxStage,
atomicité 2 bits (transientHarm), mode dégradé L2, waterfall unifié + ordre EV→ECS/SG-Ready,
hystérésis SG-Ready. Build 0/0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Patrick Schurig 2026-06-09 23:55:31 +02:00
parent e641f289db
commit 51760a7f61
3 changed files with 46 additions and 31 deletions

View File

@ -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.

View File

@ -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();

View File

@ -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;