[3e-4] arbitre : registerSgReadyAdapter + dispatch State + mode dégradé → état 2
registerSgReadyAdapter + m_sgReadyAdapters ; buildContext inclut les PAC ; applyActionsToAdapters dispatche kind==State → m_sgReadyAdapters. Mode dégradé L2 : SG-Ready → état 2 (NORMAL, mains off, force=true), JAMAIS état 1 (blocage). SAFETY.md table L2 corrigée (état 2, pas 1). Build 0/0. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
c5c22bfe19
commit
f7aa7ca7e6
@ -61,7 +61,7 @@ repli **conservatrice** : le repli n'INITIE rien, il borne ce qui tourne déjà.
|
|||||||
| EV branché mais **pas en charge** | Inchangé — reste off (off possiblement volontaire : HC/spot à venir) |
|
| EV branché mais **pas en charge** | Inchangé — reste off (off possiblement volontaire : HC/spot à venir) |
|
||||||
| EV débranché | Aucune action |
|
| EV débranché | Aucune action |
|
||||||
| ECS | Relais coupé (palier 0, `force=true`) |
|
| ECS | Relais coupé (palier 0, `force=true`) |
|
||||||
| SG-Ready PAC | État 1 (normal) |
|
| SG-Ready PAC | État **2** (normal — mains off, la PAC chauffe selon son thermostat). JAMAIS état 1 (blocage) : bloquer une PAC sous compteur muet = maison qui ne chauffe plus sans raison visible. |
|
||||||
| Batterie | Aucune charge réseau (surplus uniquement, plafonné à 0 si compteur muet) |
|
| Batterie | Aucune charge réseau (surplus uniquement, plafonné à 0 si compteur muet) |
|
||||||
|
|
||||||
« Maintenu » ≠ « démarré » : le mode dégradé ne force jamais l'activation d'une charge.
|
« Maintenu » ≠ « démarré » : le mode dégradé ne force jamais l'activation d'une charge.
|
||||||
|
|||||||
@ -4,6 +4,7 @@
|
|||||||
#include "energyarbitrator.h"
|
#include "energyarbitrator.h"
|
||||||
#include "adapters/evadapter.h"
|
#include "adapters/evadapter.h"
|
||||||
#include "adapters/ecsrelayadapter.h"
|
#include "adapters/ecsrelayadapter.h"
|
||||||
|
#include "adapters/sgreadyadapter.h"
|
||||||
#include "scheduler/rulebasedscheduler.h"
|
#include "scheduler/rulebasedscheduler.h"
|
||||||
#include "types/surpluscontext.h"
|
#include "types/surpluscontext.h"
|
||||||
#include "types/plan.h"
|
#include "types/plan.h"
|
||||||
@ -96,6 +97,18 @@ void EnergyArbitrator::registerEcsAdapter(EcsRelayAdapter *adapter)
|
|||||||
qCDebug(dcNymeaEnergy()) << "[EnergyArbitrator] EcsRelayAdapter enregistré:" << adapter->descriptor().label;
|
qCDebug(dcNymeaEnergy()) << "[EnergyArbitrator] EcsRelayAdapter enregistré:" << adapter->descriptor().label;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EnergyArbitrator::registerSgReadyAdapter(SgReadyAdapter *adapter)
|
||||||
|
{
|
||||||
|
const QString id = adapter->descriptor().id;
|
||||||
|
if (m_sgReadyAdapters.contains(id)) {
|
||||||
|
qCWarning(dcNymeaEnergy()) << "[EnergyArbitrator] SgReadyAdapter déjà enregistré:" << id;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
adapter->setParent(this);
|
||||||
|
m_sgReadyAdapters[id] = adapter;
|
||||||
|
qCDebug(dcNymeaEnergy()) << "[EnergyArbitrator] SgReadyAdapter enregistré:" << adapter->descriptor().label;
|
||||||
|
}
|
||||||
|
|
||||||
void EnergyArbitrator::update(const QDateTime ¤tDateTime)
|
void EnergyArbitrator::update(const QDateTime ¤tDateTime)
|
||||||
{
|
{
|
||||||
qCDebug(dcNymeaEnergy()) << "Updating smart charging";
|
qCDebug(dcNymeaEnergy()) << "Updating smart charging";
|
||||||
@ -170,6 +183,10 @@ SurplusContext EnergyArbitrator::buildContext(const QDateTime &now) const
|
|||||||
for (auto it = m_ecsAdapters.constBegin(); it != m_ecsAdapters.constEnd(); ++it)
|
for (auto it = m_ecsAdapters.constBegin(); it != m_ecsAdapters.constEnd(); ++it)
|
||||||
ctx.loads.append(it.value()->toLoadContext(now));
|
ctx.loads.append(it.value()->toLoadContext(now));
|
||||||
|
|
||||||
|
// --- loads[] : SG-Ready adapters (PAC) ---
|
||||||
|
for (auto it = m_sgReadyAdapters.constBegin(); it != m_sgReadyAdapters.constEnd(); ++it)
|
||||||
|
ctx.loads.append(it.value()->toLoadContext(now));
|
||||||
|
|
||||||
return ctx;
|
return ctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -191,17 +208,22 @@ void EnergyArbitrator::syncAdapters()
|
|||||||
void EnergyArbitrator::applyActionsToAdapters(const Slot &slot, const QDateTime &now)
|
void EnergyArbitrator::applyActionsToAdapters(const Slot &slot, const QDateTime &now)
|
||||||
{
|
{
|
||||||
for (const LoadAction &action : slot.actions) {
|
for (const LoadAction &action : slot.actions) {
|
||||||
// EV (Setpoint) : dispatché par adjustEvChargers() amont jusqu'à 3g.
|
// L'adaptateur applique, écrête et verrouille — il ne décide pas (règle 2).
|
||||||
if (action.kind != LoadAction::Stage)
|
if (action.kind == LoadAction::Stage) {
|
||||||
continue;
|
EcsRelayAdapter *adapter = m_ecsAdapters.value(action.loadId);
|
||||||
|
if (adapter)
|
||||||
|
adapter->applyAction(action, now);
|
||||||
|
else
|
||||||
|
qCWarning(dcNymeaEnergy()) << "[Arbitre] action Stage sans adaptateur ECS:" << action.loadId;
|
||||||
|
|
||||||
EcsRelayAdapter *adapter = m_ecsAdapters.value(action.loadId);
|
} else if (action.kind == LoadAction::State) {
|
||||||
if (!adapter) {
|
SgReadyAdapter *adapter = m_sgReadyAdapters.value(action.loadId);
|
||||||
qCWarning(dcNymeaEnergy()) << "[Arbitre] action Stage sans adaptateur ECS:" << action.loadId;
|
if (adapter)
|
||||||
continue;
|
adapter->applyAction(action, now);
|
||||||
|
else
|
||||||
|
qCWarning(dcNymeaEnergy()) << "[Arbitre] action State sans adaptateur SG-Ready:" << action.loadId;
|
||||||
}
|
}
|
||||||
// L'adaptateur applique, écrête et verrouille (anti-rebond) — il ne décide pas.
|
// EV (Setpoint) : dispatché par adjustEvChargers() amont jusqu'à 3g.
|
||||||
adapter->applyAction(action, now);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -266,6 +288,18 @@ void EnergyArbitrator::applyDegradedMode(const QDateTime &now)
|
|||||||
ev->setMaxChargingCurrent(ev->maxChargingCurrentMinValue(), now, true);
|
ev->setMaxChargingCurrent(ev->maxChargingCurrentMinValue(), now, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// SG-Ready (état 1) / Batterie (aucune charge réseau) : repli ajouté avec leurs
|
// SG-Ready (PAC) : repli en état 2 (NORMAL — mains off), JAMAIS état 1 (blocage).
|
||||||
// adaptateurs (3e/3f). Le flag degradedMode + notification client arrivent en 3c-6.
|
// Sous compteur muet on cesse de piloter : la PAC chauffe selon son propre thermostat
|
||||||
|
// (la bloquer = maison qui ne chauffe plus sans raison visible). force=true → bypass minStateHold.
|
||||||
|
for (SgReadyAdapter *adapter : m_sgReadyAdapters) {
|
||||||
|
LoadAction la;
|
||||||
|
la.loadId = adapter->descriptor().id;
|
||||||
|
la.kind = LoadAction::State;
|
||||||
|
la.state = 2;
|
||||||
|
la.force = true;
|
||||||
|
la.reason = reason;
|
||||||
|
adapter->applyAction(la, now);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Batterie (aucune charge réseau) : repli ajouté avec son adaptateur (3f).
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,6 +13,7 @@ class QTimer;
|
|||||||
|
|
||||||
class EvAdapter;
|
class EvAdapter;
|
||||||
class EcsRelayAdapter;
|
class EcsRelayAdapter;
|
||||||
|
class SgReadyAdapter;
|
||||||
class RuleBasedScheduler;
|
class RuleBasedScheduler;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ -84,6 +85,13 @@ public:
|
|||||||
*/
|
*/
|
||||||
void registerEcsAdapter(EcsRelayAdapter *adapter);
|
void registerEcsAdapter(EcsRelayAdapter *adapter);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Enregistre un SgReadyAdapter (PAC) pour inclusion dans le contexte et le dispatch.
|
||||||
|
* \param adapter Adaptateur à enregistrer ; son \c descriptor().id doit être unique.
|
||||||
|
* Adopté comme enfant Qt de l'arbitre. Appelé par le test (setup) ou la config production.
|
||||||
|
*/
|
||||||
|
void registerSgReadyAdapter(SgReadyAdapter *adapter);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief Mode dégradé L2 actif (compteur muet > 90 s) — override de SmartChargingManager.
|
* \brief Mode dégradé L2 actif (compteur muet > 90 s) — override de SmartChargingManager.
|
||||||
* \return \c true tant que les consignes de repli L2 tiennent ; \c false en régime normal.
|
* \return \c true tant que les consignes de repli L2 tiennent ; \c false en régime normal.
|
||||||
@ -125,7 +133,7 @@ protected:
|
|||||||
* 3. verifyOverloadProtection() + verifyOverloadProtectionRecovery()
|
* 3. verifyOverloadProtection() + verifyOverloadProtectionRecovery()
|
||||||
* (si \c m_degradedMode actif : retour immédiat — planification/dispatch suspendus, L2)
|
* (si \c m_degradedMode actif : retour immédiat — planification/dispatch suspendus, L2)
|
||||||
* 4. m_scheduler->getPlan() → log des decisionReason
|
* 4. m_scheduler->getPlan() → log des decisionReason
|
||||||
* 5. applyActionsToAdapters() (ECS Stage) + adjustEvChargers() (EV) → dispatch matériel
|
* 5. applyActionsToAdapters() (ECS Stage + SG-Ready State) + adjustEvChargers() (EV) → dispatch
|
||||||
*
|
*
|
||||||
* \param currentDateTime Instant courant (timer ou simulation).
|
* \param currentDateTime Instant courant (timer ou simulation).
|
||||||
*/
|
*/
|
||||||
@ -189,8 +197,9 @@ private:
|
|||||||
void applyDegradedMode(const QDateTime &now);
|
void applyDegradedMode(const QDateTime &now);
|
||||||
|
|
||||||
RuleBasedScheduler *m_scheduler = nullptr;
|
RuleBasedScheduler *m_scheduler = nullptr;
|
||||||
QHash<QString, EvAdapter *> m_adapters; //!< loadId (ThingId string) → EvAdapter*.
|
QHash<QString, EvAdapter *> m_adapters; //!< loadId (ThingId string) → EvAdapter*.
|
||||||
QHash<QString, EcsRelayAdapter *> m_ecsAdapters; //!< loadId → EcsRelayAdapter*.
|
QHash<QString, EcsRelayAdapter *> m_ecsAdapters; //!< loadId → EcsRelayAdapter*.
|
||||||
|
QHash<QString, SgReadyAdapter *> m_sgReadyAdapters; //!< loadId → SgReadyAdapter* (PAC).
|
||||||
|
|
||||||
// --- L2 watchdog fraîcheur compteur (SAFETY.md §L2) ---
|
// --- L2 watchdog fraîcheur compteur (SAFETY.md §L2) ---
|
||||||
QTimer *m_meterWatchdog = nullptr; //!< Tick 30 s, indépendant des signaux compteur.
|
QTimer *m_meterWatchdog = nullptr; //!< Tick 30 s, indépendant des signaux compteur.
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user