Pont moteur↔app : déclaration des charges etmvariableload persistée et éditable, construction des adaptateurs à chaud depuis la config (remplace le registre en dur). - LoadConfig / LoadConfigNeeds (Q_GADGET typés, introspectables) — forme mot pour mot du LoadDescriptor §4 (jonction inter-repos avec l'app). - LoadConfigStore : persistance atomique /var/lib/nymea/energy-load-configuration.json, validation en bloc, signal changed(). - Handler NymeaEnergy.GetLoadConfig / SetLoadConfig (+ notif LoadConfigChanged). GET typé objectRef<LoadConfig> ; SET schéma inline o: (powerLevels/needs conditionnels §4). - EnergyArbitrator : setLoadConfigStore + rebuildEtmVariableLoadAdapters (enabled==true seulement, §9) ; repli L2 = setPowerSetpoint(0) force=true sur tout etmvariableload (ferme le trou sécurité ouvert en T2). - Tests simulation : migration ECS→etmvariableload (arrondi fixed/dynamic, recrédit, délestage, round-trip powerSetpoint), budget partagé etmvariableload↔PAC, watchdog L2 réactivé, persistance + construction depuis config + injection RPC end-to-end. PAC SG-Ready reste hors config (§8, gelée). Les 5 tâches du brief sont couvertes. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
50 lines
1.9 KiB
C++
50 lines
1.9 KiB
C++
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
// Copyright (C) 2025 - 2026, Patrick Schurig / ETM PowerSync
|
|
#pragma once
|
|
|
|
#include <QObject>
|
|
#include <QString>
|
|
#include "../types/loadconfig.h"
|
|
|
|
/*!
|
|
* \brief Store persistant des configs de charge pilotée (contrat etmvariableload §4).
|
|
*
|
|
* Master store unique : \c setConfigs() valide, **persiste** (\c /var/lib/nymea/\c
|
|
* energy-load-configuration.json, à côté de \c energy-manager-configuration.json) et émet
|
|
* \c changed() — l'arbitre reconstruit ses adaptateurs sur ce signal, le handler notifie l'app.
|
|
*
|
|
* Format fichier : \c {"version":1,"loads":[ <LoadConfig §4>, ... ]}. Le tableau \c loads[]
|
|
* est **mot pour mot** le \c LoadDescriptor §4 (jonction inter-repos avec l'app).
|
|
*
|
|
* \invariant Écriture atomique (fichier temporaire + \c rename) — jamais de fichier mi-écrit.
|
|
* \invariant \c setConfigs() rejette en bloc (rien persisté) si une seule entrée est invalide.
|
|
*/
|
|
class LoadConfigStore : public QObject
|
|
{
|
|
Q_OBJECT
|
|
public:
|
|
explicit LoadConfigStore(QObject *parent = nullptr);
|
|
|
|
/*! \brief Configs actuellement en mémoire (chargées au démarrage ou via setConfigs). */
|
|
LoadConfigs configs() const { return m_configs; }
|
|
|
|
/*!
|
|
* \brief Remplace l'ensemble des configs : valide tout → persiste → \c emit changed().
|
|
* \param configs Nouvel ensemble (remplace l'existant — l'app envoie la liste complète).
|
|
* \param[out] error Message FR si rejet (aucune écriture dans ce cas).
|
|
* \return true si validé + persisté ; false si invalide ou échec d'écriture.
|
|
*/
|
|
bool setConfigs(const LoadConfigs &configs, QString *error = nullptr);
|
|
|
|
signals:
|
|
/*! \brief Émis après une persistance réussie de \c setConfigs(). */
|
|
void changed();
|
|
|
|
private:
|
|
QString filePath() const;
|
|
bool load();
|
|
bool save(QString *error) const;
|
|
|
|
LoadConfigs m_configs;
|
|
};
|