Couverture des fichiers non visés par la restructuration à venir : l'extraction du noyau de calcul (étape 2) et celle de m_relayMapping (étape 3) ne les touchent pas. relayrouter.h et energyarbitrator.h sont volontairement DIFFÉRÉS — documenter ce qui va changer produirait du bruit d'historique. Mesure sur la config du Doxyfile : 113 → 31 avertissements. loadconfig.h 41→0 · surpluscontext.h 30→0 · sgreadyadapter.h 4→1 (le \return restant relève du lot suivant) · etmvariableloadadapter.h 2→0 · evadapter.h 1→0 Ce sont des contrats, pas des étiquettes : enabled dit qu'une charge déclarée mais exclue n'est JAMAIS pilotée ; priority qu'il s'agit d'un rang ascendant et non d'un poids ; timestamp qu'il est la source unique du temps, dont dérivent verrous et fenêtres ; setPowerLevels qu'il trie et déduplique ; fromMap qu'il retourne une config NON validée ; internalRootMeter() qu'il peut être nul. Ajouts « // [ETM] » hors etm/ (smartchargingmanager.h) : ils échappent au périmètre du Doxyfile, la frontière étant un répertoire. Un inventaire explicite est posé au marqueur [ETM] BEGIN, distinguant les trois cas — degradedMode() et les trois accesseurs internal* sont des ajouts ETM et sont documentés ; les huit changements de visibilité seule gardent la documentation de l'amont. La définition de fait d'AGENTS.md renvoie à cet inventaire et l'étend explicitement aux ajouts hors etm/. Build amd64 0 erreur. Simulation : 7/7. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
213 lines
9.3 KiB
C++
213 lines
9.3 KiB
C++
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
*
|
|
* Copyright (C) 2013 - 2024, nymea GmbH
|
|
* Copyright (C) 2024 - 2025, chargebyte austria GmbH
|
|
*
|
|
* This file is part of nymea-energy-plugin-nymea.
|
|
*
|
|
* nymea-energy-plugin-nymea.s free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* nymea-energy-plugin-nymea.s distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with nymea-energy-plugin-nymea. If not, see <https://www.gnu.org/licenses/>.
|
|
*
|
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
|
|
|
#ifndef SMARTCHARGINGMANAGER_H
|
|
#define SMARTCHARGINGMANAGER_H
|
|
|
|
#include <QObject>
|
|
#include <QTimer>
|
|
|
|
#include "energymanagerconfiguration.h"
|
|
#include "types/charginginfo.h"
|
|
#include "types/chargingaction.h"
|
|
#include "types/chargingschedule.h"
|
|
#include "types/chargingprocessinfo.h"
|
|
#include "spotmarket/spotmarketmanager.h"
|
|
|
|
// from libnymea
|
|
#include <integrations/thingmanager.h>
|
|
#include <hardware/electricity.h>
|
|
|
|
// from libnymea-energy
|
|
#include <energymanager.h>
|
|
|
|
class EvCharger;
|
|
class RootMeter;
|
|
|
|
// Minimum charging current (A) applied when EcoWithMinCurrent is active and no surplus is available
|
|
static constexpr uint EcoMinChargingCurrent = 6;
|
|
|
|
class SmartChargingManager : public QObject
|
|
{
|
|
Q_OBJECT
|
|
public:
|
|
explicit SmartChargingManager(EnergyManager *energyManager, ThingManager *thingManager, SpotMarketManager *spotMarketManager, EnergyManagerConfiguration *configuration, QObject *parent = nullptr);
|
|
|
|
uint phasePowerLimit() const;
|
|
void setPhasePowerLimit(uint phasePowerLimit);
|
|
|
|
double acquisitionTolerance() const;
|
|
void setAcquisitionTolerance(double acquisitionTolerance);
|
|
|
|
double batteryLevelConsideration() const;
|
|
void setBatteryLevelConsideration(double batteryLevelConsideration);
|
|
|
|
bool lockOnUnplug() const;
|
|
void setLockOnUnplug(bool lockOnUnplug);
|
|
|
|
ChargingInfos chargingInfos() const;
|
|
ChargingInfo chargingInfo(const ThingId &evChargerId) const;
|
|
EnergyManager::EnergyError setChargingInfo(const ChargingInfo &chargingInfo);
|
|
|
|
ChargingSchedules chargingSchedules() const;
|
|
|
|
/*!
|
|
* \brief [ETM] Mode dégradé L2 — le compteur principal est muet depuis plus de 90 s.
|
|
*
|
|
* Base amont : toujours faux (l'amont n'a pas de watchdog). Redéfini par
|
|
* \c EnergyArbitrator, qui le pose depuis \c applyDegradedMode() (SAFETY.md §L2).
|
|
* Exposé publiquement pour la notification JSON-RPC \c ChargingSchedulesChanged.
|
|
*
|
|
* \return Vrai si la planification est suspendue et les consignes de repli posées.
|
|
* La sécurité L4 (\c verifyOverloadProtection) reste active dans cet état.
|
|
*/
|
|
virtual bool degradedMode() const { return false; }
|
|
|
|
SpotMarketManager *spotMarketManager() const;
|
|
|
|
#ifdef ENERGY_SIMULATION
|
|
void simulationCallUpdate(const QDateTime &dateTime);
|
|
void simulationCallUpdateManualSoCsWithMeter(EnergyLogs::SampleRate sampleRate, const ThingPowerLogEntry &entry);
|
|
#endif
|
|
|
|
signals:
|
|
void phasePowerLimitChanged(int phasePowerLimit);
|
|
void acquisitionToleranceChanged(double acquisitionTolerance);
|
|
void batteryLevelConsiderationChanged(double batteryLevelConsideration);
|
|
void lockOnUnplugChanged(bool lockOnUnplug);
|
|
void chargingInfoAdded(const ChargingInfo &chargingInfo);
|
|
void chargingInfoRemoved(const ThingId &evChargerThingId);
|
|
void chargingInfoChanged(const ChargingInfo &chargingInfo);
|
|
void chargingSchedulesChanged();
|
|
|
|
#ifdef ENERGY_SIMULATION
|
|
void chargingUpdated();
|
|
#endif
|
|
|
|
// [ETM] BEGIN — SmartChargingManager protected API for EnergyArbitrator (etm/).
|
|
// All changes below are visibility-only (private → protected / virtual added).
|
|
// Zero logic change. Revert by deleting this block and restoring private slots.
|
|
//
|
|
// INVENTAIRE des ajouts ETM de ce fichier — ils vivent HORS de energyplugin/etm/ et
|
|
// échappent donc au périmètre du Doxyfile ; ils sont documentés à la main, et la
|
|
// définition de fait d'AGENTS.md les couvre explicitement. Toute nouvelle marque
|
|
// [ETM] dans un en-tête amont DOIT être ajoutée ici :
|
|
//
|
|
// 1. degradedMode() — ajout ETM, virtuel, documenté ci-dessus.
|
|
// 2. update() — virtual ajouté (le corps reste amont).
|
|
// 3. prepareInformation, planSpotMarketCharging, planSurplusCharging,
|
|
// adjustEvChargers, updateManualSoCsWithoutMeter, verifyOverloadProtection,
|
|
// verifyOverloadProtectionRecovery, executeChargingAction
|
|
// — visibilité seule (private → protected).
|
|
// Sémantique amont inchangée : NON documentés
|
|
// ici, la doc appartient à l'amont.
|
|
// 4. internalEvChargers(), internalChargingActions(), internalRootMeter()
|
|
// — accesseurs ETM neufs, documentés ci-dessous.
|
|
protected slots:
|
|
virtual void update(const QDateTime ¤tDateTime); // [ETM] virtual added
|
|
void prepareInformation(const QDateTime ¤tDateTime); // [ETM] private → protected
|
|
void planSpotMarketCharging(const QDateTime ¤tDateTime); // [ETM] private → protected
|
|
void planSurplusCharging(const QDateTime ¤tDateTime); // [ETM] private → protected
|
|
void adjustEvChargers(const QDateTime ¤tDateTime); // [ETM] private → protected
|
|
void updateManualSoCsWithoutMeter(const QDateTime ¤tDateTime); // [ETM] private → protected
|
|
void verifyOverloadProtection(const QDateTime ¤tDateTime); // [ETM] private → protected
|
|
void verifyOverloadProtectionRecovery(const QDateTime ¤tDateTime); // [ETM] private → protected
|
|
|
|
protected:
|
|
void executeChargingAction(EvCharger *evCharger, const ChargingAction &chargingAction, const QDateTime ¤tDateTime); // [ETM] private → protected
|
|
|
|
// [ETM] Read-only state accessors — inline, no copies, no logic.
|
|
|
|
/*!
|
|
* \brief [ETM] Bornes VE enregistrées, en lecture seule.
|
|
* \return Référence sur la table interne — aucune copie. Valide tant que le manager
|
|
* vit ; ne pas conserver au-delà d'un cycle \c update().
|
|
*/
|
|
const QHash<ThingId, EvCharger *> &internalEvChargers() const { return m_evChargers; } // [ETM] new
|
|
|
|
/*!
|
|
* \brief [ETM] Consignes de charge du cycle courant, en lecture seule.
|
|
* \return Référence sur la table remplie par \c planSurplusCharging() /
|
|
* \c planSpotMarketCharging(). N'a de sens qu'APRÈS ces appels dans le même cycle.
|
|
*/
|
|
const QHash<EvCharger *, ChargingActions> &internalChargingActions() const { return m_chargingActions; } // [ETM] new
|
|
|
|
/*!
|
|
* \brief [ETM] Compteur principal du site, en lecture seule.
|
|
* \return Pointeur non-propriétaire ; **peut être nul** si aucun root meter n'est
|
|
* configuré — tout appelant doit le tester.
|
|
*/
|
|
RootMeter *internalRootMeter() const { return m_rootMeter; } // [ETM] new
|
|
// [ETM] END
|
|
|
|
private slots:
|
|
void updateManualSoCsWithMeter(EnergyLogs::SampleRate sampleRate, const ThingPowerLogEntry &entry);
|
|
void onThingAdded(Thing *thing);
|
|
void onThingRemoved(const ThingId &thingId);
|
|
void onActionExecuted(const Action &action, Thing::ThingError status);
|
|
void onChargingModeChanged(const ThingId &evChargerId, const ChargingInfo &chargingInfo);
|
|
|
|
private:
|
|
void setupRootMeter(Thing *thing);
|
|
void setupEvCharger(Thing *thing);
|
|
void setupPluggedInHandlers(const Thing *thing);
|
|
|
|
void storeChargingInfo(const ChargingInfo &chargingInfo);
|
|
void storeManualChargingParameters(const ThingId &evChargerId, bool enabled, int maxChargingCurrent, uint desiredPhaseCount);
|
|
bool manualChargingEnabled(const ThingId &evChargerId) const;
|
|
uint manualMaxChargingCurrent(const ThingId &evChargerId) const;
|
|
uint manualDesiredPhaseCount(const ThingId &evChargerId) const;
|
|
|
|
Electricity::Phases getAscendingPhasesForCount(uint phaseCount);
|
|
uint getBestPhaseCount(EvCharger *evCharger, double surplusAmpere);
|
|
QString chargerPhaseKey(EvCharger *evCharger) const;
|
|
|
|
EnergyManager *m_energyManager = nullptr;
|
|
ThingManager *m_thingManager = nullptr;
|
|
SpotMarketManager *m_spotMarketManager = nullptr;
|
|
EnergyManagerConfiguration *m_configuration = nullptr;
|
|
|
|
QHash<EvCharger *, ChargingSchedules> m_chargingSchedules;
|
|
QHash<EvCharger *, ChargingProcessInfo> m_processInfos;
|
|
QHash<EvCharger *, ChargingActions> m_chargingActions;
|
|
|
|
QDateTime m_lastSpotMarketPlanning;
|
|
|
|
// Overload protection
|
|
QHash<EvCharger *, bool> m_overloadProtectionActive;
|
|
|
|
uint m_phasePowerConsumptionLimit = 25;
|
|
|
|
double m_acquisitionTolerance = 0.5;
|
|
double m_batteryLevelConsideration = 0.9;
|
|
|
|
QHash<ThingId, ChargingInfo> m_chargingInfos;
|
|
|
|
RootMeter *m_rootMeter = nullptr;
|
|
QHash<ThingId, EvCharger *> m_evChargers;
|
|
|
|
};
|
|
|
|
#endif // SMARTCHARGINGMANAGER_H
|