diff --git a/sunspec/DIVERGENCES.md b/sunspec/DIVERGENCES.md new file mode 100644 index 0000000..bdfdbd6 --- /dev/null +++ b/sunspec/DIVERGENCES.md @@ -0,0 +1,113 @@ +# Divergences — vendor Fronius du plugin SunSpec + +Écarts assumés entre ce que demandent `specs/fronius-sunspec.md` et le contrat +d'une part, et ce que le code fait d'autre part. Chaque entrée dit **pourquoi** +et **comment la lever**. + +Deux étiquettes : + +- **MESURÉ** — l'écart repose sur une observation faite sur du matériel ou sur + le code de la plateforme, reproductible. +- **INFÉRÉ** — l'écart repose sur un raisonnement, faute de donnée. À reprendre + dès qu'une mesure existe. + +--- + +## D-1 — `meterSlaveId` n'est pas marqué `advanced` (MESURÉ) + +**Ce que demande AC-7.** Le paramètre `meterSlaveId` de la thingClass +`froniusConnection` doit être `advanced`. + +**Ce que fait le code.** Rien : le paramètre n'a pas ce drapeau. + +**Pourquoi.** `advanced` n'est pas une propriété de paramType dans nymea 1.15.2. +`ParamType::typeProperties()` énumère exactement `id`, `name`, `displayName`, +`type`, `defaultValue`, `inputType`, `unit`, `minValue`, `maxValue`, `stepSize`, +`allowedValues`, `readOnly`. Le vérificateur de métadonnée refuse tout champ +inconnu : + +``` +Param type "meterSlaveId" has unknown fields: "advanced" +``` + +et `PluginMetadata::isValid()` passe à `false`. Ce n'est pas un paramètre mal +affiché : **le plugin ne se charge plus du tout**, et les erreurs suivantes en +cascade (le paramType est abandonné, donc `froniusConnection` ne satisfait plus +l'interface `networkdevice`) rendent le diagnostic trompeur. + +Vérifié en chargeant le JSON, avec et sans le drapeau, dans un +`PluginMetadata` lié à la `libnymea` installée — pas seulement lu dans une +source amont qui pourrait diverger du paquet. + +**Comment lever.** Rien à faire côté plugin. Il faut soit que nymea ajoute la +propriété, soit que AC-7 soit corrigé. Un test de garde +(`test_F3_meter_slave_id_is_not_marked_advanced`) empêche qu'on ajoute le +drapeau sans refaire la mesure. + +--- + +## D-2 — délai de garde du balayage des enfants orphelins (INFÉRÉ) + +**Contexte.** `processDiscoveryResult()` fait disparaître les things enfants +dont le modèle n'est plus annoncé. Avec deux connexions sous le même parent, +chacune ne voit que la moitié du tableau : balayer sur cette moitié ferait +disparaître les enfants de l'autre à chaque découverte. Le balayage attend donc +que **toutes** les connexions du parent aient réussi une découverte, puis +confronte les enfants à l'union de leurs modèles. + +**L'écart.** Une connexion dont la découverte n'aboutit jamais bloquerait le +balayage pour toujours. Un délai de garde le débloque : +`OrphanSweepGraceSeconds = 600` (10 minutes). + +**Pourquoi cette valeur, et pourquoi elle est INFÉRÉE.** Aucune donnée ne la +fixe. Le choix est asymétrique et volontairement paresseux : un balayage trop +tardif laisse traîner un thing périmé affiché déconnecté, un balayage trop +précoce **supprime un thing de l'utilisateur**. À conséquences inégales, +réglage prudent. 10 minutes est très au-delà de tout cycle de découverte +observé, sans être infini. + +**Comment lever.** Mesurer, sur le banc puis sur matériel, le temps que met une +découverte SunSpec à échouer définitivement sur un unit ID qui ne répond pas +(timeout × retries × nombre de registres de base balayés). Le délai de garde +doit valoir quelques fois cette durée, pas dix minutes arbitraires. La valeur +est un seul `const` dans `integrationpluginsunspec.cpp` ; la changer n'a +d'effet sur rien d'autre. + +--- + +## D-3 — la configuration du plugin écrase les défauts de cadence Fronius (MESURÉ, préexistant) + +**Contexte.** `createConnection()` impose `timeout` et `numberOfRetries` du +vendor Fronius (AC-8) à la création. Mais `onPluginConfigurationChanged()` +réapplique ensuite les valeurs globales du plugin à **toutes** les connexions, +Fronius comprise. + +**L'écart.** Il est antérieur à F3 et affecte déjà la connexion onduleur seule. +F3 ne le corrige pas ; il l'étend symétriquement à la connexion compteur, pour +que les deux connexions d'un même thing se comportent identiquement. Le +plancher d'intervalle d'interrogation d'AC-8, lui, reste appliqué : il vit dans +`effectiveUpdateInterval()`, pas ici. + +**Comment lever.** Hors périmètre de F3 : cela demande de décider si les +réglages globaux du plugin doivent pouvoir passer outre un défaut vendor, ce que +ni le contrat ni la spec ne tranchent. + +--- + +## D-4 — connexion détruite par `ThingSetupInfo::aborted` et laissée dans la table (MESURÉ, préexistant, amont) + +**Contexte.** `setupThing()` relie `ThingSetupInfo::aborted` à +`SunSpecConnection::deleteLater()`, alors que `createConnection()` a déjà inséré +la connexion dans `m_sunSpecConnections`. Un setup avorté laisse donc un +pointeur pendant dans la table ; le setup suivant du même thing prend la branche +« reconfiguration » et appelle `deleteLater()` sur un objet déjà détruit. + +**L'écart.** F3 ne corrige pas ce chemin pour la connexion primaire : il est +commun aux trois vendors, y compris `solarEdge` dont aucun test ne couvre le +comportement, et le périmètre du lot est explicitement « generic et +solarEdgeConnection ne changent pas de comportement ». La connexion **compteur** +ajoutée par F3, elle, se retire proprement de sa table à la destruction. + +**Comment lever.** Relier `QObject::destroyed` au retrait de la table pour les +deux connexions, dans un lot qui assume de toucher le chemin commun, avec une +couverture de test pour `solarEdge`.