feat(rpc): NymeaEnergy.GetOptimizerProtocol sert le contrat embarqué

Le contrat OpenRPC est une ressource Qt du plugin (jamais lu sur disque à
l'exécution) ; GetOptimizerProtocol rend { protocolVersion, schema }, lecture,
PermissionScopeControlThings, aucun secret, sans *Changed (statique pour un
binaire). Documenté dans INTERFACE.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Patrick Schurig 2026-10-17 21:30:44 +02:00
parent b45d0496c6
commit 053263bc38
6 changed files with 81 additions and 0 deletions

View File

@ -53,6 +53,9 @@ SOURCES += \
$$PWD/energyarbitrator.cpp \
$$PWD/spotmarketbuild.cpp \
# Le contrat OpenRPC du protocole optimiseur (docs/), embarqué : NymeaEnergy.GetOptimizerProtocol.
RESOURCES += $$PWD/scheduler/etm-powersync-optimizer-protocol.qrc
# Version du moteur annoncée au handshake de l'optimiseur (OPTIMIZER_PROTOCOL §3, `engine`) : lue
# dans le changelog Debian, seule source de la version livrée — une constante dans le code
# dériverait au premier `+etmN`.

View File

@ -0,0 +1,8 @@
<!DOCTYPE RCC>
<!-- Le contrat OpenRPC du protocole optimiseur, EMBARQUÉ dans le plugin : NymeaEnergy.GetOptimizerProtocol
sert celui du moteur qui tourne, jamais un fichier lu sur disque à l'exécution. -->
<RCC version="1.0">
<qresource prefix="/etm-powersync">
<file alias="optimizer-protocol.openrpc.json">../../../docs/etm-powersync-optimizer-protocol.openrpc.json</file>
</qresource>
</RCC>

View File

@ -3,8 +3,14 @@
#include "optimizerprotocol.h"
#include "../types/ecotarget.h"
#include <QFile>
#include <QJsonDocument>
#include <QJsonObject>
#include <QLoggingCategory>
#include <QStringList>
Q_DECLARE_LOGGING_CATEGORY(dcNymeaEnergy)
namespace OptimizerProtocol {
namespace {
@ -277,6 +283,27 @@ QVariantMap forecastDepuisInstantane(const QVariantMap &instantane)
return out;
}
QByteArray schemaDocumentBytes()
{
QFile f(QString::fromLatin1(SchemaResource));
if (!f.open(QIODevice::ReadOnly))
return QByteArray();
return f.readAll();
}
QVariantMap schemaDocument()
{
QJsonParseError erreur;
const QJsonDocument doc = QJsonDocument::fromJson(schemaDocumentBytes(), &erreur);
if (erreur.error != QJsonParseError::NoError || !doc.isObject()) {
qCWarning(dcNymeaEnergy()).noquote()
<< "[Optimiseur] contrat OpenRPC embarqué ILLISIBLE (" + erreur.errorString()
+ ") — GetOptimizerProtocol rend un schéma vide";
return QVariantMap();
}
return doc.object().toVariantMap();
}
QVariantMap getPlanParams(const SurplusContext &ctx, const ContexteFil &fil)
{
QVariantMap out;

View File

@ -30,6 +30,23 @@ namespace OptimizerProtocol {
//! Version du protocole parlée par ce sérialiseur (§3, §9).
constexpr const char *ProtocolVersion = "1.1";
//! Ressource Qt du contrat OpenRPC embarqué (\c docs/etm-powersync-optimizer-protocol.openrpc.json).
constexpr const char *SchemaResource = ":/etm-powersync/optimizer-protocol.openrpc.json";
/*!
* \brief Le contrat OpenRPC du protocole, tel qu'EMBARQUÉ dans ce binaire.
* \return Les octets de la ressource \c SchemaResource — ceux de \c docs/ au moment du build ;
* vide si la ressource manque (build cassé). Jamais lu sur disque à l'exécution.
*/
QByteArray schemaDocumentBytes();
/*!
* \brief Le contrat OpenRPC analysé, pour \c NymeaEnergy.GetOptimizerProtocol.
* \return L'objet JSON du document ; vide si la ressource manque ou ne s'analyse pas (dit au
* journal, en avertissement, à chaque appel — un contrat qui ne se sert pas ne doit pas se taire).
*/
QVariantMap schemaDocument();
//! Mesure d'une charge, telle que la résout l'arbitre (\c EnergyArbitrator::mesurerCharge).
struct MesureFil {
QString source = QStringLiteral("none"); //!< \c meter | \c device | \c none

View File

@ -36,6 +36,7 @@
#include "etm/spotmarketbuild.h"
#include "etm/types/loadconfig.h"
#include "etm/config/loadconfigstore.h"
#include "etm/scheduler/optimizerprotocol.h" // contrat OpenRPC embarqué : GetOptimizerProtocol
#include <energymanager.h>
@ -651,6 +652,21 @@ NymeaEnergyJsonHandler::NymeaEnergyJsonHandler(SpotMarketManager *spotMarketMana
returns.insert("o:fallbackCode", enumValueName(String));
registerMethod("GetPlan", description, params, returns, Types::PermissionScopeControlThings);
// [ETM] Le contrat OpenRPC du protocole optimiseur, EMBARQUÉ (ressource Qt) : celui du moteur qui
// tourne, jamais un fichier relu sur disque. Lecture, aucun secret — même permission que les
// autres Get* de l'optimiseur. Statique pour un binaire donné : pas de *Changed (invariant 11
// vise un état runtime ; celui-ci ne change qu'avec le paquet).
params.clear(); returns.clear();
description = "The OpenRPC 1.3 document of the engine <-> optimizer protocol spoken by THIS "
"engine (docs/etm-powersync-optimizer-protocol.openrpc.json, embedded at build "
"time). protocolVersion: the version sent at handshake. schema: the document "
"(methods Handshake, GetPlan, UpdateLearned; components.schemas; x-etm-* "
"extensions for transport, fallback machine and semantic rules). No secret. "
"Static for a given binary: no notification.";
returns.insert("protocolVersion", enumValueName(String));
returns.insert("schema", enumValueName(Object));
registerMethod("GetOptimizerProtocol", description, params, returns, Types::PermissionScopeControlThings);
// Notifications
params.clear();
@ -1701,6 +1717,13 @@ JsonReply *NymeaEnergyJsonHandler::GetPlan(const QVariantMap &params)
return createReply({{"inForce", false}});
}
JsonReply *NymeaEnergyJsonHandler::GetOptimizerProtocol(const QVariantMap &params)
{
Q_UNUSED(params)
return createReply({{"protocolVersion", QString::fromLatin1(OptimizerProtocol::ProtocolVersion)},
{"schema", OptimizerProtocol::schemaDocument()}});
}
JsonReply *NymeaEnergyJsonHandler::SetLoadDerogation(const QVariantMap &params)
{
#ifdef ETM_ARBITRATOR

View File

@ -162,6 +162,9 @@ public:
//! \brief [ETM] Le plan en vigueur au dernier cycle (exigence app 2). \param params Aucun.
//! \return \c EnergyArbitrator::publishedPlan() ; \c {inForce: false} sans plan en vigueur.
Q_INVOKABLE JsonReply *GetPlan(const QVariantMap &params);
//! \brief [ETM] Le contrat OpenRPC du protocole optimiseur, embarqué (lot contrat OpenRPC).
//! \param params Aucun. \return \c protocolVersion et \c schema (\c OptimizerProtocol::schemaDocument()).
Q_INVOKABLE JsonReply *GetOptimizerProtocol(const QVariantMap &params);
signals:
void PhasePowerLimitChanged(const QVariantMap &params);