- EnergyArbitrator : public SmartChargingManager — raison documentée dans AGENTS.md §DÉCISIONS DE DESIGN - SmartChargingManager : protected slots + virtual update() + 3 accesseurs inline [ETM] - RuleBasedScheduler::getPlan() wraps planSurplusCharging/planSpotMarketCharging, annote chaque action d'un reason français - EvAdapter : ILoadAdapter concret pour evcharger — applyAction() implémenté, NON appelé en 3b (dispatch via adjustEvChargers() amont, iso-fonctionnel) - ETM_ARBITRATOR : commenté dans .pro — ne s'active qu'après preuve iso-fonctionnelle (3b-iv) - Doxygen \brief + invariants + contrats sur toutes les classes/méthodes publiques etm/ (DoD §5) - plan.h : timeSlots (pas slots, mot-clé Qt) ; commentaire JSON sérialisation "slots" OPTIMIZER_PROTOCOL §6 - .clangd : flags de repli Qt/nymea pour clangd via symlink ~/Schreibtisch/ - compile_commands.json gitignore (chemins absolus locaux) - Build : 0 erreurs, 0 warnings — libnymea_energypluginnymea.so 914 KB Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
8.4 KiB
AGENTS.md — etm-powersync-energy-plugin-etm
Moteur HEMS. Fork GPL de nymea-energy-plugin-nymea, étendu de l'optimisation EV
vers un gestionnaire d'énergie complet (EV, ECS, PAC SG-Ready, batterie).
- Licence : GPL-3.0 · Miroir public : OUI
- Branche de travail :
feature/beta-rulebased - Document d'interface faisant autorité :
docs/OPTIMIZER_PROTOCOL.md(le contrat stratégie/arbitrage — interne ET socket).INTERFACE.mdfait autorité sur l'API JSON-RPC.
⚠️ Tout plan antérieur mentionnant « créer etm/ avec PowerSyncClient et StaticHcHpProvider comme première étape » ou « injecter l'optimiseur dans SmartChargingManager » est INVALIDE et ABANDONNÉ. Ne pas le reprendre, quelle qu'en soit la source (fichier, mémoire de session, contexte).
ARCHITECTURE CIBLE (non négociable)
┌──────────────────────────────┐
│ ARBITRAGE CENTRAL │ ← généralisation du
│ budget de surplus UNIQUE │ SmartChargingManager amont
│ waterfall par priorités │
└──────┬───────────────────────┘
│ IScheduler (= contrat OPTIMIZER_PROTOCOL)
┌───────────┴───────────┐
RuleBasedScheduler SocketScheduler
(in-process, V1, GPL) (client unix://|tcp://, V1 aussi —
plan à 1 créneau personne en face en beta : repli rules)
│
│ distribue le budget en LoadAction typées
┌──────────┬────┴─────┬──────────────┐
EvAdapter EcsRelayAdapter SgReadyAdapter BatteryAdapter
(setpoint, (stage 0/1/2) (state 1-4) (constraint +
iface setpoint W réseau)
evcharger
nymea)
Règles absolues :
- UN seul arbitre. Le budget de surplus est une ressource unique, arbitrée à UN endroit. INTERDIT : managers frères par type de charge (EcsManager, BatteryManager à côté du SmartChargingManager) — deux décideurs sur le même surplus = sur-engagement et oscillations.
- Les LoadAdapters exécutent, ils ne décident pas. Un adaptateur : parle à son
matériel, déclare ses capacités/contraintes (
declared,limits, types d'action), expose sa télémétrie, applique lesLoadActionreçues. Aucune logique de répartition dedans. - Le SmartChargingManager amont est EV-spécifique : il se GÉNÉRALISE en arbitrage
multi-charges (
ChargingAction→LoadAction, bornes EV → adaptateurs). On ne branche PAS l'optimiseur dans le manager EV tel quel. - La boucle de sécurité est intouchable :
verifyOverloadProtection()(temps réel) + bornes par adaptateur écrêtent TOUTE sortie de stratégie, interne ou socket. - Plan par créneaux (OPTIMIZER_PROTOCOL §6) : seul le créneau courant est exécuté.
Le rule-based répond un plan à 1 créneau. Modèle async = cache : le plan du
cycle précédent s'applique, le recalcul se fait en fond. Jamais d'attente dans
update(). - Repli toujours fonctionnel : optimiseur absent/mort/abstain → rule-based.
Capabilities (
tier,optimizerExpected,optimizerAlive,activeStrategy) reflètent l'état en continu. decisionReasonnon vide, en français, sur chaque action. Action sans reason = rejetée.- Pas de boucle de feedback : surplus = PV mesurée + compteur, jamais le net après pilotage.
- Aucun composant propriétaire ici (Héos = repo privé
etm-powersync-optimizer). Ce repo doit compiler et tourner seul, GPL pur.
RÉPONSES FIGÉES (ne plus poser ces questions)
- Plages HC/HP et tarifs : configuration JSON, jamais hardcodé. Prévoir Tempo (6 types de jours), pas seulement HC/HP.
- Async : modèle cache (cf. règle 5).
- Bugs upstream : commits séparés du code ETM, message préfixé
[upstream-fix]. Candidats PR nymea (fix phases EV, Keba) = patchs isolés, propres, upstreamables. protocolVersion: constante"1.0", pas un paramètre de config.- Renommage : FAIT (Phase 1, commit
f4d5b20). TARGET et noms de paquets debian INCHANGÉS (.so drop-in remplaçant l.amont — garantit un seul plugin énergie chargé).
WORKFLOW OBLIGATOIRE
Chaque phase produit un livrable VALIDÉ PAR PATRICK avant la suivante. Jamais de code avant validation du design de la phase.
- Phase 0 — Analyse (en cours) : répondre par écrit, code lu à l'appui : (a) quelles charges SmartChargingManager pilote-t-il (types manipulés) ; (b) ChargingAction peut-il exprimer « ECS palier 1 » / « batterie décharge interdite » — citer ses champs ; (c) avec des managers séparés, où vivrait le budget unique. Zéro code, zéro plan d'implémentation.
- Phase 1 — Renommage :
git mvdu.pro, TARGET, debian/. Un commit, revue. - Phase 2 — Design de l'arbitrage généralisé : interface
LoadAdapter(méthodes, ce qu'un adaptateur déclare), flux du budget, mappingLoadAction→adaptateurs, où vitIScheduler. Texte + signatures, pas d'implémentation. Validation Patrick. - Phase 3 — Implémentation par étapes (chacune : compile amd64 + cross arm64,
et un scénario
docker-simulation.shqui la prouve = DoD) : 3a. structs du protocole (contexte, plan, actions) ; 3b. arbitre + RuleBasedScheduler + EvAdapter (iso-fonctionnel avec l'amont sur EV) ; 3c. EcsRelayAdapter (paliers) ; 3d. SocketScheduler (handshake/heartbeat/repli, testé contre un optimiseur factice ~50 lignes) ; 3e. SgReadyAdapter ; 3f. BatteryAdapter (constraints + charge réseau plafonnée). - Bugs upstream : au fil de l'eau, commits
[upstream-fix]séparés.
DÉCISIONS DE DESIGN (écarts et justifications)
3b-iii — EnergyArbitrator hérite de SmartChargingManager
Design validé en session : "nouvelle classe dans etm/, n'étend pas SmartChargingManager".
Écart implémenté : EnergyArbitrator : public SmartChargingManager.
Justification :
-
Contrainte NymeaEnergyJsonHandler : ce handler amont prend un
SmartChargingManager*dans son constructeur.
Sans héritage, toute solution propre (interface commune, pointeur générique) nécessiterait de modifiernymeaenergyjsonhandler.h/.cpp— violation de la règle "Modifier le code amont uniquement pour corriger des bugs". -
verifyOverloadProtection() intacte : héritée bit-pour-bit, connectée aux mêmes signaux via le constructeur du parent. Zéro risque de régression sur la sécurité.
-
simulationCallUpdate() polymorphe : appelle
update()virtuel → redirige automatiquement versEnergyArbitrator::update(). Les tests amont passent sans modification. -
Minimal upstream diff : seuls les attributs
protected/virtualchangent danssmartchargingmanager.h(marqués// [ETM]). Zéro logique upstream modifiée.
Risque accepté : EnergyArbitrator a accès à l'état privé de SCM via les
accesseurs internal*. La discipline AGENTS (LoadAdapters exécutent, ne décident pas ;
un seul arbitre) compense. Si SCM était refactorisé en amont pour exposer une interface
publique propre, l'héritage pourrait être remplacé par composition.
DÉFINITION DE FAIT (par étape de phase 3)
- Compile amd64 et cross arm64.
- Scénario de simulation ajouté/étendu qui démontre le comportement (le harnais
docker-simulation.sh+tests/autohérités sont le banc de test). decisionReasonvisibles dans les logs de simulation.- Aucune régression des tests amont existants.
- Toute classe/méthode publique de
etm/porte un commentaire Doxygen :\brief,\param,\return, et surtout le contrat de comportement (invariants, écrêtage, hypothèses que l'appelant peut faire). Les headers 3a servent de modèle — les convertir au format Doxygen lors du passage 3b.
RÉFÉRENCES
docs/OPTIMIZER_PROTOCOL.md— le contrat. §5 (SurplusContext), §6 (plan/actions), §7 (repli), annexe C (priorités).README.md— architecture (deux boucles, frontière),etm_powersync_energy.svg.INTERFACE.md— API JSON-RPC existante (NymeaEnergy, cible futureEms).- Carte globale du workspace :
../AGENTS.md.