Patrick Schurig 5f49e4ca3c [3b-wip] EnergyArbitrator + RuleBasedScheduler + EvAdapter (dispatch amont, ETM_ARBITRATOR désactivé)
- EnergyArbitrator : public SmartChargingManager — raison documentée dans AGENTS.md §DÉCISIONS DE DESIGN
- SmartChargingManager : protected slots + virtual update() + 3 accesseurs inline [ETM]
- RuleBasedScheduler::getPlan() wraps planSurplusCharging/planSpotMarketCharging, annote chaque action d'un reason français
- EvAdapter : ILoadAdapter concret pour evcharger — applyAction() implémenté, NON appelé en 3b (dispatch via adjustEvChargers() amont, iso-fonctionnel)
- ETM_ARBITRATOR : commenté dans .pro — ne s'active qu'après preuve iso-fonctionnelle (3b-iv)
- Doxygen \brief + invariants + contrats sur toutes les classes/méthodes publiques etm/ (DoD §5)
- plan.h : timeSlots (pas slots, mot-clé Qt) ; commentaire JSON sérialisation "slots" OPTIMIZER_PROTOCOL §6
- .clangd : flags de repli Qt/nymea pour clangd via symlink ~/Schreibtisch/
- compile_commands.json gitignore (chemins absolus locaux)
- Build : 0 erreurs, 0 warnings — libnymea_energypluginnymea.so 914 KB

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-07 23:16:49 +02:00

95 lines
3.1 KiB
C++

// SPDX-License-Identifier: GPL-3.0-or-later
// Copyright (C) 2025 - 2026, Patrick Schurig / ETM PowerSync
#include "evadapter.h"
#include "../energyarbitrator.h"
#include "../../evcharger.h"
#include "../../types/chargingaction.h"
#include "plugininfo.h"
EvAdapter::EvAdapter(EvCharger *evCharger, EnergyArbitrator *parent)
: QObject(parent)
, m_charger(evCharger)
, m_parent(parent)
{
}
LoadDescriptor EvAdapter::descriptor() const
{
LoadDescriptor d;
d.id = m_charger->thing()->id().toString();
d.label = m_charger->name();
d.adapter = QStringLiteral("evcharger");
d.priority = 100;
d.declared.minA = m_charger->maxChargingCurrentMinValue();
d.declared.maxA = m_charger->maxChargingCurrentMaxValue();
d.declared.phases = static_cast<int>(m_charger->phaseCount());
d.limits.chargingEnabledLockS = static_cast<int>(m_charger->chargingEnabledLockDuration());
d.limits.currentChangeLockS = static_cast<int>(m_charger->chargingCurrentLockDuration());
d.supportedKinds = { LoadAction::Setpoint };
return d;
}
LoadTelemetry EvAdapter::telemetry() const
{
LoadTelemetry t;
t.currentPowerW = m_charger->currentPower();
t.available = m_charger->available();
t.lastActionAt = m_lastActionAt;
return t;
}
LoadContext EvAdapter::toLoadContext() const
{
LoadContext ctx;
ctx.id = m_charger->thing()->id().toString();
ctx.adapter = QStringLiteral("evcharger");
ctx.label = m_charger->name();
ctx.declared = descriptor().declared;
ctx.limits = descriptor().limits;
ctx.telemetry.currentPowerW = m_charger->currentPower();
ctx.telemetry.pluggedIn = m_charger->pluggedIn();
ctx.telemetry.charging = m_charger->charging();
return ctx;
}
LoadAction EvAdapter::applyAction(const LoadAction &action)
{
if (action.kind != LoadAction::Setpoint)
return action;
if (action.reason.isEmpty()) {
qCWarning(dcNymeaEnergy()) << "[EvAdapter]" << m_charger->name()
<< "— LoadAction sans reason rejetée.";
return action;
}
const uint minA = m_charger->maxChargingCurrentMinValue();
const uint maxA = m_charger->maxChargingCurrentMaxValue();
const uint clampedA = static_cast<uint>(
qBound(static_cast<double>(minA), action.currentA, static_cast<double>(maxA)));
const uint phases = (m_charger->canSetPhaseCount() && action.phaseCount > 0)
? qBound(1u, action.phaseCount, m_charger->phaseCount())
: m_charger->phaseCount();
const auto issuer = (action.funding == LoadAction::Surplus)
? ChargingAction::ChargingActionIssuerSurplusCharging
: ChargingAction::ChargingActionIssuerTimeRequirement;
ChargingAction ca(action.chargingEnabled, clampedA, phases, issuer, false);
const QDateTime now = QDateTime::currentDateTimeUtc();
m_parent->doExecuteChargingAction(m_charger, ca, now);
m_lastActionAt = now;
LoadAction applied = action;
applied.currentA = clampedA;
applied.phaseCount = phases;
return applied;
}