feat: journaliser la divergence, et publier la frontière de période de reporting

DIVERGENCE JOURNALISÉE. `divergent` et `since` ne vivaient que dans la charge
utile de télémétrie et dans un QHash en mémoire — donc nulle part. Rien ne
comptait les épisodes : chaque redémarrage de nymead effaçait l'état, et le hash
est PURGÉ dès que l'écart cesse, si bien qu'une divergence qui s'ouvre et se
referme entre deux lectures ne laissait aucune trace. Un observateur externe
échantillonnant en continu aurait raté exactement les épisodes courts, qui sont
probablement les plus nombreux.

Conséquence : le critère « attendre que les traces parlent » attendait
indéfiniment. Le silence du journal était garanti par construction, pas par
l'absence du phénomène — l'instrument dont le silence ressemble à un résultat.
Deux `qCInfo`, aux deux transitions, avec la durée à la fermeture. Le journal de
.75 est persistant depuis le 2026-08-13 (200 Mo, un mois) : le comptage devient
trivial, et l'étape 4 pourra se décider sur des faits.

FRONTIÈRE DE PÉRIODE PUBLIÉE (LM-1005-b-iv). `System.GetTime` donne un NOM de
fuseau ; un décalage ne s'en déduit pas sans base de fuseaux, et embarquer une
base pour recalculer une frontière que le moteur connaît déjà resterait faux les
jours de changement d'heure, où la journée fait 23 ou 25 heures. Trois clients la
recalculeraient de trois façons, et la seule qui compte est celle qui a servi au
calcul. Même règle que `counts`.

C'est aussi la parade à la troisième nature d'heure murale : la période de
reporting suit l'installation, correctement, mais repose sur la prémisse que le
fuseau de la box SOIT celui de l'installation — et c'était le seul des trois cas
où personne ne vérifiait. `timeZone` rend la vérification possible.

DEUX CHAMPS, PAS UN (LM-1005-b-v), et c'est un ajout à la demande. La baseline se
repose à chaque redémarrage de nymead et à chaque recul de compteur, donc EN
PLEIN JOUR. 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 ferait écrire
« aujourd'hui » sur des chiffres qui ne parlent que de l'après-midi — un silence
de la même famille que celui qu'on ferme. `measuredSince` dit la portée réelle ;
quand les deux coïncident, le cas normal, il n'y a rien à dire de plus, et c'est
ce qui rend l'écart lisible le jour où il apparaît. Même paire, même raison que
`progress.measuredSince` / `targetWh`.

Les trois champs sont TOUJOURS présents, même quand les deux taux sont omis :
« la période a commencé à minuit et rien n'est encore mesurable » est une phrase
vraie et utile, là où une réponse vide ne se distingue pas d'une panne.

INVARIANT 11 au passage : le schéma du Get et celui du Changed étaient déclarés
DEUX fois. Ajouter trois champs à deux endroits est exactement la manière dont
ils divergent — d'où `energyRatiosSchema()`, déclaré une fois et servi aux deux,
comme `loadTelemetrySchema()`.

TEST vu mordre : remplacer `measuredSince` par la frontière — l'implémentation
naïve que la demande aurait pu produire — fait échouer l'assertion sur la repose
en plein jour (attendu 10:00, obtenu 00:00).

Suites : simulation 66/66, loadmodel 23/23, charging 48/48, spotmarket 32/32.
This commit is contained in:
Patrick Schurig 2026-09-05 08:01:16 +02:00
parent be556ef207
commit ae5e8639c4
9 changed files with 234 additions and 9 deletions

View File

