fix(sg-ready): ECS-110 sans Q_ASSERT — refus explicite au lieu d'une assertion muette
Les deux Q_ASSERT de SgReadyAdapter (states non vide, état 2 présent) gardaient un invariant de configuration. QT_NO_DEBUG les retire du binaire livré : le jour où la PAC passe à la configuration, ils ne gardent plus rien chez le client. Ils sont donc retirés AVANT ce basculement, remplacés par un drapeau m_usable calculé à la construction : journalisation critique, refus de toute commande, available faux. L'état 2 est le plancher de repli du mode dégradé L2 et de la désactivation (ECS-413, ECS-414) — une PAC qui ne peut pas l'exprimer ne doit pas piloter. Ajoute aussi claimedRelays(), accesseur des contacts revendiqués, consommé par le commit qui branche la fabrique depuis la configuration. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
9b4945bb58
commit
42cb2f080b
@ -33,8 +33,21 @@ SgReadyAdapter::SgReadyAdapter(ThingManager *thingManager,
|
||||
{
|
||||
m_states = m_stateRelays.keys();
|
||||
std::sort(m_states.begin(), m_states.end());
|
||||
Q_ASSERT(!m_states.isEmpty());
|
||||
Q_ASSERT(m_stateRelays.contains(2)); // état 2 (normal) = repli sûr obligatoire
|
||||
|
||||
// ECS-110 — la présence de l'état 2 NE DOIT PAS reposer sur un Q_ASSERT : QT_NO_DEBUG est
|
||||
// défini en release, l'assertion disparaît du binaire livré. Tant que les états venaient
|
||||
// d'une construction codée en dur elle ne pouvait pas échouer ; dès qu'ils viennent de la
|
||||
// configuration, elle serait le SEUL garde-fou sur le repli sûr du mode dégradé L2.
|
||||
//
|
||||
// La vérification vit donc dans LoadConfig::isValid() (refus explicite, message FR) et le
|
||||
// contrôle ci-dessous n'est qu'un dernier filet pour les constructions directes (tests,
|
||||
// enregistrement codé en dur) : il PARLE au lieu de disparaître.
|
||||
m_usable = !m_states.isEmpty() && m_stateRelays.contains(2);
|
||||
if (!m_usable)
|
||||
qCCritical(dcNymeaEnergy()) << "[SgReadyAdapter]" << m_label
|
||||
<< "— encodage INUTILISABLE : l'état 2 (normal) n'est pas"
|
||||
<< "exprimable. C'est le repli sûr du mode dégradé L2 ;"
|
||||
<< "l'adaptateur refusera toute commande.";
|
||||
}
|
||||
|
||||
LoadDescriptor SgReadyAdapter::descriptor() const
|
||||
@ -56,7 +69,7 @@ LoadDescriptor SgReadyAdapter::descriptor() const
|
||||
LoadTelemetry SgReadyAdapter::telemetry() const
|
||||
{
|
||||
LoadTelemetry t;
|
||||
t.available = !m_faulted; // ECS-414 : sort de l'ARBITRAGE, pas de la COMPTABILITÉ.
|
||||
t.available = m_usable && !m_faulted; // ECS-414 : sort de l'ARBITRAGE, pas de la COMPTABILITÉ.
|
||||
t.lastActionAt = m_lastActionAt;
|
||||
// Base du recrédit budget = puissance ALLOUÉE de l'état (déclaré), pas la conso mesurée
|
||||
// (états 1/2 → 0 ; états 3/4 → P3/P4). Cf. invariant 8.
|
||||
@ -82,7 +95,7 @@ LoadContext SgReadyAdapter::toLoadContext(const QDateTime &now) const
|
||||
ctx.limits = descriptor().limits;
|
||||
|
||||
ctx.telemetry.currentPowerW = telemetry().currentPowerW;
|
||||
ctx.telemetry.available = !m_faulted;
|
||||
ctx.telemetry.available = m_usable && !m_faulted;
|
||||
ctx.telemetry.state = m_currentState;
|
||||
ctx.telemetry.lastSwitch = m_lastSwitch;
|
||||
if (m_faulted) {
|
||||
@ -107,6 +120,12 @@ LoadAction SgReadyAdapter::applyAction(const LoadAction &action, const QDateTime
|
||||
return action;
|
||||
}
|
||||
|
||||
if (!m_usable) {
|
||||
LoadAction refused = action;
|
||||
refused.state = m_currentState;
|
||||
return refused; // encodage sans état 2 : aucune commande, cf. constructeur
|
||||
}
|
||||
|
||||
// ECS-414 — en défaut, plus aucune commande, y compris forcée : trois tentatives ont
|
||||
// déjà échoué, en réémettre masquerait l'état sans rien réparer.
|
||||
if (m_faulted) {
|
||||
|
||||
@ -119,6 +119,14 @@ public:
|
||||
*/
|
||||
void applySafeState(const QDateTime &now) override;
|
||||
|
||||
/*! \brief Contacts que cet adaptateur revendique, tous états confondus.
|
||||
*
|
||||
* Sert à détecter qu'une PAC de configuration (LM-300) et une PAC enregistrée en dur
|
||||
* commandent le même matériel — situation où deux adaptateurs se disputeraient les mêmes
|
||||
* contacts et où le budget compterait la charge deux fois.
|
||||
*/
|
||||
QSet<QString> claimedRelays() const { return allRelays(); }
|
||||
|
||||
/*!
|
||||
* \brief État SG-Ready courant (1-4).
|
||||
* \return Dernier état réellement commuté ; valeur initiale tant qu'aucune transition
|
||||
@ -188,6 +196,10 @@ private:
|
||||
int m_stateTarget = 2;
|
||||
Phase m_phase = PhaseNominale;
|
||||
bool m_faulted = false;
|
||||
//! Faux si l'encodage ne permet pas d'exprimer l'état 2 — repli sûr du mode dégradé L2.
|
||||
//! L'adaptateur refuse alors TOUTE commande, et le dit (ECS-110 : jamais un Q_ASSERT,
|
||||
//! qui disparaît du binaire release).
|
||||
bool m_usable = true;
|
||||
QDateTime m_lastSwitch; //!< Dernier changement d'état (null = jamais).
|
||||
QDateTime m_lastActionAt;
|
||||
};
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user