feat(délestage §2): le disjoncteur passe par la résolution, au watt près
`phasePowerLimit` devient un `DrawCap{Breaker, perPhaseW}` et
`evPhaseAllowanceW()` traverse `resolveDrawMargin()` — la mécanique du
délestage est branchée, et rien ne bouge. C'est le but : à comportement
constant, tout écart observé ensuite est un défaut, pas une évolution.
L'égalité est obtenue par CONSTRUCTION. `phaseDrawW()` inverse
`calculateAllowanceAmpere()` phase par phase plutôt que de relire les états
du compteur : les lectures amont ne sont pas symétriques entre phases, et
les recopier ici en aurait fait deux versions vouées à diverger.
Deux déplacements sans changement de valeur : l'écrêtage `qMax(0, …)`
descend chez le consommateur (la résolution rend une marge SIGNÉE, dont
l'étape 4 a besoin pour dire de combien délester), et le facteur 230 remonte
dans le plafond, qui s'exprime en watts comme les autres.
Vérification : quatre suites diffées ligne à ligne contre une référence
capturée avant l'édition — charging 48/48 et simulation 50/50 identiques,
loadmodel 21/21, spotmarket 32/32. Contre-épreuve : à 1 V près la suite ne
discrimine pas (consignes en ampères entiers), donc cet essai ne prouve
rien ; à un facteur dix, 7 échecs. Le chemin est vivant.
Au passage, une `\note` du .h affirmait qu'on rend à la borne ce qu'elle
tire déjà. Le code ne l'a jamais fait, et c'est délibérément de sécurité :
sur une phase en dépassement la marge est négative, et c'est ce signe qui
ramène la borne à son minimum.
Décisions 6.1 à 6.3 gravées dans le design, et défaut amont consigné dans
SAFETY.md — non corrigé sciemment, il appartient à un [upstream-fix] isolé.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015F7G5VeaPVSMeVNjiGj36p
This commit is contained in:
parent
e1c1030d6f
commit
b29489bdb5
@ -105,10 +105,36 @@ sèche-linge, tout ce que le HEMS ne pilote pas. La couche L4 garde le dernier m
|
||||
on sait lequel ». Testable **isolément** (`tests/auto/loadmodel`, sans serveur nymea).
|
||||
**Comportement inchangé** : rien ne le consomme encore.
|
||||
|
||||
**Étape 2 — la SOURCE `Breaker` branchée, à comportement CONSTANT.** `phasePowerLimit` devient un
|
||||
`DrawCap{Breaker, perPhaseW}` et `evPhaseAllowanceW()` passe par la résolution. Le résultat doit
|
||||
être **identique au watt près** — c'est ce qui rend l'étape vérifiable : les 46 tests de
|
||||
`charging` ne doivent pas bouger d'une ligne.
|
||||
**Étape 2 — la SOURCE `Breaker` branchée, à comportement CONSTANT. ✅ FAITE (2026-08-30).**
|
||||
`phasePowerLimit` devient un `DrawCap{Breaker, perPhaseW}` et `evPhaseAllowanceW()` passe par
|
||||
`resolveDrawMargin()`, via `drawCaps()` / `phaseDrawW()` / `evDrawMargin()`.
|
||||
|
||||
*Comment l'égalité au watt est obtenue.* `phaseDrawW()` **inverse** `calculateAllowanceAmpere()`
|
||||
phase par phase (`tirage = (limite − marge) × 230`) au lieu de relire les états du compteur.
|
||||
Ce détour n'est pas de la coquetterie : les lectures amont ne sont pas symétriques entre phases
|
||||
(voir `SAFETY.md`, « défaut amont connu »), et les recopier ici en aurait fait deux versions
|
||||
vouées à diverger. L'inversion rend l'égalité vraie **par construction**, quelles que soient ces
|
||||
lectures — y compris si le défaut amont est corrigé plus tard.
|
||||
|
||||
*Deux choses ont changé de place, sans changer de valeur.* L'écrêtage `qMax(0, …)` descend dans
|
||||
`evPhaseAllowanceW()` : la résolution, elle, rend une marge **signée**, parce que l'étape 4 a
|
||||
besoin de l'ampleur du dépassement (décision 6.3). Et le facteur 230 remonte dans le plafond,
|
||||
qui s'exprime désormais en watts comme tous les autres.
|
||||
|
||||
*Vérification.* Quatre suites relancées, verdicts **diffés ligne à ligne** contre une référence
|
||||
capturée avant l'édition : `charging` 48/48 identique, `simulation` 50/50 identique,
|
||||
`loadmodel` 21/21, `spotmarket` 32/32.
|
||||
|
||||
*Contre-épreuve, en deux temps.* Une sortie identique s'obtiendrait aussi si l'ancien chemin
|
||||
survivait et que le nouveau était mort. Perturber le plafond de **1 V** (230 → 229) ne fait
|
||||
rien bouger : la suite ne discrimine pas à ce grain, les consignes étant en ampères entiers.
|
||||
Ce premier essai ne prouve donc rien, et il faut le dire. C'est la perturbation d'un **facteur
|
||||
dix** (230 → 23) qui tranche : **7 échecs** dans `testEcoMode`, `testEcoModePhaseSwitching` et
|
||||
`testBatteryLevelConsideration`. Le chemin est vivant, et c'est bien lui qui décide.
|
||||
|
||||
> À retenir pour la suite : la sensibilité de `charging` à la limite de phase est de l'ordre de
|
||||
> l'ampère, pas du watt. « Identique au watt près » y est donc vérifié comme une identité de
|
||||
> **résultats**, pas de grandeurs intermédiaires — pour celles-ci, c'est l'inversion qui répond.
|
||||
|
||||
**Étape 3 — la cascade est bornée par la marge.** Le waterfall cesse d'allouer au-delà. C'est ici
|
||||
que le comportement change pour les charges non-EV, et c'est ici que le motif `DRAW_CAP` naît
|
||||
@ -123,27 +149,43 @@ réservée aux bornes ; ordre inverse du rang, `force = true`, verrous bypassés
|
||||
|
||||
---
|
||||
|
||||
## 6. CE QUI DEMANDE UNE DÉCISION
|
||||
## 6. CE QUI A ÉTÉ TRANCHÉ (2026-08-30)
|
||||
|
||||
**6.1 — La marge doit-elle tenir compte de ce que le HEMS a lui-même commandé et qui n'est pas
|
||||
encore mesuré ?** C'est la correction A, transposée au soutirage. Sans elle, deux cycles
|
||||
consécutifs peuvent décider d'acheter la même marge deux fois. Avec elle, on réintroduit un
|
||||
terme de latence dans une couche de **sécurité**, où une hypothèse fausse coupe du courant.
|
||||
**Recommandation : NON dans la cascade, OUI nulle part ailleurs** — la cascade borne sur la marge
|
||||
MESURÉE, et l'écart d'un cycle est absorbé par la marge que le disjoncteur garde par
|
||||
construction (`allowancePerPhase -= 1` existe déjà côté proxy). À trancher explicitement.
|
||||
**6.1 — La marge tient-elle compte du commandé non encore mesuré ? → NON.**
|
||||
C'était la correction A transposée au soutirage. Le motif du refus est plus fort que celui de
|
||||
la recommandation initiale : le commandé-non-encore-mesuré, aujourd'hui, **c'est
|
||||
`budget.evReservedW`** — et on vient d'établir (LM-1011, `PlanDraw`) que ce champ mélange deux
|
||||
natures et n'est pas le registre qu'il paraît être. Bâtir une décision de **sécurité** sur le
|
||||
seul champ dont on sait qu'il n'est pas ce qu'il annonce, ce serait construire sur du sable.
|
||||
La cascade borne donc sur la marge **MESURÉE**. L'écart d'un cycle est absorbé par la marge que
|
||||
le disjoncteur garde par construction (`allowancePerPhase -= 1`, côté proxy).
|
||||
|
||||
**6.2 — Que fait le délestage L4 d'une charge sous verrou `minOn` ?** Le repli L2 bypasse les
|
||||
verrous (`force = true`) parce qu'un compteur muet est une panne. Un dépassement de plafond
|
||||
est-il de la même gravité ? **Recommandation : OUI pour `Breaker` et `GridOperator`** — le
|
||||
disjoncteur ne connaît pas le court-cycling, et un ordre du gestionnaire ne se négocie pas.
|
||||
**NON pour `EcoSelfImposed`** : un plafond qu'on s'impose ne justifie pas d'abîmer un
|
||||
compresseur. C'est la première conséquence concrète du fait que la source soit un paramètre.
|
||||
**6.2 — Le délestage L4 bypasse-t-il `minOn` ? → OUI, pour `Breaker` et `GridOperator`
|
||||
seulement.** Le disjoncteur ne connaît pas le court-cycling, et un ordre du gestionnaire de
|
||||
réseau ne se négocie pas. **NON pour `EcoSelfImposed`** : un plafond qu'on s'impose ne justifie
|
||||
pas d'abîmer un compresseur — on renonce au plancher éco plutôt qu'au matériel. C'est la
|
||||
première conséquence concrète du fait que la source soit un paramètre et non une constante.
|
||||
|
||||
**6.3 — Le motif `DRAW_CAP` naît à l'étape 3 : que porte-t-il exactement ?** R8 demande la
|
||||
source. Faut-il aussi `capW`, `marginW`, et la **phase** qui borne quand la source est
|
||||
`Breaker` ? **Recommandation : oui aux trois** — sans la phase, un installateur devant une maison
|
||||
triphasée déséquilibrée ne sait pas où aller mesurer.
|
||||
> **Précaution attachée à 6.2 — le bypass doit SE DIRE dans le motif.** Un verrou franchi en
|
||||
> silence, c'est un compresseur qui court-cycle sans que personne ne sache pourquoi. Le motif
|
||||
> porte donc un marqueur explicite du franchissement : sans lui, la trace montrerait une charge
|
||||
> coupée avant son `minOn` et **rien** n'expliquerait l'écart à la règle, qui passerait pour un
|
||||
> défaut de l'implémentation du verrou. C'est la même doctrine que partout ailleurs ici —
|
||||
> **publier le régime, ne jamais le laisser déduire.**
|
||||
|
||||
**6.3 — Ce que porte le motif `DRAW_CAP` (naissance à l'étape 3) :**
|
||||
|
||||
| Champ | Pourquoi il y est |
|
||||
|---|---|
|
||||
| la **marge** | de combien on manquait — c'est la grandeur qui a décidé |
|
||||
| la **source** | ce qu'on a le droit d'en faire diffère selon elle (cf. 6.2) ; exigence R8 |
|
||||
| l'**ampleur du dépassement** | « on frôle » et « on dépasse de 1200 W » ne se traitent pas pareil, et la marge seule, écrêtée, ne les distingue plus |
|
||||
| la **phase** qui borne | sans elle, un installateur devant une triphasée déséquilibrée ne sait pas où aller mesurer |
|
||||
| le **`minOn` franchi**, le cas échéant | conséquence directe de la précaution ci-dessus |
|
||||
|
||||
Le dépassement est disponible **parce que** `DrawMargin::watts` n'est pas écrêté à zéro :
|
||||
l'écrêtage appartient au consommateur (une borne ne prend jamais une puissance négative), la
|
||||
résolution, elle, dit la vérité (`-1200` veut dire quelque chose que `0` a perdu).
|
||||
|
||||
---
|
||||
|
||||
|
||||
@ -172,6 +172,37 @@ Note : en mode simulation, `simulationCallUpdate()` (ligne 295-299) appelle `upd
|
||||
puis `verifyOverloadProtection()` une seconde fois — double appel intentionnel dans
|
||||
les tests, pas dans la production.
|
||||
|
||||
### Défaut amont connu — `calculateAllowanceAmpere()` est asymétrique entre phases
|
||||
|
||||
**Relevé le 2026-08-30, non corrigé, sciemment.** `rootmeter.cpp` calcule la place libre
|
||||
différemment selon la phase :
|
||||
|
||||
| Phase | Condition testée | Valeur réellement lue |
|
||||
|---|---|---|
|
||||
| A | `hasState("currentPhaseA")` | `currentPhaseA` — **ampères**, correct |
|
||||
| B | `hasState("currentPhaseB")` | `currentPowerPhaseB / 230` — **watts**, et `voltagePhaseB` ignoré |
|
||||
| C | `hasState("currentPhaseC")` | `currentPowerPhaseC / 230` — idem |
|
||||
|
||||
Deux conséquences, d'inégale gravité :
|
||||
|
||||
1. **Bénigne** : sur B et C la tension réelle est ignorée au profit de 230 V, alors que la
|
||||
branche suivante, elle, la lit. Erreur de quelques pour-cent.
|
||||
2. **Sérieuse** : un compteur qui publierait `currentPhaseB` **sans** `currentPowerPhaseB`
|
||||
entre dans la première branche, y lit `0`, et rend une place libre égale au plafond entier.
|
||||
**La protection de surcharge serait alors aveugle sur B et C** — elle n'écrêterait rien,
|
||||
sans rien signaler.
|
||||
|
||||
**Pourquoi ce n'est pas corrigé dans ce lot.** Le mock du banc publie les six états, donc le
|
||||
cas 2 n'y est pas atteint et aucun relevé machine ne le montre. Surtout, le correctif **change
|
||||
le comportement** : il rendrait la protection plus restrictive sur B et C, donc écrêterait des
|
||||
bornes qui ne l'étaient pas. Il appartient à un commit `[upstream-fix]` isolé, avec son propre
|
||||
test, et non à une étape dont tout l'intérêt est d'être à comportement constant.
|
||||
|
||||
**Ce que le moteur en fait en attendant.** `EnergyArbitrator::phaseDrawW()` obtient le tirage
|
||||
par phase en **inversant** cette fonction plutôt qu'en relisant les états. C'est délibéré :
|
||||
recopier les lectures amont ici en ferait deux versions vouées à diverger, et la version ETM
|
||||
« corrigée » masquerait le défaut au lieu de le laisser visible là où il est.
|
||||
|
||||
### Règle de code ETM
|
||||
|
||||
`EnergyArbitrator::update()` appelle `verifyOverloadProtection()` en **position 3**,
|
||||
|
||||
@ -551,35 +551,99 @@ uint EnergyArbitrator::evTargetPhases(EvCharger *ev, double referenceA)
|
||||
return qMax(1u, qBound(pi.minPossiblePhaseCount, phases, pi.maxPossiblePhaseCount));
|
||||
}
|
||||
|
||||
QList<DrawCap> EnergyArbitrator::drawCaps() const
|
||||
{
|
||||
QList<DrawCap> plafonds;
|
||||
|
||||
// 0 = non réglé : la protection de surcharge est désactivée (contrat amont de
|
||||
// SetPhasePowerLimit). Rien à écrêter, et surtout rien à annoncer — un plafond publié
|
||||
// ici serait une limite inventée.
|
||||
const uint limiteA = phasePowerLimit();
|
||||
if (limiteA > 0) {
|
||||
DrawCap disjoncteur;
|
||||
disjoncteur.source = DrawCapSource::Breaker;
|
||||
disjoncteur.perPhaseW = limiteA * 230.0;
|
||||
plafonds.append(disjoncteur);
|
||||
}
|
||||
|
||||
// GridOperator et EcoSelfImposed : aucun émetteur à ce stade. Leur absence de la liste
|
||||
// n'est pas un oubli — c'est l'état du système, et la liste dit lequel des plafonds
|
||||
// existe RÉELLEMENT. Un plafond qu'on n'a pas ne doit pas figurer à zéro (LM-1013).
|
||||
return plafonds;
|
||||
}
|
||||
|
||||
QList<double> EnergyArbitrator::phaseDrawW(Electricity::Phases masque, uint limiteA) const
|
||||
{
|
||||
QList<double> tirages;
|
||||
RootMeter *meter = internalRootMeter();
|
||||
if (!meter || limiteA == 0)
|
||||
return tirages;
|
||||
|
||||
// Masque vide == PhaseNone : le proxy interroge alors LES TROIS phases. Reproduire la
|
||||
// convention plutôt que de renvoyer une liste vide, qui ferait passer « toutes les
|
||||
// phases » pour « aucune contrainte ».
|
||||
const QList<Electricity::Phase> toutes =
|
||||
{ Electricity::PhaseA, Electricity::PhaseB, Electricity::PhaseC };
|
||||
|
||||
for (Electricity::Phase p : toutes) {
|
||||
if (masque != Electricity::PhaseNone && !masque.testFlag(p))
|
||||
continue;
|
||||
|
||||
// Tirage de la phase, en watts, obtenu en INVERSANT calculateAllowanceAmpere plutôt
|
||||
// qu'en relisant les états du compteur. Relire serait dupliquer des lectures qui
|
||||
// diffèrent d'une phase à l'autre côté amont (rootmeter.cpp : la phase A se lit en
|
||||
// ampères, B et C en watts/230), et laisser les deux copies dériver. L'inversion rend
|
||||
// l'égalité au watt VRAIE PAR CONSTRUCTION, quelles que soient ces lectures.
|
||||
const double margeA = meter->calculateAllowanceAmpere(p, limiteA);
|
||||
tirages.append((limiteA - margeA) * 230.0);
|
||||
}
|
||||
return tirages;
|
||||
}
|
||||
|
||||
DrawMargin EnergyArbitrator::evDrawMargin(EvCharger *ev, uint phaseCount) const
|
||||
{
|
||||
DrawMargin sansObjet;
|
||||
if (!ev || phaseCount == 0 || !internalRootMeter())
|
||||
return sansObjet;
|
||||
|
||||
const QList<DrawCap> plafonds = drawCaps();
|
||||
if (plafonds.isEmpty())
|
||||
return sansObjet;
|
||||
|
||||
// MÊME masque que le proxy — la marge d'une borne se juge sur les phases qu'elle occupe,
|
||||
// pas sur celles de la maison. Une monophasée sur A n'a pas à répondre d'une surcharge
|
||||
// sur C, qu'elle ne peut ni causer ni soulager.
|
||||
const Electricity::Phases masque =
|
||||
const_cast<EnergyArbitrator *>(this)->phasesForCount(phaseCount);
|
||||
|
||||
return resolveDrawMargin(plafonds,
|
||||
phaseDrawW(masque, phasePowerLimit()),
|
||||
internalRootMeter()->currentPower());
|
||||
}
|
||||
|
||||
double EnergyArbitrator::evPhaseAllowanceW(EvCharger *ev, uint phaseCount) const
|
||||
{
|
||||
if (!ev || phaseCount == 0)
|
||||
return -1;
|
||||
|
||||
// 0 = non réglé : la protection de surcharge est désactivée (contrat amont de
|
||||
// SetPhasePowerLimit). Rien à écrêter, et surtout rien à annoncer — un plafond publié
|
||||
// ici serait une limite inventée.
|
||||
const uint limite = phasePowerLimit();
|
||||
if (limite == 0)
|
||||
const DrawMargin marge = evDrawMargin(ev, phaseCount);
|
||||
if (!marge.borne)
|
||||
return -1;
|
||||
|
||||
RootMeter *meter = internalRootMeter();
|
||||
if (!meter)
|
||||
return -1;
|
||||
|
||||
// Place LIBRE par phase, telle que la calcule le proxy — même fonction, même masque de
|
||||
// phases, et surtout SANS rendre à la borne ce qu'elle tire déjà.
|
||||
// Place LIBRE offerte à la borne, et surtout SANS lui rendre ce qu'elle tire déjà.
|
||||
//
|
||||
// Ce dernier point est délibéré et il est de sécurité. Rendre sa propre consommation à une
|
||||
// borne serait plus juste en régime établi, mais ferait disparaître la réponse à la
|
||||
// surcharge : sur une phase en dépassement, la place libre est NÉGATIVE, et c'est ce signe
|
||||
// surcharge : sur une phase en dépassement, la marge est NÉGATIVE, et c'est ce signe
|
||||
// qui ramène la borne à son minimum. Avec le retour, elle se verrait autoriser ce qu'elle
|
||||
// consomme déjà — c'est-à-dire précisément la puissance qui cause le dépassement.
|
||||
// (`testOverloadProtectionEcoMode` : 16 A ramenés à 6 A ; avec le retour, 9 A maintenus.)
|
||||
const double allowanceA = meter->calculateAllowanceAmpere(
|
||||
const_cast<EnergyArbitrator *>(this)->phasesForCount(phaseCount), limite);
|
||||
|
||||
return qMax(0.0, allowanceA) * 230.0 * phaseCount;
|
||||
//
|
||||
// L'écrêtage à zéro est fait ICI, chez le consommateur, et non dans la résolution : la
|
||||
// marge PORTE le dépassement (elle vaut -1200 quand on dépasse de 1200 W) parce que
|
||||
// l'étape 4 en a besoin pour dire de combien délester. Ce qu'une borne peut prendre
|
||||
// n'est jamais négatif ; ce qu'il faut retirer à la maison, si.
|
||||
return qMax(0.0, marge.watts) * phaseCount;
|
||||
}
|
||||
|
||||
bool EnergyArbitrator::evCommandedByWaterfall(EvCharger *evCharger) const
|
||||
|
||||
@ -9,6 +9,7 @@
|
||||
// Inclus (et non déclaré en avant) : m_builtFrom stocke des LoadConfig PAR VALEUR — c'est ce
|
||||
// qui permet au rebuild incrémental (ECS-412) de comparer l'ancienne et la nouvelle config.
|
||||
#include "types/loadconfig.h"
|
||||
#include "types/drawcap.h"
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QVariantMap>
|
||||
@ -312,6 +313,50 @@ public:
|
||||
*/
|
||||
double evFloorW(EvCharger *ev, uint phaseCount) const;
|
||||
|
||||
/*!
|
||||
* \brief Plafonds de tirage en vigueur à cet instant.
|
||||
*
|
||||
* \return La liste des \c DrawCap réellement actifs — vide si personne ne borne.
|
||||
*
|
||||
* \par Un seul mécanisme, plusieurs sources
|
||||
* Le disjoncteur, une limite de gestionnaire de réseau et un plancher éco auto-imposé
|
||||
* bornent tous le tirage ; ils ne diffèrent que par ce qu'on a le droit d'en faire.
|
||||
* Les faire cohabiter dans UNE liste évite trois écrêtages concurrents dont le dernier
|
||||
* gagnerait sans qu'on sache lequel a parlé.
|
||||
*
|
||||
* \note Un plafond absent n'est pas un plafond à zéro. La liste ne contient que ce qui
|
||||
* existe, pour qu'\c resolveDrawMargin puisse répondre « sans objet » plutôt que
|
||||
* « 0 W autorisé » — deux choses que rien ne distinguerait ensuite.
|
||||
*/
|
||||
QList<DrawCap> drawCaps() const;
|
||||
|
||||
/*!
|
||||
* \brief Tirage par phase, en watts, tel que le compteur amont le lit.
|
||||
*
|
||||
* \param masque Phases à relever ; \c PhaseNone == les trois (convention amont).
|
||||
* \param limiteA Limite par phase en ampères, nécessaire à l'inversion (voir ci-dessous).
|
||||
* \return Un watt par phase retenue, dans l'ordre A, B, C. Liste vide sans compteur.
|
||||
*
|
||||
* \note La valeur est obtenue en **inversant** \c RootMeter::calculateAllowanceAmpere()
|
||||
* phase par phase, et non en relisant les états. Les lectures amont ne sont pas
|
||||
* symétriques entre phases ; les recopier ici en ferait deux versions vouées à diverger.
|
||||
* L'inversion rend l'égalité au watt vraie par construction.
|
||||
*/
|
||||
QList<double> phaseDrawW(Electricity::Phases masque, uint limiteA) const;
|
||||
|
||||
/*!
|
||||
* \brief Marge de tirage vue par une borne, avec la source qui la borne.
|
||||
*
|
||||
* \param ev Borne. \param phaseCount Nombre de phases VISÉ.
|
||||
* \return La marge retenue — \c borne à faux si aucun plafond n'est en vigueur.
|
||||
*
|
||||
* \note La marge est jugée sur les phases que la borne OCCUPE : une monophasée sur A n'a
|
||||
* pas à répondre d'une surcharge sur C, qu'elle ne peut ni causer ni soulager.
|
||||
* \note La marge peut être **négative** — elle porte alors l'ampleur du dépassement.
|
||||
* L'écrêtage à zéro appartient au consommateur, pas à la résolution.
|
||||
*/
|
||||
DrawMargin evDrawMargin(EvCharger *ev, uint phaseCount) const;
|
||||
|
||||
/*!
|
||||
* \brief [3g-2] Puissance maximale (W) que la protection de surcharge laisse à cette borne.
|
||||
*
|
||||
@ -327,9 +372,12 @@ public:
|
||||
* waterfall pourrait allouer un surplus que le branchement ne supporte pas, et la seule
|
||||
* chose qui rattraperait serait \c verifyOverloadProtection() **après coup**.
|
||||
*
|
||||
* \note Le plafond est un **plafond de charge**, pas une marge : l'allowance du compteur
|
||||
* est la place LIBRE, à laquelle on rend ce que la borne tire déjà — sans quoi une borne
|
||||
* en charge se verrait interdire ce qu'elle consomme déjà.
|
||||
* \note Le plafond est la place LIBRE, et on n'y rend **pas** ce que la borne tire déjà.
|
||||
* C'est délibéré et c'est de sécurité : sur une phase en dépassement la marge est
|
||||
* négative, et c'est ce signe qui ramène la borne à son minimum. Rendre sa propre
|
||||
* consommation lui rendrait exactement la puissance qui cause le dépassement
|
||||
* (`testOverloadProtectionEcoMode`). La formulation inverse figurait ici et était
|
||||
* fausse : le code ne l'a jamais fait.
|
||||
* \note Valeur de CYCLE : elle dépend de ce que le reste de la maison consomme à cet
|
||||
* instant. Elle est donc relue à chaque \c toLoadContext(), jamais mémorisée.
|
||||
*/
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user