421 lines
20 KiB
Markdown
421 lines
20 KiB
Markdown
# Spec — support Fronius du plugin SunSpec
|
||
|
||
Critères d'acceptation numérotés, dérivés du contrat de conception Fronius
|
||
(document interne, non versionné ici) et du brief de lot 0.
|
||
|
||
Deux natures de critères, à ne jamais mélanger dans un même commit :
|
||
|
||
| Bloc | Nature | Commits |
|
||
|---|---|---|
|
||
| **AC-1 … AC-4** | correctifs d'anomalies présentes dans nymea 1.15.2 amont | message en anglais, aucune référence ETM / Fronius / PowerSync — ils ont vocation à être proposés en amont |
|
||
| **AC-5 … AC-15** | spécifique ETM (vendor `fronius`) | commits séparés, préfixe libre |
|
||
|
||
Statut des critères : voir `## Tableau de statut` en fin de document.
|
||
|
||
---
|
||
|
||
## Invariants applicables à toute l'implémentation
|
||
|
||
Ces règles ne sont pas des critères testables un à un ; elles conditionnent
|
||
chaque AC ci-dessous.
|
||
|
||
- **INV-1 — aucune adresse de registre en dur.** Toute lecture et toute écriture
|
||
passent par la découverte dynamique puis par les offsets du modèle. Le nombre
|
||
de blocs répétitifs du modèle 160 décale l'adresse absolue du modèle 124, et
|
||
sans batterie le modèle 124 est absent : une adresse absolue n'est jamais
|
||
valide durablement. Les adresses des documentations constructeur servent à
|
||
valider un dump, jamais à adresser.
|
||
- **INV-2 — `libnymea-sunspec` n'est jamais modifiée.** Tout vit dans
|
||
`sunspec/`. Voir `sunspec/VENDORED.md`.
|
||
- **INV-3 — une consigne écrite est relue.** Plusieurs onduleurs acceptent une
|
||
écriture puis l'ignorent sans code d'exception Modbus.
|
||
- **INV-4 — la régulation se ferme sur la puissance mesurée**, jamais sur la
|
||
consigne émise.
|
||
- **INV-5 — les scale factors ne sont pas statiques.** Ils changent avec le
|
||
firmware et en fonctionnement (auto-scale). Relire le SF concerné
|
||
immédiatement avant conversion et avant écriture.
|
||
|
||
---
|
||
|
||
## Bloc 0 — correctifs upstream
|
||
|
||
### AC-1 — plage des consignes de charge/décharge (anomalie A1)
|
||
|
||
`chargingRate` et `dischargingRate` sont déclarés `minValue: 0, maxValue: 100`
|
||
dans `integrationpluginsunspec.json`. La plage réelle des points `InWRte` /
|
||
`OutWRte` du modèle 124 est **−100,00 … +100,00** (`int16`, SF −2).
|
||
|
||
**Critère.** Dans `integrationpluginsunspec.json`, les deux states
|
||
`chargingRate` et `dischargingRate` de la thingClass `sunspecStorage` ont
|
||
`minValue == -100` et `maxValue == 100`.
|
||
|
||
**Pourquoi c'est bloquant.** Avec la borne basse à 0, une consigne de charge
|
||
forcée (valeur négative, convention de signe du constructeur) est structurellement
|
||
inexprimable : nymea rejette le paramètre d'action avant qu'il n'atteigne le
|
||
plugin.
|
||
|
||
---
|
||
|
||
### AC-2 — cohérence lecture/écriture des consignes (anomalie A2)
|
||
|
||
`onStorageBlockUpdated()` alimente `chargingRate` depuis `wChaGra()` et
|
||
`dischargingRate` depuis `wDisChaGra()`. Ce sont les **gradients**
|
||
(`% WChaMax/s`), en lecture seule sur GEN24. Les actions, elles, écrivent dans
|
||
`InWRte` / `OutWRte` (`% WChaMax`). Le state relu n'est donc pas la grandeur
|
||
écrite : l'utilisateur écrit 50, relit tout autre chose.
|
||
|
||
**Critère.**
|
||
1. `chargingRate` est alimenté depuis `inWRte()`.
|
||
2. `dischargingRate` est alimenté depuis `outWRte()`.
|
||
3. Les gradients restent exposés, mais sous des noms non ambigus :
|
||
`chargeGradient` ← `wChaGra()`, `dischargeGradient` ← `wDisChaGra()`,
|
||
en lecture seule.
|
||
|
||
---
|
||
|
||
### AC-3 — réconciliation de `enableDischarging` (anomalie A3)
|
||
|
||
`onStorageBlockUpdated()` réconcilie `enableCharging` depuis
|
||
`storCtlMod().testFlag(Storctl_modCharge)` mais ne fait rien pour
|
||
`enableDischarging`. Le state reste donc figé sur sa dernière valeur écrite, et
|
||
diverge silencieusement de l'onduleur dès que celui-ci change d'état seul.
|
||
|
||
**Critère.** `enableDischarging` est alimenté depuis
|
||
`storCtlMod().testFlag(Storctl_modDiScharge)`, symétriquement à
|
||
`enableCharging`.
|
||
|
||
---
|
||
|
||
### AC-4 — écriture de `InOutWRte_RvrtTms` (anomalie A4)
|
||
|
||
Le setter `setInOutWRteRvrtTms()` existe dans `libnymea-sunspec` mais n'est
|
||
jamais appelé. `RvrtTms` est le délai au bout duquel l'onduleur abandonne la
|
||
consigne et reprend son comportement natif. À 0 — le défaut — une consigne reste
|
||
active indéfiniment, y compris si le HEMS s'arrête, perd le réseau ou plante.
|
||
|
||
**Critère.**
|
||
1. Une valeur `revertTime` est configurable par thing (paramètre de la
|
||
thingClass `sunspecStorage`), en secondes.
|
||
2. Toute séquence de consigne (`InWRte`, `OutWRte`, `StorCtlMod`, `ChaGriSet`)
|
||
se termine par une écriture de `InOutWRte_RvrtTms` à cette valeur.
|
||
3. `RvrtTms` est écrit **en dernier** dans la séquence.
|
||
4. Défaut du vendor `sunspec` générique : **0** (comportement amont inchangé).
|
||
Défaut du vendor `fronius` : **300** (voir AC-10).
|
||
|
||
**Plage.** 0 – 28800 s.
|
||
|
||
---
|
||
|
||
## Bloc 1 — vendor `fronius`, structure
|
||
|
||
### AC-5 — filtre fabricant à la découverte
|
||
|
||
**Critère.** Pour `froniusConnectionThingClassId`, `discoverThings()` ne retient
|
||
que les résultats dont au moins un modèle Common porte un nom de fabricant
|
||
contenant `Fronius`, insensible à la casse (`hasManufacturer()` fait déjà un
|
||
`contains()` insensible à la casse). Les autres résultats sont ignorés, sans
|
||
créer de `ThingDescriptor`.
|
||
|
||
Symétriquement, un GEN24 ne doit pas être proposé deux fois : le vendor
|
||
générique `sunspecConnection` continue de le proposer (comportement amont
|
||
inchangé) ; c'est l'utilisateur qui choisit la thingClass qu'il appaire.
|
||
|
||
---
|
||
|
||
### AC-6 — ordre des octets
|
||
|
||
**Critère.** Une connexion `fronius` utilise `ByteOrderLittleEndian`, c'est-à-dire
|
||
le défaut SunSpec, à la découverte comme à la connexion.
|
||
|
||
Le `ByteOrderBigEndian` forcé du vendor `solarEdge` est une anomalie propre à
|
||
SolarEdge et ne doit pas être étendue.
|
||
|
||
> **Inférence, pas fait mesuré.** L'ordre des octets du GEN24 n'est documenté
|
||
> nulle part dans la documentation constructeur consultée ; on suppose le défaut
|
||
> SunSpec. À confirmer au banc (phase 2) puis sur matériel (phase 3).
|
||
|
||
---
|
||
|
||
### AC-7 — paramètres de la thingClass `froniusConnection`
|
||
|
||
**Critère.** La thingClass `froniusConnection` existe, sous un bloc `vendors`
|
||
→ `fronius`, et porte exactement :
|
||
|
||
| Paramètre | Défaut | Contrainte |
|
||
|---|---|---|
|
||
| `address` | — | comme `sunspecConnection` |
|
||
| `hostName` | — | comme `sunspecConnection` |
|
||
| `macAddress` | — | comme `sunspecConnection` |
|
||
| `port` | 502 | — |
|
||
| `slaveId` | **1** | fixe en TCP sur GEN24 : masqué ou en lecture seule |
|
||
| `meterSlaveId` | **200** | plage 0 – 247, `advanced`, **0 = pas de compteur sur cette connexion** |
|
||
|
||
Aucun paramètre `endianness` n'est exposé (cf. AC-6 : la valeur est imposée).
|
||
|
||
---
|
||
|
||
### AC-8 — plancher de cadence d'interrogation
|
||
|
||
Le support constructeur recommande un intervalle d'interrogation supérieur à
|
||
**7 secondes** pour les applications tierces. En deçà, la surcharge de
|
||
communication peut faire passer l'onduleur hors ligne vis-à-vis du portail
|
||
constructeur ; le rétablissement exige une coupure AC+DC d'environ une minute
|
||
**sur site**.
|
||
|
||
**Critère.**
|
||
1. Défauts du vendor `fronius` : `updateInterval = 10` s, `timeout = 2000` ms,
|
||
`numberOfRetries = 3`.
|
||
2. **Garde-fou dur** : toute valeur d'intervalle strictement inférieure à 7 s
|
||
configurée sur une connexion `fronius` est remontée à 7 s, avec un
|
||
`qCWarning` explicite. Ce n'est pas une valeur par défaut modifiable : c'est
|
||
un plancher dans le code.
|
||
3. Le plancher s'applique aussi en debug.
|
||
4. Le vendor `sunspec` générique et le vendor `solarEdge` ne sont pas affectés.
|
||
|
||
---
|
||
|
||
### AC-9 — registres interdits en écriture
|
||
|
||
Sur GEN24, ces écritures sont acceptées puis **ignorées sans code d'exception
|
||
Modbus** — l'échec est invisible côté HEMS :
|
||
`WChaMax`, `WChaGra`, `WDisChaGra`, `InOutWRte_WinTms`, `InOutWRte_RmpTms`,
|
||
`VAChaMax` (dont le scale factor n'est de surcroît pas supporté).
|
||
|
||
**Critère.** Sur une connexion `fronius`, les setters correspondants
|
||
(`setWChaMax`, `setWChaGra`, `setWDisChaGra`, `setInOutWRteWinTms`,
|
||
`setInOutWRteRmpTms`, `setVAChaMax`) ne sont **jamais** appelés. Une tentative
|
||
est refusée avant émission, avec un `qCWarning` nommant le registre.
|
||
|
||
---
|
||
|
||
### AC-10 — `revertTime` par défaut à 300 s sur `fronius`
|
||
|
||
**Critère.** La thingClass `froniusConnection` impose `revertTime = 300` s aux
|
||
things `sunspecStorage` créés sous elle, là où le vendor générique laisse 0
|
||
(AC-4.4).
|
||
|
||
> **Hypothèse de travail — décision D4 non validée.** Le contrat recommande
|
||
> `RvrtTms = 300 s` à chaque consigne, avec rafraîchissement à chaque cycle
|
||
> d'optimisation, plutôt que le choix inverse (ne rien écrire) fait par evcc.
|
||
> Le raisonnement retenu : ce qui est acceptable pour un logiciel auto-hébergé
|
||
> ne l'est pas pour un produit installé sous la responsabilité d'ETM — une
|
||
> consigne persistante après un crash du HEMS immobilise la batterie du client.
|
||
> **Prérequis non levé :** la sémantique du timer est décrite dans le chapitre
|
||
> général du manuel et illustrée sur le modèle 123, pas sur le 124. Elle doit
|
||
> être vérifiée expérimentalement sur le modèle 124 avant d'être figée. Si la
|
||
> vérification échoue : replier sur `RvrtTms = 0` + watchdog applicatif côté
|
||
> HEMS.
|
||
|
||
---
|
||
|
||
## Bloc 2 — chaîne de pilotage batterie
|
||
|
||
### AC-11 — correspondance intention HEMS → registres
|
||
|
||
Convention de signe du constructeur : **négatif = charge, positif = décharge**.
|
||
|
||
**Critère.** Les six modes produisent exactement cette séquence de registres :
|
||
|
||
| Mode | `ChaGriSet` | `StorCtl_Mod` | `InWRte` | `OutWRte` | `RvrtTms` |
|
||
|---|---|---|---|---|---|
|
||
| `NORMAL` | — | 0 | — | 100 | 300 |
|
||
| `NO_DISCHARGE` | — | 2 | — | 0 | 300 |
|
||
| `NO_CHARGE` | — | 1 | 0 | — | 300 |
|
||
| `FROZEN` | — | 3 | 0 | 0 | 300 |
|
||
| `FORCE_CHARGE(p %)` | 1 | 2 | — | **−p** | 300 |
|
||
| `LIMIT(c %, d %)` | — | 3 | c | d | 300 |
|
||
|
||
`—` = registre non écrit pour ce mode.
|
||
|
||
**Ordre d'écriture imposé :** `ChaGriSet` → `StorCtl_Mod` → `InWRte` / `OutWRte`
|
||
→ `RvrtTms` en dernier.
|
||
|
||
`StorCtl_Mod` est un bitfield : bit 0 = CHARGE, bit 1 = DISCHARGE.
|
||
`ChaGriSet` : 0 = PV, 1 = GRID.
|
||
|
||
> Les exemples du manuel constructeur affichent des fenêtres de puissance dont
|
||
> les signes sont incohérents avec la convention annoncée. Les **valeurs de
|
||
> registres** de ces exemples sont correctes, les fenêtres annotées ne le sont
|
||
> pas : la table ci-dessus est construite sur les valeurs de registres.
|
||
|
||
---
|
||
|
||
### AC-12 — relecture du scale factor avant écriture
|
||
|
||
**Critère.** `InOutWRte_SF` est relu immédiatement avant chaque écriture de
|
||
`InWRte` / `OutWRte`. Une valeur de SF mise en cache lors d'un cycle
|
||
d'interrogation antérieur n'est pas réutilisée.
|
||
|
||
Motif (INV-5) : le constructeur avertit que les SF changent avec le firmware
|
||
**et en fonctionnement**. Un SF auto-scalé entre la lecture et l'écriture
|
||
produit une consigne d'un facteur 10 à côté.
|
||
|
||
---
|
||
|
||
### AC-13 — vérification par relecture et state `controlHealthy`
|
||
|
||
**Critère.**
|
||
1. Après chaque séquence de consigne, `StorCtl_Mod`, `InWRte`, `OutWRte` et
|
||
`ChaGriSet` sont relus et comparés aux valeurs écrites.
|
||
2. En cas de divergence : `qCWarning` explicite nommant le registre, la valeur
|
||
écrite et la valeur relue.
|
||
3. Un state `controlHealthy` (bool) passe à `false` en cas de divergence, à
|
||
`true` après une séquence vérifiée conforme.
|
||
|
||
C'est la seule parade aux échecs silencieux du §5 du contrat (pilotage Modbus
|
||
non autorisé, charge réseau non autorisée, restriction d'IP, assistant de
|
||
tarification qui écrase les consignes).
|
||
|
||
---
|
||
|
||
## Bloc 3 — modèle 160 (MPPT et blocs batterie)
|
||
|
||
### AC-14 — identification des blocs par `IDStr`, jamais par index
|
||
|
||
Les blocs répétitifs du modèle 160 portent les chaînes `MPPT 1`, `MPPT 2`,
|
||
`StCha 3`, `StDisCha 4`. Sur un onduleur à 3 ou 4 MPPT, les blocs batterie ne
|
||
sont **pas** en position 3 et 4.
|
||
|
||
**Critère.**
|
||
1. Le classement d'un bloc en « string PV » ou « batterie » se fait sur
|
||
`inputIdSting()`, jamais sur l'index du bloc.
|
||
2. Un bloc dont `IDStr` commence par `MPPT` est un string PV.
|
||
3. Un bloc dont `IDStr` commence par `StCha` est le bloc batterie en charge ;
|
||
`StDisCha`, le bloc batterie en décharge.
|
||
4. Un `IDStr` inconnu n'est pas classé et ne contribue à aucun agrégat ; il est
|
||
signalé par un `qCWarning`.
|
||
|
||
Le préfixe est comparé de façon insensible à la casse et après suppression des
|
||
espaces de bord. **Attention à l'ordre du test** : `StDisCha` contient
|
||
`StCha` comme sous-chaîne uniquement si on teste par `contains()` — le test doit
|
||
donc porter sur le préfixe, et `StDisCha` doit être testé avant `StCha` si une
|
||
implémentation par `startsWith` en cascade est retenue.
|
||
|
||
---
|
||
|
||
### AC-15 — agrégats de puissance issus du modèle 160
|
||
|
||
`DCW` est un `uint16`, donc toujours positif : le signe est porté par le bloc
|
||
actif, le bloc inactif étant à zéro.
|
||
|
||
**Critère.**
|
||
1. Puissance PV = Σ `DCW` des blocs classés « string PV ».
|
||
2. Puissance batterie = `DCW[StDisCha]` − `DCW[StCha]`, **positif = décharge**.
|
||
3. `DCW_SF` est partagé par tous les modules et auto-scalé : il est relu avant
|
||
conversion (INV-5).
|
||
4. **Aucun calcul de production PV par différence AC/DC ± batterie.** Le GEN24
|
||
expose la puissance PV réelle string par string ; répliquer le contournement
|
||
du vendor `solarEdge` — qui existe parce que cet onduleur n'expose pas le
|
||
modèle 160 — serait une régression.
|
||
|
||
---
|
||
|
||
## Hypothèses de travail — points non tranchés
|
||
|
||
Chacun de ces points est traité selon l'hypothèse indiquée, faute de
|
||
confirmation humaine au moment de l'implémentation.
|
||
|
||
| # | Point | Statut au contrat | Hypothèse retenue |
|
||
|---|---|---|---|
|
||
| H-1 | Architecture multi-unit-ID (D1) | ⬜ en attente de validation | **Option A′** : un thing `froniusConnection` portant deux `SunSpecConnection` internes (onduleur `slaveId`, compteur `meterSlaveId`). C'est l'option recommandée par le contrat : UX correcte, aucune modification de `libnymea-sunspec`, rebasabilité préservée. |
|
||
| H-2 | Watchdog batterie (D4) | ⬜ à valider | **`RvrtTms = 300 s`** à chaque consigne (AC-10). Prérequis non levé : sémantique du timer à vérifier sur le modèle 124, pas seulement sur le 123. |
|
||
| H-3 | Ordre des octets du GEN24 | inférence explicite du brief | Défaut SunSpec, little-endian (AC-6). Non documenté par le constructeur. |
|
||
| H-4 | Deux connexions TCP simultanées vers le même onduleur | inférence explicite du brief | Non supposé résolu. Le constructeur recommande des requêtes séquentielles ; deux boucles d'interrogation parallèles violeraient potentiellement cette recommandation. **À mesurer au banc**, pas à supposer. En attendant, sérialiser. |
|
||
| H-5 | Assistant de tarification écrasant les consignes | « à confirmer », absent de la doc constructeur | Constat empirique tiers. Traité en message de diagnostic (AC-13.2), pas en logique de pilotage. |
|
||
| H-6 | Ordre des phases du chantier | contrat §8 : la phase 2 ne démarre pas avant que la phase 1 (simulateur) soit verte | **Précondition non vérifiée ici.** Le simulateur `hems-sim` n'a pas été exécuté. Les AC ci-dessous sont validés par tests unitaires hors ligne, ce qui ne remplace pas une validation contre un simulateur reproduisant la sémantique du constructeur. |
|
||
|
||
---
|
||
|
||
## Non vérifiable par les tests unitaires de ce dépôt
|
||
|
||
À porter en phase 2 (banc / simulateur) puis en phase 3 (matériel) :
|
||
|
||
- l'ordre des octets réel du GEN24 (H-3) ;
|
||
- le comportement en double connexion TCP (H-4) ;
|
||
- l'expiration effective de `RvrtTms` sur le modèle 124 (H-2) ;
|
||
- le refus d'écriture par exception Modbus quand le pilotage n'est pas autorisé
|
||
côté onduleur ;
|
||
- l'auto-scale effectif des scale factors en fonctionnement (INV-5) ;
|
||
- le fait que les registres de AC-9 soient bien ignorés silencieusement ;
|
||
- le comportement des blocs batterie du modèle 160 selon la version de firmware
|
||
(avant/après 1.31, `DCV` et `DCWH` reportés en continu ou zérotés à l'arrêt).
|
||
|
||
---
|
||
|
||
## Hors périmètre
|
||
|
||
- modèles 7xx ;
|
||
- Modbus RTU ;
|
||
- onduleurs Verto / Tauro (le mapping MPPT diffère — c'est le motif d'AC-14,
|
||
mais ils ne sont pas testés) ;
|
||
- toute modification de `libnymea-sunspec` (INV-2) ;
|
||
- tout mécanisme de compensation PV à la SolarEdge (AC-15.4) ;
|
||
- anomalies A5 (modèle 160 absent du `switch` de `processDiscoveryResult()`),
|
||
A6 (`MinRsvPct` non exposé) et A7 (`.toInt()` sur des paramètres destinés à
|
||
des setters `float`) : réelles et documentées au contrat, mais hors des quatre
|
||
correctifs demandés pour ce lot.
|
||
|
||
---
|
||
|
||
## Tableau de statut
|
||
|
||
Suite : `sunspec/tests/`, **38 tests, 38 verts**.
|
||
|
||
Build : `qmake6 NYMEA_SUNSPEC_PATH=<sysroot> sunspec/tests/tests.pro && make check`.
|
||
Sans `NYMEA_SUNSPEC_PATH`, `sunspec.pri` résout `libnymea-sunspec` par
|
||
`pkg-config`, ce qui suppose `libnymea-sunspec-dev` installé système.
|
||
|
||
| AC | Objet | Test | Statut |
|
||
|---|---|---|---|
|
||
| AC-1 | plage −100…+100 | `test_AC_1_*` | vert |
|
||
| AC-2 | lecture `inWRte`/`outWRte` | `test_AC_2_*` | vert |
|
||
| AC-3 | `enableDischarging` relu | `test_AC_3_*` | vert |
|
||
| AC-4 | `RvrtTms` écrit en dernier | `test_AC_4_*` | vert |
|
||
| AC-5 | filtre fabricant | `test_AC_5_*` | vert |
|
||
| AC-6 | little-endian | `test_AC_6_*` | vert — mais c'est une **inférence** (H-3), pas une mesure |
|
||
| AC-7 | paramètres `froniusConnection` | `test_AC_7_*` | vert |
|
||
| AC-8 | plancher 7 s | `test_AC_8_*` | vert, avec réserve ci-dessous |
|
||
| AC-9 | registres interdits | `test_AC_9_*` | vert |
|
||
| AC-10 | `revertTime` 300 s | `test_AC_10_*` | vert |
|
||
| AC-11 | table mode → registres | `test_AC_11_*` | vert au niveau du plan ; **aucune action n'expose encore les six modes** |
|
||
| AC-12 | relecture du SF | `test_AC_12_*` | vert |
|
||
| AC-13 | relecture + `controlHealthy` | `test_AC_13_*` | vert |
|
||
| AC-14 | classement par `IDStr` | `test_AC_14_*` | vert ; **non branché** — modèle 160 toujours absent du `switch` (anomalie A5, hors périmètre) |
|
||
| AC-15 | agrégats modèle 160 | `test_AC_15_*` | idem AC-14 |
|
||
|
||
### Réserves à lever
|
||
|
||
- **AC-8** — le minuteur d'interrogation de nymea est **global au plugin**, pas
|
||
par connexion. Le plancher est donc appliqué à l'échelle du plugin dès qu'une
|
||
connexion `fronius` est configurée : c'est plus strict que l'AC, jamais moins.
|
||
Le paramètre `updateInterval` par thing a été **retiré** de
|
||
`froniusConnection` : l'architecture du plugin ne peut pas l'honorer par
|
||
connexion, et un réglage sans effet serait pire que son absence.
|
||
- **AC-11** — la fonction `FroniusRules::planBatteryMode()` produit exactement
|
||
la table du contrat, et le plan est exécutable par
|
||
`IntegrationPluginSunSpec::executeSetpointPlan()`. Il manque l'action nymea
|
||
qui l'appelle : le nommage de cette action et de ses paramètres n'est pas
|
||
tranché par le contrat.
|
||
- **AC-14 / AC-15** — les fonctions de classement et d'agrégation sont écrites
|
||
et testées, mais aucun thing du modèle 160 n'est créé tant que l'anomalie A5
|
||
(`ModelIdMppt` absent du `switch` de `processDiscoveryResult()`) n'est pas
|
||
corrigée. A5 est hors du périmètre des quatre correctifs demandés.
|
||
- **AC-15.4** — respecté par construction : aucun calcul de production PV par
|
||
différence n'a été ajouté.
|
||
|
||
### Multi-unit-ID (H-1)
|
||
|
||
**Non implémenté.** `froniusConnection` porte bien le paramètre `meterSlaveId`
|
||
(AC-7, vert), mais le plugin n'instancie encore qu'une seule `SunSpecConnection`
|
||
par thing. Le passage à deux connexions internes touche `m_sunSpecConnections`,
|
||
`createConnection()`, `setupThing()`, `processDiscoveryResult()`,
|
||
`thingRemoved()`, `postSetupThing()` et `onRefreshTimer()`.
|
||
|
||
Deux raisons de ne pas l'avoir fait dans la foulée : la décision D1 n'est pas
|
||
validée, et le brief demande explicitement de traiter ce lot **isolément**,
|
||
après que le reste soit fonctionnel sur l'onduleur seul. S'y ajoute H-4 : rien
|
||
ne dit que l'onduleur accepte deux connexions TCP simultanées, et le
|
||
constructeur recommande des requêtes séquentielles. Ce point se mesure au banc ;
|
||
le supposer serait exactement le mode de défaillance que le contrat cherche à
|
||
éviter.
|