@ -1296,6 +1296,54 @@ charge utile, et **seulement la sienne**. Toutes les clés internes sont optionn
---
### Taux d'énergie — `GetEnergyRatios` / `EnergyRatiosChanged`
| champ | présence | sens |
|---|---|---|
| `selfConsumptionRate` | **omis** si n/a | part de la production consommée sur place, % dans [0, 100] |
| `autonomyRate` | **omis** si n/a | part de la consommation couverte sans soutirage, % |
| `periodStartedAt` | **toujours** | epoch (s) du **minuit local** qui ouvre la période de reporting |
| `measuredSince` | **toujours** | epoch (s) où la **baseline courante a été posée** |
| `timeZone` | **toujours** | identifiant IANA du fuseau où la frontière a été construite |
Les trois derniers sont présents **même quand les deux taux sont omis** : « la période a commencé
à minuit et rien n'est encore mesurable » est une phrase vraie et utile, là où une réponse vide ne
se distingue pas d'une panne.
> #### Pourquoi la frontière est PUBLIÉE et non recalculée
>
> `System.GetTime` publie un **nom IANA**. Un décalage ne s'en déduit pas sans base de fuseaux, et
> embarquer une base pour recalculer une frontière que le moteur connaît déjà serait le mauvais
> geste — le recalcul resterait **faux les jours de changement d'heure**, où la journée fait 23 ou
> 25 heures. Trois clients la recalculeraient de trois façons ; la seule qui compte est celle qui
> a servi au calcul. **Même règle que `counts`.**
> #### `periodStartedAt` ≠ `measuredSince`, 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 — donc en
> plein jour. Un jour où nymead a redémarré à 14 h, ces ratios ne couvrent pas la journée : ils
> couvrent **depuis 14 h**.
>
> Avec la seule frontière, l'écran écrirait « aujourd'hui » sur des chiffres qui ne parlent que de
> l'après-midi. Avec la paire, il peut écrire la phrase vraie : **« depuis 14:00 »**. Quand les
> deux coïncident — le cas normal — il n'y a rien à dire de plus, et c'est ce qui rend l'écart
> lisible le jour où il apparaît.
>
> Même paire, même raison que `progress.measuredSince` / `targetWh` de l'avancement éco.
> #### Ce que `timeZone` rend possible, et qui n'existait pas
>
> La période de reporting est la **troisième nature d'heure murale** du système, après l'habitude
> du client et le calendrier réglementé (`LM-1005-b`). Elle suit l'installation, ce qui est
> correct — **mais seulement si le fuseau de la box est celui de l'installation.** Relevé le
> 2026-09-04 : `.75` tourne en `Europe/London`, donc son bilan journalier se remet à zéro à
> **01:00 heure française**, et rien nulle part ne le disait.
>
> Publier le fuseau est ce qui rend cette vérification **possible** — c'est le seul des trois cas
> où personne ne contrôlait quoi que ce soit.
---
### Spot market
#### `NymeaEnergy.GetAvailableSpotMarketProviders`

View File

@ -1,3 +1,22 @@
powersync-energy-plugin-nymea (1.15.2+etm56) trixie; urgency=medium
* Divergence commande/mesure JOURNALISÉE à l'ouverture et à la fermeture
(qCInfo). Elle n'était que publiée dans la télémétrie et tenue dans un QHash
en mémoire : rien ne comptait les épisodes, le hash était purgé dès que
l'écart cessait, et chaque redémarrage effaçait l'état. Le silence du journal
était garanti par construction.
* GetEnergyRatios / EnergyRatiosChanged publient trois champs de plus :
periodStartedAt (epoch du minuit local), measuredSince (epoch de pose de la
baseline) et timeZone (IANA). Toujours présents, même quand les deux taux
sont omis.
* periodStartedAt ≠ measuredSince : la baseline se repose à chaque redémarrage
de nymead, donc en plein jour. Sans la paire, l'écran écrit « aujourd'hui »
sur des chiffres qui ne parlent que de l'après-midi.
* Le schéma des ratios est désormais déclaré UNE fois (energyRatiosSchema),
servi au Get et au Changed — invariant 11.
-- ETM Schurig <dev@etm-schurig.eu> Sat, 05 Sep 2026 09:30:00 +0200
powersync-energy-plugin-nymea (1.15.2+etm55) trixie; urgency=medium
* §10 / LM-1013-b-v : sous le régime `unmeasurable`, la passe éco commande à

