// 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 . * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #ifndef ENERGYRATIOSCALCULATOR_H #define ENERGYRATIOSCALCULATOR_H #include /*! * \brief [Phase 2] Ratios énergétiques canoniques (autoconsommation / autonomie), en %. * * Source canonique côté energymanager : remplace le seam interim app-side * (\c EnergyRatiosInterim.compute). **PUR MESURE** (frontière GPL) : dérive par Δ de * cumuls sur la période affichée (journée depuis minuit local), pas par intégration du * signal live → déterministe, pas de drift de polling. * * \par Formules * \code * autonomie = (Δ totalConsumption − Δ totalAcquisition) / Δ totalConsumption * autoconsommation = (Δ totalProduction − Δ totalReturn) / Δ totalProduction * \endcode * * \invariant Reseed de la baseline dans trois cas, et seulement ceux-là : premier appel, * changement de jour local, OU compteur non monotone (Δ < 0 — redémarrage de nymead, * thing ré-ajouté, rollover). * \invariant Dénominateur ≤ 0 → résultat marqué n/a (\c valid == false). **Jamais de NaN**, * jamais un 0 % qui se ferait passer pour une mesure. * \invariant Porté 1:1 de l'interim app — toute divergence de formule est un défaut. */ class EnergyRatiosCalculator { public: /*! \brief Résultat d'un calcul : deux ratios, chacun accompagné de sa validité. */ struct Ratios { bool autoconsommationValid = false; //!< Faux = n/a (dénominateur nul) — ne pas afficher \c autoconsommation. double autoconsommation = 0.0; //!< Part de la production consommée sur place, % dans [0, 100]. bool autonomieValid = false; //!< Faux = n/a (dénominateur nul) — ne pas afficher \c autonomie. double autonomie = 0.0; //!< Part de la consommation couverte sans soutirage, % dans [0, 100]. /*! * \brief Début de la PÉRIODE de reporting en cours — le minuit local qui l'ouvre. * * Publié plutôt que laissé à déduire : \c System.GetTime donne un nom IANA, et un * décalage ne s'en déduit pas sans base de fuseaux. Embarquer une base pour recalculer * une frontière que le moteur connaît déjà serait le mauvais geste — et le recalcul * resterait faux les jours de changement d'heure, où la journée fait 23 ou 25 heures. */ QDateTime debutPeriode; /*! * \brief Instant où la BASELINE courante a été posée. Ce n'est PAS toujours * \c debutPeriode, et l'écart est la seule chose qui empêche les ratios de mentir. * * La baseline se repose à chaque redémarrage de nymead et à chaque recul de compteur. * Un jour où nymead a redémarré à 14 h, les ratios ne couvrent PAS la journée : ils * couvrent depuis 14 h. Publier la seule frontière de journée ferait afficher * « aujourd'hui » sur des chiffres qui ne parlent que de l'après-midi. * * Même paire, même raison que \c measuredSince / \c targetWh de l'avancement éco. */ QDateTime mesureDepuis; }; /*! \brief Construit sans baseline : le premier \c compute() la posera. */ EnergyRatiosCalculator() = default; /*! * \brief Réinitialise la baseline (ex. changement d'installation). * \note Le \c compute() suivant repose la baseline et retourne donc n/a sur les deux * ratios : sans écart de cumuls, il n'y a rien à mesurer. */ void reset(); /*! * \brief Calcule les deux ratios depuis les cumuls courants. * \param totalProduction Cumul de production. Unité libre — elle s'annule dans le ratio. * \param totalReturn Cumul réinjecté au réseau, même unité. * \param totalConsumption Cumul de consommation, même unité. * \param totalAcquisition Cumul soutiré au réseau, même unité. * \param now Horodatage en heure **LOCALE** : sert à détecter le changement * de jour, donc le reseed. Passer de l'UTC fausserait la frontière * de journée. * \return Les deux ratios ; un ratio dont le dénominateur est ≤ 0 revient \c valid == false. */ Ratios compute(double totalProduction, double totalReturn, double totalConsumption, double totalAcquisition, const QDateTime &now); private: static bool ratio(double numerator, double denominator, double *outPercent); static qint64 localDay(const QDateTime &now); bool m_hasBaseline = false; //! Instant de pose de la baseline courante — publié en \c mesureDepuis. QDateTime m_baselinePosee; double m_baseProduction = 0.0; double m_baseReturn = 0.0; double m_baseConsumption = 0.0; double m_baseAcquisition = 0.0; qint64 m_baseDay = 0; }; #endif // ENERGYRATIOSCALCULATOR_H