Patrick Schurig c88ba6ad45 [upstream-fix] rootmeter: la tension est locale à chaque phase
`voltage` était déclarée une seule fois hors des trois blocs et mutée par
l'un d'eux : une phase sans tension déclarée était calculée avec celle d'une
autre.

Et le défaut n'était pas celui qu'on croyait — le mesurer l'a montré.
L'interface energymeter rend currentPhaseA et voltagePhaseA OBLIGATOIRES, B
et C optionnels : la branche « puissance ÷ tension » de A est morte par
construction. La fuite n'allait donc pas de A vers B, comme la note d'origine
l'affirmait, mais de B vers C.

Démontré : B à 200 V, C à 4 000 W sans tension déclarée → 5,0 A de marge au
lieu de 7,6. Deux conditions, dont aucune n'était évidente avant d'écrire le
test : le masque doit couvrir B ET C — interroger C seule n'exécute pas le
bloc de B — et C doit être la phase la plus contrainte, sinon le qMin retient
B et masque l'écart. Mes deux premières versions du test passaient pour ces
deux raisons.

Le mock ne savait pas produire le cas : il a fallu meterPowerOnly, avec les
puissances et la tension de B mais sans les courants de B/C ni la tension de
C. Troisième fois qu'ouvrir le mock révèle un défaut plutôt que de seulement
débloquer un test.

draw.bindingPhase dit LAQUELLE des phases borne. perPhaseBound disait qu'une
phase borne, jamais laquelle : l'écran ne pouvait dire que « bridé » sans
dire par quoi. DRAW_CAP la porte aussi mais n'est émis que si une charge est
refusée — un cycle qui borde sans rien refuser laisserait l'écran muet.

Et dans AGENTS.md : une conclusion juste posée sur un raisonnement faux
survit jusqu'à ce qu'on bâtisse dessus, parce que la conclusion PROTÈGE le
raisonnement de l'examen. Un résultat faux se voit ; un résultat juste ferme
la question. Parade : quand une décision est écrite avec sa raison, la raison
mérite son propre contrôle — c'est elle que le lot suivant utilisera.

Suite : simulation 65/65, loadmodel 22/22, charging 48/48 identique à la
référence, spotmarket 32/32, doxygen 0 avertissement.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015F7G5VeaPVSMeVNjiGj36p
2026-09-04 06:02:17 +02:00

593 lines
23 KiB
C++