View File

@ -1814,11 +1814,33 @@ void EnergyArbitrator::attachCommandDivergence(QVariantMap &entry, const QString
// déphasage d'un cycle entre l'écriture et sa mesure. C'est pourtant TOUTE la différence
// entre un fonctionnement normal et le cas relevé le 2026-08-31, où l'écart durait
// indéfiniment parce que plus personne ne commandait.
if (!divergent)
// JOURNALISÉ AUX DEUX TRANSITIONS, et pas seulement publié.
//
// `divergent` et `since` ne vivaient que dans la charge utile de télémétrie et dans ce
// QHash en mémoire — donc nulle part. Rien ne comptait les épisodes : un redémarrage de
// nymead effaçait l'état, et le hash est PURGÉ dès que l'écart cesse, si bien qu'une
// divergence qui s'ouvre et se referme entre deux lectures ne laissait aucune trace. Même
// un observateur externe échantillonnant en continu aurait raté les épisodes courts, qui
// sont probablement les plus nombreux.
//
// Sans ces deux lignes, « attendre que les traces parlent » attend indéfiniment : le
// silence du journal était garanti par construction, pas par l'absence du phénomène.
if (!divergent) {
if (m_divergenceDepuis.contains(loadId)) {
const qint64 dureeS = m_divergenceDepuis.value(loadId).secsTo(QDateTime::currentDateTime());
qCInfo(dcNymeaEnergy()) << "[Divergence] FIN" << loadId
<< "après" << dureeS << "s"
<< "— attendu" << attenduW << "W, mesuré" << mesureW << "W";
}
m_divergenceDepuis.remove(loadId);
else if (!m_divergenceDepuis.contains(loadId))
m_divergenceDepuis.insert(loadId, m_lastTelemetryPublish.isValid()
? m_lastTelemetryPublish : QDateTime::currentDateTime());
} else if (!m_divergenceDepuis.contains(loadId)) {
const QDateTime debut = m_lastTelemetryPublish.isValid()
? m_lastTelemetryPublish : QDateTime::currentDateTime();
m_divergenceDepuis.insert(loadId, debut);
qCInfo(dcNymeaEnergy()) << "[Divergence] DÉBUT" << loadId
<< "— attendu" << attenduW << "W, mesuré" << mesureW << "W,"
<< "écart" << (mesureW - attenduW) << "W";
}
QVariantMap commande;
commande.insert(QStringLiteral("expectedW"), attenduW);

View File

@ -24,6 +24,8 @@
#include "energyratioscalculator.h"
#include <QTimeZone>
#include <algorithm>
void EnergyRatiosCalculator::reset()
@ -34,6 +36,9 @@ void EnergyRatiosCalculator::reset()
m_baseConsumption = 0.0;
m_baseAcquisition = 0.0;
m_baseDay = 0;
// Sans cette ligne, un `mesureDepuis` périmé survivrait au reset et daterait la mesure
// d'avant l'installation qu'on vient de changer.
m_baselinePosee = QDateTime();
}
EnergyRatiosCalculator::Ratios EnergyRatiosCalculator::compute(double totalProduction,
@ -59,6 +64,9 @@ EnergyRatiosCalculator::Ratios EnergyRatiosCalculator::compute(double totalProdu
m_baseReturn = totalReturn;
m_baseConsumption = totalConsumption;
m_baseAcquisition = totalAcquisition;
// L'instant de pose, distinct de la frontière de journée : un redémarrage de nymead
// repose la baseline en plein jour, et les ratios ne couvrent alors que depuis là.
m_baselinePosee = now;
}
const double dProduction = totalProduction - m_baseProduction;
@ -69,6 +77,11 @@ EnergyRatiosCalculator::Ratios EnergyRatiosCalculator::compute(double totalProdu
Ratios result;
result.autoconsommationValid = ratio(dProduction - dReturn, dProduction, &result.autoconsommation);
result.autonomieValid = ratio(dConsumption - dAcquisition, dConsumption, &result.autonomie);
// La frontière se construit dans le fuseau de `now` — le même que celui qui a servi à
// décider du reseed. En prendre un autre ferait publier une frontière que le calcul
// n'a pas utilisée.
result.debutPeriode = QDateTime(now.date(), QTime(0, 0), now.timeZone());
result.mesureDepuis = m_baselinePosee;
return result;
}

View File

@ -57,6 +57,29 @@ public:
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. */
@ -91,6 +114,8 @@ private:
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;

View File

@ -35,6 +35,7 @@
#include <energymanager.h>
#include <QLoggingCategory>
#include <QTimeZone>
Q_DECLARE_LOGGING_CATEGORY(dcNymeaEnergy)
NymeaEnergyJsonHandler::NymeaEnergyJsonHandler(SpotMarketManager *spotMarketManager, SmartChargingManager *smartChargingManager, LoadConfigStore *loadConfigStore, EnergyManager *energyManager, QObject *parent):
@ -269,8 +270,7 @@ NymeaEnergyJsonHandler::NymeaEnergyJsonHandler(SpotMarketManager *spotMarketMana
description = "Get the current energy ratios (self-consumption / autonomy) in percent, "
"derived from the energy manager's cumulative counters over the current local "
"day. A rate field is omitted (never null) when not available (denominator <= 0).";
returns.insert("o:selfConsumptionRate", enumValueName(Double));
returns.insert("o:autonomyRate", enumValueName(Double));
returns = energyRatiosSchema();
registerMethod("GetEnergyRatios", description, params, returns, Types::PermissionScopeControlThings);
// [ETM] ECS-410 — levée du verrou de défaut d'une charge. Délibérée et journalisée : le
@ -434,8 +434,7 @@ NymeaEnergyJsonHandler::NymeaEnergyJsonHandler(SpotMarketManager *spotMarketMana
params.clear();
description = "Emitted whenever the energy ratios (self-consumption / autonomy) change. "
"A rate field is omitted (never null) when not available.";
params.insert("o:selfConsumptionRate", enumValueName(Double));
params.insert("o:autonomyRate", enumValueName(Double));
params = energyRatiosSchema();
registerNotification("EnergyRatiosChanged", description, params);
if (m_energyManager) {
connect(m_energyManager, &EnergyManager::powerBalanceChanged,
@ -754,6 +753,29 @@ JsonReply *NymeaEnergyJsonHandler::SetLoadConfig(const QVariantMap &params)
// [Phase 2] Construit le map de ratios courant depuis les cumuls de l'energymanager.
// Champ omis (jamais null) quand n/a. Met à jour l'état caché (m_lastRatios).
QVariantMap NymeaEnergyJsonHandler::energyRatiosSchema()
{
QVariantMap champs;
champs.insert("o:selfConsumptionRate", enumValueName(Double));
champs.insert("o:autonomyRate", enumValueName(Double));
// La FRONTIÈRE de la période, publiée plutôt que recalculée par chaque client. Trois
// clients la recalculeraient de trois façons, et la seule qui compte est celle qui a servi
// au calcul. `System.GetTime` donne un nom IANA : un décalage ne s'en déduit pas sans base
// de fuseaux, et le recalcul resterait faux les jours de changement d'heure, où la journée
// fait 23 ou 25 heures. Même règle que `counts`.
champs.insert("periodStartedAt", enumValueName(Uint));
// DEPUIS QUAND on mesure réellement — pas toujours la frontière. La baseline se repose à
// chaque redémarrage de nymead et à chaque recul de compteur ; un jour où nymead a
// redémarré à 14 h, ces ratios ne couvrent que l'après-midi. Sans ce champ, l'écran écrit
// « aujourd'hui » sur des chiffres qui ne parlent pas de la journée. Même paire, même
// raison que `measuredSince` / `targetWh` de l'avancement éco.
champs.insert("measuredSince", enumValueName(Uint));
// Le fuseau dans lequel la frontière a été construite. Il rend la période VÉRIFIABLE : sans
// lui, un bilan journalier découpé au mauvais endroit se lit comme un bilan normal.
champs.insert("timeZone", enumValueName(String));
return champs;
}
QVariantMap NymeaEnergyJsonHandler::currentEnergyRatios()
{
QVariantMap result;
@ -774,6 +796,14 @@ QVariantMap NymeaEnergyJsonHandler::currentEnergyRatios()
result.insert("selfConsumptionRate", r.autoconsommation);
if (r.autonomieValid)
result.insert("autonomyRate", r.autonomie);
// Ces trois-là sont TOUJOURS présents, même quand les deux taux sont omis : « la période a
// commencé à minuit et rien n'est encore mesurable » est une phrase vraie et utile, alors
// qu'une réponse vide ne se distingue pas d'une panne.
result.insert("periodStartedAt", static_cast<qint64>(r.debutPeriode.toSecsSinceEpoch()));
result.insert("measuredSince",
static_cast<qint64>((r.mesureDepuis.isValid() ? r.mesureDepuis : r.debutPeriode)
.toSecsSinceEpoch()));
result.insert("timeZone", QString::fromUtf8(QTimeZone::systemTimeZoneId()));
return result;
}
@ -972,7 +1002,12 @@ void NymeaEnergyJsonHandler::onPowerBalanceChanged()
const bool moved = !hadPrev
|| changed(prev.autoconsommationValid, prev.autoconsommation, now.autoconsommationValid, now.autoconsommation)
|| changed(prev.autonomieValid, prev.autonomie, now.autonomieValid, now.autonomie);
|| changed(prev.autonomieValid, prev.autonomie, now.autonomieValid, now.autonomie)
// La bascule de période et la repose de baseline sont des changements de la charge
// utile : ne pas les notifier laisserait un client connecté sur une frontière périmée
// — le silence ambigu que l'invariant 11 interdit.
|| prev.debutPeriode != now.debutPeriode
|| prev.mesureDepuis != now.mesureDepuis;
if (moved)
emit EnergyRatiosChanged(ratios);

View File

@ -156,6 +156,11 @@ private:
* celui qu'on ne teste pas tous les jours.
*/
static QVariantMap loadTelemetrySchema();
//! \brief Charge utile des ratios — DÉCLARÉE UNE FOIS, servie au \c Get et au \c Changed.
//! Deux déclarations divergent, et c'est l'appel de reprise à froid qui casse (invariant 11).
//! \return Le schéma des champs, prêt pour \c registerMethod et \c registerNotification.
static QVariantMap energyRatiosSchema();
//! \return Charge utile courante ; carte MINIMALE (sans \c timestamp) si aucun cycle
//! d'arbitrage n'a encore eu lieu — cf. \c EnergyArbitrator::loadTelemetry().
QVariantMap currentLoadTelemetry() const;

View File

@ -650,6 +650,29 @@ Seul le second peut coûter de l'argent, et seul le second est **indevinable par
fournisseur. Les fusionner en un unique « les fuseaux diffèrent » ferait disparaître celui des
deux qui compte derrière celui qui se corrige tout seul.
**LM-1005-b-iv — la frontière d'une période de reporting se PUBLIE, elle ne se recalcule pas.**
*(Demandé par l'agent app, posé le 2026-09-05.)*
`GetEnergyRatios` publie `periodStartedAt` (epoch), `measuredSince` (epoch) et `timeZone` (IANA).
**Pourquoi publier plutôt que laisser déduire.** `System.GetTime` donne un NOM de fuseau ; 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. Trois clients la recalculeraient de
trois façons, et la seule qui compte est celle qui a servi au calcul. **Même règle que `counts`.**
**Et c'est la PARADE à la troisième nature.** La période de reporting suit l'installation, ce qui
est correct — mais repose sur la prémisse que le fuseau de la box soit celui de l'installation, et
c'était le seul des trois cas où **personne ne vérifiait**. Publier la frontière et son fuseau est
exactement ce qui rend la vérification possible.
**LM-1005-b-v — `periodStartedAt` et `measuredSince` sont DEUX champs, et les confondre fait
mentir les ratios.** La baseline se repose à chaque redémarrage de nymead et à chaque recul de
compteur — donc en plein jour. Un jour où nymead a redémarré à 14 h, les ratios ne couvrent pas la
journée : ils couvrent depuis 14 h. Avec la seule frontière, l'écran écrit « aujourd'hui » sur des
chiffres qui ne parlent que de l'après-midi. Même paire, même raison que
`progress.measuredSince` / `targetWh`.
**Conséquence de conception** : la passe 1 n'est **pas** bornée par le surplus. Elle est bornée
par une **autorisation de soutirage** — un objet distinct, que le rule-based rendra binaire et
que le MILP rendra continu. La passe 2, elle, reste strictement bornée par le reliquat de

View File

@ -977,6 +977,41 @@ void Simulation::testEnergyRatiosAlignment()
// F — même jour (30) : auto=(600-200)/600≈66.667%, autonomie=(1000-500)/1000=50%.
EnergyRatiosCalculator::Ratios f = calc.compute(2600, 700, 7000, 4500, d30_10);
eqAuto(f, 66.6667); eqAuton(f, 50.0);
// ── LA FRONTIÈRE DE PÉRIODE ET L'INSTANT DE MESURE, qui ne sont PAS le même instant ──
//
// Publier la seule frontière ferait écrire « aujourd'hui » sur des chiffres qui ne parlent
// pas de la journée : la baseline se repose à chaque redémarrage de nymead et à chaque
// recul de compteur, donc en plein jour. C'est la paire qui dit la vérité, pas le champ.
const QDateTime minuit29(QDate(2026, 6, 29), QTime(0, 0));
const QDateTime minuit30(QDate(2026, 6, 30), QTime(0, 0));
// Cas NORMAL — pose à minuit : les deux coïncident, et c'est ce qui rend l'écart lisible
// quand il apparaît.
QCOMPARE(a.debutPeriode, minuit29);
QCOMPARE(a.mesureDepuis, d29_00);
// Sans repose, l'instant de mesure NE BOUGE PAS : il date la baseline, pas le calcul.
QCOMPARE(b.debutPeriode, minuit29);
QCOMPARE(b.mesureDepuis, d29_00);
// REPOSE EN PLEIN JOUR (compteur non monotone à 10:00) — LES DEUX DIVERGENT. C'est le cas
// qui justifie la paire : la frontière dit toujours minuit, la mesure ne couvre que depuis
// 10 h, et un écran qui n'aurait que la frontière l'affirmerait à tort sur la journée.
QCOMPARE(d.debutPeriode, minuit29);
QCOMPARE(d.mesureDepuis, d29_10);
QVERIFY2(d.mesureDepuis > d.debutPeriode,
"une repose en plein jour doit se VOIR : sinon les ratios mentent sur leur portée");
// BASCULE DE JOUR — la frontière suit le nouveau jour, et la mesure date du premier calcul
// APRÈS minuit, jamais de minuit lui-même. Sur une box qui calcule en continu l'écart est
// de quelques secondes ; l'affirmer nul serait une approximation que rien n'oblige à faire.
QCOMPARE(e.debutPeriode, minuit30);
QCOMPARE(e.mesureDepuis, d30_02);
// Et la frontière ne rebouge pas dans la journée.
QCOMPARE(f.debutPeriode, minuit30);
QCOMPARE(f.mesureDepuis, d30_02);
#endif
}