- OptimizerStatus::Vivacite : optimizerAlive, échecs comptés PAR REQUÊTE (E17), deux
consécutifs = mort, socket fermé = mort, retour au premier plan valide ; une abstention
rompt la série sans ressusciter. strategieActive() : none sous L2, prioritaire sur tout ;
rules dès qu'un optimiseur n'est plus vivant, sans attendre le cycle suivant.
- SocketScheduler : branchement de la vivacité, cache abandonné à la mort, UpdateLearned
journalisé une fois et ignoré.
- NymeaEnergy.GetOptimizerStatus + OptimizerStatusChanged (une notification par
transition, charge utile déclarée une fois) ; champs installateur absents s'ils sont
inconnus ; jamais le token, tokenConfigured seulement. Pas de tier (Q5).
- SetOptimizerEndpoint (persisté, appliqué à chaud) et SetOptimizerToken (écriture seule,
fichier 0600, Q2), tous deux PermissionScopeAdmin ; token déclaré Variant pour qu'un
mauvais type ne soit pas recopié au journal par le validateur nymea.
- NymeaEnergy.GetPlan : plan en vigueur (appliqué au créneau courant, demandé ensuite),
codes et paramètres filtrés seulement, inForce toujours présent, fallbackCode quand un
optimiseur configuré n'a pas commandé.
- Tests : tableau §7 ligne par ligne, transitions notifiées une fois, RPC, token absent de
tout journal du moteur et de toute réponse.
Sans endpoint : decisions IDENTIQUE (23 792 lignes), suites inchangées, journal >= info
inchangé (8 739 lignes).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Réglage `Optimizer/endpoint` (energy.conf), lu au démarrage par
EnergyArbitrator::relireReglageOptimiseur() — lecture seule, la RPC viendra
en T4. ABSENT : aucun SocketScheduler ni OptimizerClient n'existe, update()
interroge le rule-based comme avant (decisions IDENTIQUE, 23 772 lignes).
SocketScheduler (IScheduler) : à chaque cycle, le rule-based en OMBRE (effets
de bord conservés), puis GetPlan envoyé sans attendre (getPlanParams + fil de
l'arbitre : compteur racine, PV, phases, réserve, domaines, mesures,
prévision réelle) ; si le plan en cache est valide pour CE cycle, ses actions
remplacent celles de l'ombre (OptimizerPlan::substituer). Sinon rule-based :
plan absent, invalide, expiré, abstention, connexion perdue. Un plan invalide
vide le cache. Les actions passent par applyActionsToAdapters comme celles du
rule-based ; apresDispatch() relit l'appliqué (Q3, Q4, clamped), avant la
fusion L4. L4 et L2 inchangés : L4 garde le dernier mot, L2 suspend tout.
Déclencheur réel (§5.1 trigger) : intent (ChargingInfo), load-change (Thing
ajouté/retiré), config (SetLoadConfig, limites, réserve), forecast (révision
nouvelle) ; le plus significatif l'emporte, consommé à l'envoi.
Journal : le texte libre de l'optimiseur y va (assaini), jamais dans la
télémétrie ; lignes en clair au changement seulement (stratégie, motifs,
écrêtage). Version du moteur du handshake lue dans debian/changelog.
Points d'accroche T4 : reponseJugee(), echecRequete(), strategieDuCycle().
INTERFACE.md : niveau unique « optimizer », budget/draw sous l'optimiseur,
préséance L4 > indisponible/dégradé > optimiseur > éco|confort.
Tests : suite etm-powersync-socketscheduler (12 fonctions, 43 passés) —
faux optimiseur unix et tcp dans le processus, arbitre réel.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>