// SPDX-License-Identifier: GPL-3.0-or-later
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright (C) 2013 - 2024, nymea GmbH
* Copyright (C) 2024 - 2025, chargebyte austria GmbH
*
* This file is part of nymea-energy-plugin-nymea.
*
* nymea-energy-plugin-nymea.s free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* nymea-energy-plugin-nymea.s distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with nymea-energy-plugin-nymea. If not, see <https://www.gnu.org/licenses/>.
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "energytestbase.h"
#include <QTimeZone>
EnergyTestBase::EnergyTestBase(QObject *parent) :
NymeaTestBase(parent),
m_networkAccessManager(new QNetworkAccessManager(this))
{
qCDebug(dcTests()) << "Running in:" << QDir::currentPath();
QString mockPluginEnvPath = QDir(QDir::currentPath() + "/../../mocks/plugins/energymocks/").absolutePath();
qCDebug(dcTests()) << "Mock plugin path:" << mockPluginEnvPath;
QString energyPluginEnvPath = QDir(QDir::currentPath() + "/../../../energyplugin/").absolutePath();
qCDebug(dcTests()) << "Energy plugin path:" << energyPluginEnvPath;
// Add our mock plugin to the loading path
if (QString(qgetenv("NYMEA_PLUGINS_EXTRA_PATH")).isEmpty()) {
qputenv("NYMEA_PLUGINS_EXTRA_PATH", mockPluginEnvPath.toUtf8());
} else {
qputenv("NYMEA_PLUGINS_EXTRA_PATH", QString(QString(qgetenv("NYMEA_PLUGINS_EXTRA_PATH")) + ":" + mockPluginEnvPath).toUtf8());
}
QFileInfo configurationFileInfo(":/energy-manager-configuration.json");
if (configurationFileInfo.exists()) {
qputenv("NYMEA_ENERGY_MANAGER_CONFIG", ":/energy-manager-configuration.json");
}
// Add this enery plugin to the paths
qputenv("NYMEA_ENERGY_PLUGINS_EXTRA_PATH", energyPluginEnvPath.toUtf8());
qCDebug(dcTests()).nospace() << "NYMEA_PLUGINS_EXTRA_PATH=" << qgetenv("NYMEA_PLUGINS_EXTRA_PATH");
qCDebug(dcTests()).nospace() << "NYMEA_ENERGY_PLUGINS_EXTRA_PATH=" << qgetenv("NYMEA_ENERGY_PLUGINS_EXTRA_PATH");
qCDebug(dcTests()).nospace() << "NYMEA_EXPERIENCE_PLUGINS_PATH=" << qgetenv("NYMEA_EXPERIENCE_PLUGINS_PATH");
}
QDateTime EnergyTestBase::utcDateTime(const QDate &date, const QTime &time)
{
#if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0)
return QDateTime(date, time, QTimeZone::UTC);
#else
return QDateTime(date, time, Qt::UTC);
#endif
}
void EnergyTestBase::initTestCase(const QString &loggingRules, bool checkExperienceLoaded, bool disableLogEngine)
{
QDir dir(NymeaSettings::translationsPath());
dir.mkpath(NymeaSettings::translationsPath());
QStringList languages = {"de", "en_US"};
foreach (const QString &language, languages) {
QFile f(NymeaSettings::translationsPath().append("/nymead-" + language + ".qm"));
QVERIFY2(f.open(QFile::WriteOnly), "Could not create translation file.");
f.write(" ");
f.close();
}
// If testcase asserts cleanup won't do. Lets clear any previous test run settings leftovers
QSettings energySettings(NymeaSettings::settingsPath() + "/energy.conf", QSettings::IniFormat);
energySettings.clear();
if (loggingRules.isEmpty()) {
NymeaTestBase::initTestCase("*.info=true\n*.debug=false\nApplication.debug=true\nTests.debug=true\nExperiences.debug=true\nNymeaEnergy.debug=true\nEnergyMocks.debug=true\nDBus.warning=false", disableLogEngine);
} else {
NymeaTestBase::initTestCase(loggingRules, disableLogEngine);
}
QVariant reply = injectAndWait("JSONRPC.Hello");
qCDebug(dcTests()) << qUtf8Printable(QJsonDocument::fromVariant(reply).toJson());
if (checkExperienceLoaded) {
QString experienceVersion = "0.8"; // TODO: Perhaps this should be defined in the project file somewhere
QVERIFY2(reply.toMap().value("params").toMap().contains("experiences"),
QString("No experience plugins loaded!\n%1")
.arg(QString(QJsonDocument::fromVariant(reply).toJson())).toUtf8());
QVariantMap experienceDefinition;
experienceDefinition.insert("name", "NymeaEnergy");
experienceDefinition.insert("version", experienceVersion);
QVERIFY2(reply.toMap().value("params").toMap().value("experiences").toList().contains(experienceDefinition),
QString("NymeaEnergy %1 experience plugins not loaded!\n%2")
.arg(experienceVersion)
.arg(QString(QJsonDocument::fromVariant(reply).toJson())).toUtf8());
enableNotifications({"Integrations", "Energy", "NymeaEnergy"});
}
removeDevices();
}
void EnergyTestBase::cleanupTestCase()
{
removeDevices();
NymeaTestBase::cleanupTestCase();
}
void EnergyTestBase::init()
{
removeDevices();
}
void EnergyTestBase::cleanup()
{
removeDevices();
}
QNetworkReply *EnergyTestBase::setMeterStates(const QVariantMap &phasesPower, bool connected, quint16 port)
{
QUrl requestUrl;
requestUrl.setScheme("http");
requestUrl.setHost("127.0.0.1");
requestUrl.setPort(port);
requestUrl.setPath("/setstates");
QUrlQuery query;
query.addQueryItem("connected", connected ? "true" : "false");
query.addQueryItem("currentPowerPhaseA", QString::number(phasesPower.value("A").toInt()));
query.addQueryItem("currentPowerPhaseB", QString::number(phasesPower.value("B").toInt()));
query.addQueryItem("currentPowerPhaseC", QString::number(phasesPower.value("C").toInt()));
// Tension et courant par phase, OPTIONNELS : passés dans `phasesPower` sous les clés
// "uA"/"uB"/"uC" et "iA"/"iB"/"iC". Absents, le mock dérive comme avant — aucun scénario
// existant ne bouge. Présents, ils permettent enfin une triphasée DÉSÉQUILIBRÉE et une
// tension ≠ 230 V, que le banc ne savait pas produire.
for (const auto &p : {std::make_pair("uA", "voltagePhaseA"), std::make_pair("uB", "voltagePhaseB"),
std::make_pair("uC", "voltagePhaseC"), std::make_pair("iA", "currentPhaseA"),
std::make_pair("iB", "currentPhaseB"), std::make_pair("iC", "currentPhaseC")})
if (phasesPower.contains(p.first))
query.addQueryItem(p.second, QString::number(phasesPower.value(p.first).toDouble()));
requestUrl.setQuery(query);
QNetworkRequest request(requestUrl);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
return m_networkAccessManager->post(request, QByteArray());
}
QNetworkReply *EnergyTestBase::setCarStates(uint batteryLevel, uint capacity, uint minChargingCurrent, uint phaseCount, quint16 port)
{
QUrl requestUrl;
requestUrl.setScheme("http");
requestUrl.setHost("127.0.0.1");
requestUrl.setPort(port);
requestUrl.setPath("/setstates");
QUrlQuery query;
query.addQueryItem("batteryLevel", QString::number(batteryLevel));
query.addQueryItem("capacity", QString::number(capacity));
query.addQueryItem("minChargingCurrent", QString::number(minChargingCurrent));
query.addQueryItem("phaseCount", QString::number(phaseCount));
requestUrl.setQuery(query);
QNetworkRequest request(requestUrl);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
return m_networkAccessManager->post(request, QByteArray());
}
QNetworkReply *EnergyTestBase::setChargerStates(bool connected, bool power, bool pluggedIn, const QString &phases, int maxChargingCurrent, int maxChargingCurrentMaxValue, quint16 port)
{
QUrl requestUrl;
requestUrl.setScheme("http");
requestUrl.setHost("127.0.0.1");
requestUrl.setPort(port);
requestUrl.setPath("/setstates");
QUrlQuery query;
query.addQueryItem("connected", connected ? "true" : "false");
query.addQueryItem("power", power ? "true" : "false");
query.addQueryItem("pluggedIn", pluggedIn ? "true" : "false");
query.addQueryItem("usedPhases", phases);
query.addQueryItem("maxChargingCurrent", QString::number(maxChargingCurrent));
query.addQueryItem("maxChargingCurrentMaxValue", QString::number(maxChargingCurrentMaxValue));
requestUrl.setQuery(query);
QNetworkRequest request(requestUrl);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
return m_networkAccessManager->post(request, QByteArray());
}
QNetworkReply *EnergyTestBase::setChargerWithPhaseCountSwitchingStates(bool connected, bool power, bool pluggedIn, const QString &phases, int maxChargingCurrent, int maxChargingCurrentMaxValue, uint desiredPhaseCount, quint16 port)
{
QUrl requestUrl;
requestUrl.setScheme("http");
requestUrl.setHost("127.0.0.1");
requestUrl.setPort(port);
requestUrl.setPath("/setstates");
QUrlQuery query;
query.addQueryItem("connected", connected ? "true" : "false");
query.addQueryItem("power", power ? "true" : "false");
query.addQueryItem("pluggedIn", pluggedIn ? "true" : "false");
query.addQueryItem("usedPhases", phases);
query.addQueryItem("maxChargingCurrent", QString::number(maxChargingCurrent));
query.addQueryItem("maxChargingCurrentMaxValue", QString::number(maxChargingCurrentMaxValue));
query.addQueryItem("desiredPhaseCount", QString::number(desiredPhaseCount));
requestUrl.setQuery(query);
QNetworkRequest request(requestUrl);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
return m_networkAccessManager->post(request, QByteArray());
}
QNetworkReply *EnergyTestBase::setSimpleChargerStates(bool connected, bool power, bool pluggedIn, int phaseCount, int maxChargingCurrent, quint16 port)
{
QUrl requestUrl;
requestUrl.setScheme("http");
requestUrl.setHost("127.0.0.1");
requestUrl.setPort(port);
requestUrl.setPath("/setstates");
QUrlQuery query;
query.addQueryItem("connected", connected ? "true" : "false");
query.addQueryItem("power", power ? "true" : "false");
query.addQueryItem("pluggedIn", pluggedIn ? "true" : "false");
query.addQueryItem("phaseCount", QString::number(phaseCount));
query.addQueryItem("maxChargingCurrent", QString::number(maxChargingCurrent));
requestUrl.setQuery(query);
QNetworkRequest request(requestUrl);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
return m_networkAccessManager->post(request, QByteArray());
}
QNetworkReply *EnergyTestBase::setEnergyStorageStates(uint batteryLevel, int currentPower, quint16 port)
{
QUrl requestUrl;
requestUrl.setScheme("http");
requestUrl.setHost("127.0.0.1");
requestUrl.setPort(port);
requestUrl.setPath("/setstates");
QUrlQuery query;
query.addQueryItem("batteryLevel", QString::number(batteryLevel));
query.addQueryItem("currentPower", QString::number(currentPower));
requestUrl.setQuery(query);
QNetworkRequest request(requestUrl);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
return m_networkAccessManager->post(request, QByteArray());
}
QNetworkReply *EnergyTestBase::setPowerSwitchStates(bool power, double currentPower, quint16 port)
{
QUrl requestUrl;
requestUrl.setScheme("http");
requestUrl.setHost("127.0.0.1");
requestUrl.setPort(port);
requestUrl.setPath("/setstates");
QUrlQuery query;
query.addQueryItem("power", power ? "true" : "false");
query.addQueryItem("currentPower", QString::number(currentPower));
requestUrl.setQuery(query);
QNetworkRequest request(requestUrl);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
return m_networkAccessManager->post(request, QByteArray());
}
QNetworkReply *EnergyTestBase::getActionHistory(quint16 port)
{
QUrl requestUrl;
requestUrl.setScheme("http");
requestUrl.setHost("127.0.0.1");
requestUrl.setPort(port);
requestUrl.setPath("/actionhistory");
QNetworkRequest request(requestUrl);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
return m_networkAccessManager->get(request);
}
QNetworkReply *EnergyTestBase::clearActionHistroy(quint16 port)
{
QUrl requestUrl;
requestUrl.setScheme("http");
requestUrl.setHost("127.0.0.1");
requestUrl.setPort(port);
requestUrl.setPath("/clearactionhistory");
QNetworkRequest request(requestUrl);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
return m_networkAccessManager->get(request);
}
QUuid EnergyTestBase::addMeter(quint16 port)
{
QVariantList thingParams;
QVariantMap portParam;
portParam.insert("paramTypeId", "{7abcc8a1-08b1-45bc-9116-10f9848359f9}");
portParam.insert("value", port);
thingParams.append(portParam);
QVariantMap params;
params.insert("thingClassId", mockMeterThingClassId.toString());
params.insert("name", "Meter");
params.insert("thingParams", thingParams);
QVariant response = injectAndWait("Integrations.AddThing", params);
verifyThingError(response);
return response.toMap().value("params").toMap().value("thingId").toUuid();
}
QUuid EnergyTestBase::addMeterPowerOnly(quint16 port)
{
QVariantList thingParams;
QVariantMap portParam;
portParam.insert("paramTypeId", "{c1108d3a-6391-428f-8d75-91e8986657d6}");
portParam.insert("value", port);
thingParams.append(portParam);
QVariantMap params;
params.insert("thingClassId", mockMeterPowerOnlyThingClassId.toString());
params.insert("name", "Meter");
params.insert("thingParams", thingParams);
QVariant response = injectAndWait("Integrations.AddThing", params);
verifyThingError(response);
return response.toMap().value("params").toMap().value("thingId").toUuid();
}
QUuid EnergyTestBase::addCar(quint16 port)
{
QVariantList thingParams;
QVariantMap portParam;
portParam.insert("paramTypeId", "{29533bc8-d71e-4ce5-8ce3-bf87f0370391}");
portParam.insert("value", port);
thingParams.append(portParam);
QVariantMap params;
params.insert("thingClassId", mockCarThingClassId.toString());
params.insert("name", "Tschitti Tschitti Bäng Bäng");
params.insert("thingParams", thingParams);
QVariant response = injectAndWait("Integrations.AddThing", params);
verifyThingError(response);
return response.toMap().value("params").toMap().value("thingId").toUuid();
}
QUuid EnergyTestBase::addCharger(const QString &phases, double maxChargingCurrentUpperLimit, quint16 port)
{
QVariantList thingParams;
QVariantMap portParam;
portParam.insert("paramTypeId", "{652624a2-8f9a-4bc3-b34f-5e3492af4d30}");
portParam.insert("value", port);
QVariantMap phasesParam;
phasesParam.insert("paramTypeId", "{facd5c76-d15e-4e29-9929-5e1764ae05dc}");
phasesParam.insert("value", phases);
QVariantMap maxCurrentParam;
maxCurrentParam.insert("paramTypeId", "{234c6676-1ec0-4eff-bed0-ecee7ce82074}");
maxCurrentParam.insert("value", maxChargingCurrentUpperLimit);
thingParams.append(portParam);
thingParams.append(phasesParam);
thingParams.append(maxCurrentParam);
QVariantMap params;
params.insert("thingClassId", mockChargerThingClassId.toString());
params.insert("name", "Charger");
params.insert("thingParams", thingParams);
QVariant response = injectAndWait("Integrations.AddThing", params);
verifyThingError(response);
return response.toMap().value("params").toMap().value("thingId").toUuid();
}
QUuid EnergyTestBase::addChargerWithPhaseCountSwitching(const QString &phases, double maxChargingCurrentUpperLimit, quint16 port)
{
QVariantList thingParams;
QVariantMap portParam;
portParam.insert("paramTypeId", "{d4492038-51bf-4f3c-8b93-89af4d8edd6b}");
portParam.insert("value", port);
QVariantMap phasesParam;
phasesParam.insert("paramTypeId", "{fb64b557-d4ee-4a89-9141-0c585ee476c9}");
phasesParam.insert("value", phases);
QVariantMap maxCurrentParam;
maxCurrentParam.insert("paramTypeId", "{7c1a941d-3eb2-4c6b-90b5-b0faf82dcb73}");
maxCurrentParam.insert("value", maxChargingCurrentUpperLimit);
thingParams.append(portParam);
thingParams.append(phasesParam);
thingParams.append(maxCurrentParam);
QVariantMap params;
params.insert("thingClassId", mockChargerWithPhaseSwitchingThingClassId.toString());
params.insert("name", "Charger with phase count switching");
params.insert("thingParams", thingParams);
QVariant response = injectAndWait("Integrations.AddThing", params);
verifyThingError(response);
return response.toMap().value("params").toMap().value("thingId").toUuid();
}
QUuid EnergyTestBase::addSimpleCharger(double maxChargingCurrentUpperLimit, quint16 port)
{
QVariantList thingParams;
QVariantMap portParam;
portParam.insert("paramTypeId", "{e94f863b-47a2-44e4-8103-bb4b8a817f47}");
portParam.insert("value", port);
QVariantMap maxCurrentParam;
maxCurrentParam.insert("paramTypeId", "{61943026-985b-4728-bfaa-299d0fbcdcae}");
maxCurrentParam.insert("value", maxChargingCurrentUpperLimit);
thingParams.append(portParam);
thingParams.append(maxCurrentParam);
QVariantMap params;
params.insert("thingClassId", mockSimpleChargerThingClassId.toString());
params.insert("name", "Simple charger");
params.insert("thingParams", thingParams);
QVariant response = injectAndWait("Integrations.AddThing", params);
verifyThingError(response);
return response.toMap().value("params").toMap().value("thingId").toUuid();
}
QUuid EnergyTestBase::addEnergyStorage(uint capacity, double maxChargingPowerUpperLimit, double maxDischargingPowerUpperLimit, quint16 port)
{
QVariantList thingParams;
QVariantMap portParam;
portParam.insert("paramTypeId", "{b074a76f-6382-48b9-b101-1d13904f30c8}");
portParam.insert("value", port);
QVariantMap capacityParam;
capacityParam.insert("paramTypeId", "{fd56c49e-98e2-4373-bf23-fd8b5a58e339}");
capacityParam.insert("value", capacity);
QVariantMap maxChargingPowerParam;
maxChargingPowerParam.insert("paramTypeId", "{769391a8-b801-42fc-af63-90367bf46bdf}");
maxChargingPowerParam.insert("value", maxChargingPowerUpperLimit);
QVariantMap maxDischargingPowerParam;
maxDischargingPowerParam.insert("paramTypeId", "{559d9434-9b31-4e0d-a654-2ea1e5f65a82}");
maxDischargingPowerParam.insert("value", maxDischargingPowerUpperLimit);
thingParams.append(portParam);
thingParams.append(capacityParam);
thingParams.append(maxChargingPowerParam);
thingParams.append(maxDischargingPowerParam);
QVariantMap params;
params.insert("thingClassId", mockEnergyStorageThingClassId.toString());
params.insert("name", "Energy storage");
params.insert("thingParams", thingParams);
QVariant response = injectAndWait("Integrations.AddThing", params);
verifyThingError(response);
return response.toMap().value("params").toMap().value("thingId").toUuid();
}
QUuid EnergyTestBase::addDryRelay(quint16 port)
{
QVariantMap portParam;
portParam.insert("paramTypeId", "{9f3d6b18-2c47-4a80-91e5-7d0a4b3c8e26}");
portParam.insert("value", port);
QVariantMap params;
params.insert("thingClassId", mockDryRelayThingClassId.toString());
params.insert("name", QString("Dry relay %1").arg(port));
params.insert("thingParams", QVariantList() << portParam);
QVariant response = injectAndWait("Integrations.AddThing", params);
verifyThingError(response);
return response.toMap().value("params").toMap().value("thingId").toUuid();
}
QUuid EnergyTestBase::addPowerSwitch(double nominalPower, quint16 port)
{
QVariantList thingParams;
QVariantMap portParam;
portParam.insert("paramTypeId", "{e3398429-45fd-4add-a789-4d11bfd9560f}");
portParam.insert("value", port);
QVariantMap nominalPowerParam;
nominalPowerParam.insert("paramTypeId", "{b850a4d1-af0f-477d-ac73-56071f371884}");
nominalPowerParam.insert("value", nominalPower);
thingParams.append(portParam);
thingParams.append(nominalPowerParam);
QVariantMap params;
params.insert("thingClassId", mockPowerSwitchThingClassId.toString());
// Nom unique par port : plusieurs relais (paliers ECS) peuvent coexister.
params.insert("name", QString("Power switch %1").arg(port));
params.insert("thingParams", thingParams);
QVariant response = injectAndWait("Integrations.AddThing", params);
verifyThingError(response);
return response.toMap().value("params").toMap().value("thingId").toUuid();
}
QUuid EnergyTestBase::addEtmVariableLoad(quint16 port)
{
QVariantList thingParams;
QVariantMap portParam;
portParam.insert("paramTypeId", "{c8d52a1b-4f3e-4b02-a07c-3d9f2e6b8c51}"); // etmVariableLoad port
portParam.insert("value", port);
thingParams.append(portParam);
QVariantMap params;
params.insert("thingClassId", "{b7c41f0a-3e2d-4a91-9f6b-2c8e1d5a7b40}"); // etmVariableLoadThingClassId
params.insert("name", QString("etmVariableLoad %1").arg(port));
params.insert("thingParams", thingParams);
QVariant response = injectAndWait("Integrations.AddThing", params);
verifyThingError(response);
return response.toMap().value("params").toMap().value("thingId").toUuid();
}
void EnergyTestBase::removeDevices()
{
QVariant configuredDevices = injectAndWait("Integrations.GetThings");
foreach (const QVariant &dev, configuredDevices.toMap().value("params").toMap().value("things").toList()) {
qCDebug(dcTests()) << "Removing thing" << dev.toMap().value("name").toString() << dev.toMap().value("id").toUuid();
QVariant response = removeDevice(dev.toMap().value("id").toUuid());
verifyThingError(response);
}
}
QVariant EnergyTestBase::removeDevice(const QUuid &thingId)
{
qCDebug(dcTests()) << "Remove device" << thingId.toString();
QVariantMap params;
params.insert("thingId", thingId.toString());
return injectAndWait("Integrations.RemoveThing", params);
}
bool EnergyTestBase::verifyActionExecuted(const QVariantList &actionHistory, const QString &actionName)
{
foreach (const QVariant &actionHistoryVariant, actionHistory) {
QVariantMap actionHistoryMap = actionHistoryVariant.toMap();
if (actionHistoryMap.value("name").toString() == actionName) {
return true;
}
}
return false;
}
QVariant EnergyTestBase::getLastValueFromExecutedAction(const QVariantList &actionHistory, const QString &actionName, const QString &paramName)
{
QVariant paramValue;
foreach (const QVariant &actionHistoryVariant, actionHistory) {
QVariantMap actionHistoryMap = actionHistoryVariant.toMap();
if (actionHistoryMap.value("name").toString() == actionName) {
foreach (const QVariant &paramVariant, actionHistoryMap.value("params").toList()) {
if (paramVariant.toMap().value("name").toString() == paramName) {
paramValue = paramVariant.toMap().value("value");
}
}
}
}
return paramValue;
}