// 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