[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
093fa09b5e
commit
b06ac15714
@ -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 débranché | Aucune action |
|
||||
| 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) |
|
||||
|
||||
« Maintenu » ≠ « démarré » : le mode dégradé ne force jamais l'activation d'une charge.
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
#include "energyarbitrator.h"
|
||||
#include "adapters/evadapter.h"
|
||||
#include "adapters/ecsrelayadapter.h"
|
||||
#include "adapters/sgreadyadapter.h"
|
||||
#include "scheduler/rulebasedscheduler.h"
|
||||
#include "types/surpluscontext.h"
|
||||
#include "types/plan.h"
|
||||
@ -96,6 +97,18 @@ void EnergyArbitrator::registerEcsAdapter(EcsRelayAdapter *adapter)
|
||||
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)
|
||||
{
|
||||
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)
|
||||
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;
|
||||
}
|
||||
|
||||
@ -191,17 +208,22 @@ void EnergyArbitrator::syncAdapters()
|
||||
void EnergyArbitrator::applyActionsToAdapters(const Slot &slot, const QDateTime &now)
|
||||
{
|
||||
for (const LoadAction &action : slot.actions) {
|
||||
// EV (Setpoint) : dispatché par adjustEvChargers() amont jusqu'à 3g.
|
||||
if (action.kind != LoadAction::Stage)
|
||||
continue;
|
||||
// L'adaptateur applique, écrête et verrouille — il ne décide pas (règle 2).
|
||||
if (action.kind == LoadAction::Stage) {
|
||||
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);
|
||||
if (!adapter) {
|
||||
qCWarning(dcNymeaEnergy()) << "[Arbitre] action Stage sans adaptateur ECS:" << action.loadId;
|
||||
continue;
|
||||
} else if (action.kind == LoadAction::State) {
|
||||
SgReadyAdapter *adapter = m_sgReadyAdapters.value(action.loadId);
|
||||
if (adapter)
|
||||
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.
|
||||
adapter->applyAction(action, now);
|
||||
// EV (Setpoint) : dispatché par adjustEvChargers() amont jusqu'à 3g.
|
||||
}
|
||||
}
|
||||
|
||||
@ -266,6 +288,18 @@ void EnergyArbitrator::applyDegradedMode(const QDateTime &now)
|
||||
ev->setMaxChargingCurrent(ev->maxChargingCurrentMinValue(), now, true);
|
||||
}
|
||||
|
||||
// SG-Ready (état 1) / Batterie (aucune charge réseau) : repli ajouté avec leurs
|
||||
// adaptateurs (3e/3f). Le flag degradedMode + notification client arrivent en 3c-6.
|
||||
// SG-Ready (PAC) : repli en état 2 (NORMAL — mains off), JAMAIS état 1 (blocage).
|
||||
// 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 EcsRelayAdapter;
|
||||
class SgReadyAdapter;
|
||||
class RuleBasedScheduler;
|
||||
|
||||
/*!
|
||||
@ -84,6 +85,13 @@ public:
|
||||
*/
|
||||
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.
|
||||
* \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()
|
||||
* (si \c m_degradedMode actif : retour immédiat — planification/dispatch suspendus, L2)
|
||||
* 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).
|
||||
*/
|
||||
@ -189,8 +197,9 @@ private:
|
||||
void applyDegradedMode(const QDateTime &now);
|
||||
|
||||
RuleBasedScheduler *m_scheduler = nullptr;
|
||||
QHash<QString, EvAdapter *> m_adapters; //!< loadId (ThingId string) → EvAdapter*.
|
||||
QHash<QString, EcsRelayAdapter *> m_ecsAdapters; //!< loadId → EcsRelayAdapter*.
|
||||
QHash<QString, EvAdapter *> m_adapters; //!< loadId (ThingId string) → EvAdapter*.
|
||||
QHash<QString, EcsRelayAdapter *> m_ecsAdapters; //!< loadId → EcsRelayAdapter*.
|
||||
QHash<QString, SgReadyAdapter *> m_sgReadyAdapters; //!< loadId → SgReadyAdapter* (PAC).
|
||||
|
||||
// --- L2 watchdog fraîcheur compteur (SAFETY.md §L2) ---
|
||||
QTimer *m_meterWatchdog = nullptr; //!< Tick 30 s, indépendant des signaux compteur.
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user