// SPDX-License-Identifier: GPL-3.0-or-later // Copyright (C) 2025 - 2026, Patrick Schurig / ETM PowerSync #pragma once #include #include "../types/loadaction.h" #include "../types/loaddescriptor.h" #include "../types/surpluscontext.h" /*! * \brief Vue runtime minimale exposée par un adaptateur à l'arbitre. */ struct LoadTelemetry { double currentPowerW = 0; //!< Puissance mesurée (W). bool available = true; //!< Faux si l'appareil nymea est absent ou en erreur. QDateTime lastActionAt; //!< Dernier instant où applyAction() a produit un effet. }; /*! * \brief Interface pure des adaptateurs de charge. * * Les implémentations concrètes héritent de QObject + ILoadAdapter et déclarent leurs * propres signaux (telemetryChanged, descriptorChanged). * * \invariant Les adaptateurs EXÉCUTENT, ils ne décident pas (AGENTS règle 2). * \invariant applyAction() écrête les valeurs selon les limites matérielles réelles * (second filet après l'écrêtage de l'arbitre). * \invariant applyAction() avec \c reason vide doit être rejetée silencieusement. * \invariant Les méthodes non-applyAction() retournent immédiatement (pas de I/O bloquant). * \invariant **Temps = paramètre, jamais l'horloge.** Toute logique temporelle d'un * adaptateur (verrous minOn/minOff, fenêtres, fraîcheur…) utilise EXCLUSIVEMENT le * \c now (= \c ctx.timestamp) reçu en paramètre de \c toLoadContext()/applyAction(). * JAMAIS \c QDateTime::currentDateTime(). C'est cette source unique, partagée avec le * scheduler, qui rend impossible toute divergence décision/exécution et qui rend la * logique injectable en simulation. Contrat pour tout futur adaptateur (SgReady, Battery). */ class ILoadAdapter { public: virtual ~ILoadAdapter() = default; /*! * \brief Description statique de la charge : capacités, limites, priorité, needs. * \return LoadDescriptor construit depuis la configuration matérielle. * \note Peut être rappelé à chaque cycle — l'implémentation doit être légère. */ virtual LoadDescriptor descriptor() const = 0; /*! * \brief Télémétrie runtime (puissance, disponibilité, dernière action). * \return LoadTelemetry issue de l'état courant de l'appareil nymea. */ virtual LoadTelemetry telemetry() const = 0; /*! * \brief Construit l'entrée §5 loads[] pour le SurplusContext. * \param now Temps de cycle (\c ctx.timestamp). Source unique pour l'évaluation des * verrous (minStage/maxStage) — JAMAIS \c QDateTime::currentDateTime() côté adaptateur, * afin que décision (scheduler) et exécution (applyAction) partagent le même temps. * \return LoadContext incluant declared, limits, needs et télémétrie type-spécifique. */ virtual LoadContext toLoadContext(const QDateTime &now) const = 0; /*! * \brief Applique l'action et retourne ce qui a réellement été envoyé au matériel. * * L'arbitre a déjà écrêté selon les limites et le budget — ceci est le second filet. * * \param action Action à appliquer. Doit avoir \c reason non vide. * \param now Temps de cycle (\c ctx.timestamp) — MÊME source que toLoadContext(), * pour que l'évaluation des verrous coïncide avec celle vue par le scheduler. * \return L'action après écrêtage matériel (peut différer de l'entrée). * \note Retour silencieux sans effet si \c action.reason est vide. */ virtual LoadAction applyAction(const LoadAction &action, const QDateTime &now) = 0; };