diff --git a/sunspec/DIVERGENCES.md b/sunspec/DIVERGENCES.md index cfef716..3d50d93 100644 --- a/sunspec/DIVERGENCES.md +++ b/sunspec/DIVERGENCES.md @@ -173,3 +173,30 @@ installée porte une tension au repos » est donc **INFÉRÉE des deux côtés** batterie et vérifier `InBatV`/`StorAval` non nuls. **À faire avant tout déploiement** : un discriminateur trop strict ferait disparaître une vraie batterie chez un client. + +--- + +## D-7 — la présence batterie ne peut pas être vérifiée par la suite logique (MESURÉ) + +**Contexte.** La première version du garde de D-6 demandait +`isFroniusChild(thing->id())`, qui résout le parent par `myThings().findById()`. + +**L'écart.** Pendant le setup initial, la connexion parente n'est **pas encore +enregistrée** dans `myThings()` : la recherche répond faux, le garde Fronius est +contourné, et l'enfant storage est créé malgré `InBatV = StorAval = 0`. Trace +nymead correspondante : +`ThingManager: Invalid parent thing id … Not adding thing to the system.` + +Le garde interroge désormais directement le `thing` de connexion que la fonction +tient déjà (`thing->thingClassId() == froniusConnectionThingClassId`), sans +aucune recherche. Même traitement pour le signe compteur, qui utilise le +`parentThing` déjà résolu et non nul. + +**Ce que cela apprend.** La suite `tests/logic` ne peut pas attraper ce défaut : +elle couvre la décision pure (`FroniusRules`), pas le câblage du plugin, c'est à +dire *quel prédicat est interrogé et à quel moment du cycle de vie*. Seul un +essai en boucle fermée contre un simulateur le révèle. Toute modification du +garde de présence doit donc être rejouée sur banc, pas seulement en unitaire. + +**Comment lever.** Étendre `tests/plugin`, qui instancie réellement le plugin, à +un scénario de setup initial avec un modèle 124 à zéro. Hors périmètre ici. diff --git a/sunspec/integrationpluginsunspec.cpp b/sunspec/integrationpluginsunspec.cpp index 94ccdeb..390a5b5 100644 --- a/sunspec/integrationpluginsunspec.cpp +++ b/sunspec/integrationpluginsunspec.cpp @@ -841,8 +841,12 @@ void IntegrationPluginSunSpec::processDiscoveryResult(Thing *thing, SunSpecConne * phantom battery stuck on batteryCritical. Only the Fronius * path is gated; the generic and solarEdge vendors keep their * previous behaviour. */ + /* Asked of the connection thing we already hold, not through + * isFroniusChild(): during the initial setup the parent is not + * registered in myThings() yet, so a lookup answers false and + * the gate would be skipped exactly when it is needed. */ SunSpecStorageModel *storageModel = qobject_cast(model); - if (storageModel && isFroniusChild(thing->id()) + if (storageModel && thing->thingClassId() == froniusConnectionThingClassId && !FroniusRules::hasBattery(storageModel->inBatV(), storageModel->storAval())) { qCDebug(dcSunSpec()) << "Fronius model 124 found but no battery is installed" << "(InBatV" << storageModel->inBatV() @@ -1853,7 +1857,8 @@ void IntegrationPluginSunSpec::onMeterBlockUpdated() /* Sign applied to every power reading of this block. The generic vendors * keep the historical negation; a meter sitting under a Fronius connection * already reports in the nymea convention. See FroniusRules. */ - const double powerSign = FroniusRules::meterPowerSign(isFroniusChild(thing->parentId())); + const double powerSign = FroniusRules::meterPowerSign( + parentThing->thingClassId() == froniusConnectionThingClassId); switch (model->modelId()) { case SunSpecModelFactory::ModelIdMeterSinglePhase: {