- supprime selfRate/autoRate de _parsePowerBalance, le getter autoconsommationW, les .abs() interim - ratios dérivés par Δ-de-cumuls (§2.1/§8.1) dans lib/services/energy_ratios.dart - reseed jour-roulant & Δ non-monotone ; gardes : dén≤0→null, pas de NaN, clamp [0,100] - selfConsumptionPower net-signé sans .abs() (§2.2) ; signes nymea bruts préservés (§7.1) - seam swappable vers un state plugin en Phase 2 (une seule fonction) Interim (Phase 1). Gestion du signe d'affichage renvoyée à la Phase 4 (§1.1). Réf. UI_DATA_CONTRACT.md rev.5. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
100 lines
3.9 KiB
Dart
100 lines
3.9 KiB
Dart
/// Ratios énergétiques affichés (autoconsommation / autonomie), en %.
|
||
/// `null` = **n/a** (dénominateur nul) — jamais de NaN, jamais d'aberration.
|
||
class EnergyRatios {
|
||
final double? autoconsommation; // % dans [0, 100], ou null = n/a
|
||
final double? autonomie; // % dans [0, 100], ou null = n/a
|
||
|
||
const EnergyRatios({this.autoconsommation, this.autonomie});
|
||
|
||
static const none = EnergyRatios();
|
||
}
|
||
|
||
/// **SEAM UNIQUE — INTERIM (§2.1).**
|
||
///
|
||
/// Tout le calcul app-side des ratios vit **ici et nulle part ailleurs**.
|
||
/// Cible Phase 2 : remplacer le corps de [compute] par la lecture d'**UN** state
|
||
/// unique de l'energymanager → le swap se fait en **un seul endroit**.
|
||
///
|
||
/// Méthode interim (§2.1 / §8.1) : dériver 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.
|
||
///
|
||
/// ```
|
||
/// autonomie = (Δ totalConsumption − Δ totalAcquisition) / Δ totalConsumption
|
||
/// autoconsommation = (Δ totalProduction − Δ totalReturn) / Δ totalProduction
|
||
/// ```
|
||
class EnergyRatiosInterim {
|
||
// Baseline = cumuls au début de la période courante (minuit local / reseed).
|
||
double? _baseProduction;
|
||
double? _baseReturn;
|
||
double? _baseConsumption;
|
||
double? _baseAcquisition;
|
||
int? _baseDay; // index de jour local
|
||
|
||
/// Réinitialise la baseline (ex. switch d'installation, §Étape 8).
|
||
void reset() {
|
||
_baseProduction = null;
|
||
_baseReturn = null;
|
||
_baseConsumption = null;
|
||
_baseAcquisition = null;
|
||
_baseDay = null;
|
||
}
|
||
|
||
/// Calcule les ratios à partir des **cumuls** courants (mêmes unités en entrée,
|
||
/// elles s'annulent dans le ratio). `now` sert à détecter le changement de jour.
|
||
EnergyRatios compute({
|
||
required double totalProduction,
|
||
required double totalReturn,
|
||
required double totalConsumption,
|
||
required double totalAcquisition,
|
||
required DateTime now,
|
||
}) {
|
||
final day = _localDay(now);
|
||
|
||
// Reseed si : 1er appel, nouveau jour local, OU compteur **non monotone**
|
||
// (Δ<0 → restart nymead / re-add thing / rollover). Jamais de ratio négatif.
|
||
final nonMonotone = _baseProduction != null &&
|
||
(totalProduction < _baseProduction! ||
|
||
totalReturn < _baseReturn! ||
|
||
totalConsumption < _baseConsumption! ||
|
||
totalAcquisition < _baseAcquisition!);
|
||
|
||
if (_baseDay != day || _baseProduction == null || nonMonotone) {
|
||
_baseDay = day;
|
||
_baseProduction = totalProduction;
|
||
_baseReturn = totalReturn;
|
||
_baseConsumption = totalConsumption;
|
||
_baseAcquisition = totalAcquisition;
|
||
}
|
||
|
||
final dProduction = totalProduction - _baseProduction!;
|
||
final dReturn = totalReturn - _baseReturn!;
|
||
final dConsumption = totalConsumption - _baseConsumption!;
|
||
final dAcquisition = totalAcquisition - _baseAcquisition!;
|
||
|
||
return EnergyRatios(
|
||
autoconsommation: _ratio(dProduction - dReturn, dProduction),
|
||
autonomie: _ratio(dConsumption - dAcquisition, dConsumption),
|
||
);
|
||
}
|
||
|
||
/// Ratio en % borné [0, 100]. Dénominateur ≤ 0 (nuit sans prod, etc.) → `null`
|
||
/// (n/a) — **jamais de NaN**.
|
||
static double? _ratio(double numerator, double denominator) {
|
||
if (denominator <= 0) return null;
|
||
return (numerator / denominator * 100).clamp(0.0, 100.0);
|
||
}
|
||
|
||
static int _localDay(DateTime t) =>
|
||
DateTime(t.year, t.month, t.day).millisecondsSinceEpoch ~/ 86400000;
|
||
|
||
/// **INTERIM (§2.2)** — bande d'autoconsommation **instantanée** (W).
|
||
/// Formule canonique unique : `max(production − max(export, 0), 0)`.
|
||
/// Réseau net-signé (négatif = export). **Aucun `.abs()`, aucun flip de signe.**
|
||
static double selfConsumptionPower(double production, double acquisition) {
|
||
final export = acquisition < 0 ? -acquisition : 0.0;
|
||
final self = production - export;
|
||
return self > 0 ? self : 0.0;
|
||
}
|
||
}
|