2029 lines
81 KiB
C++
2029 lines
81 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 "testcharging.h"
|
|
|
|
#include <hardware/electricity.h>
|
|
|
|
TestCharging::TestCharging(QObject *parent):
|
|
EnergyTestBase(parent)
|
|
{
|
|
|
|
}
|
|
|
|
void TestCharging::testAddCharger()
|
|
{
|
|
// Add
|
|
QUuid chargerThingId = addCharger("ABC", 16);
|
|
QVERIFY2(!chargerThingId.isNull(), "Did not receive valid ThingId");
|
|
|
|
// Remove
|
|
QVariant response = removeDevice(chargerThingId);
|
|
verifyThingError(response);
|
|
}
|
|
|
|
void TestCharging::testAddSimpleCharger()
|
|
{
|
|
// Add
|
|
QUuid chargerThingId = addSimpleCharger(20);
|
|
QVERIFY2(!chargerThingId.isNull(), "Did not receive valid ThingId");
|
|
|
|
// Remove
|
|
QVariant response = removeDevice(chargerThingId);
|
|
verifyThingError(response);
|
|
}
|
|
|
|
void TestCharging::testAddCar()
|
|
{
|
|
// Add
|
|
QUuid carThingId = addCar();
|
|
QVERIFY2(!carThingId.isNull(), "Did not receive valid ThingId");
|
|
|
|
// Remove
|
|
QVariant response = removeDevice(carThingId);
|
|
verifyThingError(response);
|
|
}
|
|
|
|
void TestCharging::testAddMeter()
|
|
{
|
|
QVariant response, notification;
|
|
|
|
QSignalSpy notificationSpy(m_mockTcpServer, &MockTcpServer::outgoingData);;
|
|
|
|
// Make sure there is no root meter
|
|
response = injectAndWait("Energy.GetRootMeter");
|
|
QVERIFY(response.toMap().value("params").toMap().isEmpty());
|
|
QVERIFY(response.toMap().value("status").toString() == "success");
|
|
|
|
// Add mock meter
|
|
QUuid meterThingId = addMeter();
|
|
QVERIFY2(!meterThingId.isNull(), "Did not receive valid ThingId");
|
|
|
|
// Check notification Energy.RootMeterChanged containing our added meter thing id
|
|
if (notificationSpy.count() == 0) notificationSpy.wait();
|
|
notification = checkNotification(notificationSpy, "Energy.RootMeterChanged");
|
|
QCOMPARE(notification.toMap().value("params").toMap().value("rootMeterThingId").toUuid(), meterThingId);
|
|
|
|
// Make sure this is our root meter now
|
|
response = injectAndWait("Energy.GetRootMeter");
|
|
QCOMPARE(response.toMap().value("params").toMap().value("rootMeterThingId").toUuid(), meterThingId);
|
|
|
|
// Undo all steps
|
|
|
|
// Remove meter
|
|
notificationSpy.clear();
|
|
response = removeDevice(meterThingId);
|
|
qDebug() << response.toMap();
|
|
|
|
// Check notification Energy.RootMeterChanged containing our added meter thing id
|
|
if (notificationSpy.count() == 0) notificationSpy.wait();
|
|
notification = checkNotification(notificationSpy, "Energy.RootMeterChanged");
|
|
QVERIFY(notification.toMap().value("params").toMap().isEmpty());
|
|
|
|
// Make sure there is no root meter
|
|
response = injectAndWait("Energy.GetRootMeter");
|
|
QVERIFY(response.toMap().value("params").toMap().isEmpty());
|
|
QVERIFY(response.toMap().value("status").toString() == "success");
|
|
}
|
|
|
|
void TestCharging::getSetPhaseLimits()
|
|
{
|
|
QVariant response, notification;
|
|
QSignalSpy notificationSpy(m_mockTcpServer, &MockTcpServer::outgoingData);;
|
|
|
|
uint phasePowerLimit = 0;
|
|
uint newPhasePowerLimit = 30;
|
|
|
|
response = injectAndWait("NymeaEnergy.GetPhasePowerLimit");
|
|
phasePowerLimit = response.toMap().value("params").toMap().value("phasePowerLimit").toUInt();
|
|
// Initially 0 since we have no things at all set up
|
|
if (phasePowerLimit == newPhasePowerLimit) {
|
|
newPhasePowerLimit++;
|
|
}
|
|
|
|
notificationSpy.clear();
|
|
response = injectAndWait("NymeaEnergy.SetPhasePowerLimit", QVariantMap({{"phasePowerLimit", newPhasePowerLimit}}));
|
|
QVERIFY(response.toMap().value("params").toMap().value("energyError").toString() == "EnergyErrorNoError");
|
|
if (notificationSpy.count() == 0) notificationSpy.wait();
|
|
notification = checkNotification(notificationSpy, "NymeaEnergy.PhasePowerLimitChanged");
|
|
QCOMPARE(notification.toMap().value("params").toMap().value("phasePowerLimit").toUInt(), newPhasePowerLimit);
|
|
|
|
response = injectAndWait("NymeaEnergy.GetPhasePowerLimit");
|
|
phasePowerLimit = response.toMap().value("params").toMap().value("phasePowerLimit").toUInt();
|
|
QCOMPARE(phasePowerLimit, newPhasePowerLimit);
|
|
|
|
|
|
notificationSpy.clear();
|
|
newPhasePowerLimit = 30000;
|
|
response = injectAndWait("NymeaEnergy.SetPhasePowerLimit", QVariantMap({{"phasePowerLimit", newPhasePowerLimit}}));
|
|
QVERIFY(response.toMap().value("params").toMap().value("energyError").toString() == "EnergyErrorNoError");
|
|
if (notificationSpy.count() == 0) notificationSpy.wait();
|
|
notification = checkNotification(notificationSpy, "NymeaEnergy.PhasePowerLimitChanged");
|
|
QCOMPARE(notification.toMap().value("params").toMap().value("phasePowerLimit").toUInt(), newPhasePowerLimit);
|
|
|
|
response = injectAndWait("NymeaEnergy.GetPhasePowerLimit");
|
|
phasePowerLimit = response.toMap().value("params").toMap().value("phasePowerLimit").toUInt();
|
|
QCOMPARE(phasePowerLimit, newPhasePowerLimit);
|
|
}
|
|
|
|
|
|
void TestCharging::getSetLockOnUnplug_data()
|
|
{
|
|
QTest::addColumn<bool>("lockOnUnplug");
|
|
|
|
QTest::newRow("Enable") << true;
|
|
QTest::newRow("Disable") << false;
|
|
}
|
|
|
|
void TestCharging::getSetLockOnUnplug()
|
|
{
|
|
QFETCH(bool, lockOnUnplug);
|
|
|
|
QVariantMap params;
|
|
QVariant response, notification;
|
|
QSignalSpy notificationSpy(m_mockTcpServer, &MockTcpServer::outgoingData);;
|
|
|
|
// Set initial lockOnUnplug to oposit, so we get the changed notification for sure
|
|
params.clear(); response.clear(); notification.clear(); notificationSpy.clear();
|
|
params.insert("lockOnUnplug", !lockOnUnplug);
|
|
response = injectAndWait("NymeaEnergy.SetLockOnUnplug", params);
|
|
verifyEnergyError(response);
|
|
|
|
// Set actual lockOnUnplug
|
|
params.clear(); response.clear(); notification.clear(); notificationSpy.clear();
|
|
params.insert("lockOnUnplug", lockOnUnplug);
|
|
response = injectAndWait("NymeaEnergy.SetLockOnUnplug", params);
|
|
verifyEnergyError(response);
|
|
|
|
// If the value has changed, verify the notification
|
|
if (notificationSpy.count() == 0) notificationSpy.wait();
|
|
notification = checkNotification(notificationSpy, "NymeaEnergy.LockOnUnplugChanged");
|
|
QCOMPARE(notification.toMap().value("params").toMap().value("lockOnUnplug").toDouble(), lockOnUnplug);
|
|
|
|
// Get acquisition tolerance
|
|
params.clear(); response.clear(); notification.clear(); notificationSpy.clear();
|
|
response = injectAndWait("NymeaEnergy.GetLockOnUnplug", params);
|
|
QVERIFY2(response.toMap().value("params").toMap().contains("lockOnUnplug"), "Did not get aquisition tolerance.");
|
|
QCOMPARE(response.toMap().value("params").toMap().value("lockOnUnplug").toBool(), lockOnUnplug);
|
|
}
|
|
|
|
void TestCharging::getSetAcquisitionTolerance_data()
|
|
{
|
|
QTest::addColumn<double>("initialAcquisitionTolerance");
|
|
QTest::addColumn<double>("acquisitionTolerance");
|
|
QTest::addColumn<EnergyManager::EnergyError>("expectedError");
|
|
|
|
QTest::newRow("Default") << 0.1 << 0.5 << EnergyManager::EnergyErrorNoError;
|
|
QTest::newRow("Unchanged") << 0.5 << 0.5 << EnergyManager::EnergyErrorNoError;
|
|
QTest::newRow("< 0.0") << 0.5 << -1.0 << EnergyManager::EnergyErrorInvalidParameter;
|
|
QTest::newRow("> 1.0") << 0.5 << 2.0 << EnergyManager::EnergyErrorInvalidParameter;
|
|
}
|
|
|
|
void TestCharging::getSetAcquisitionTolerance()
|
|
{
|
|
QFETCH(double, initialAcquisitionTolerance);
|
|
QFETCH(double, acquisitionTolerance);
|
|
QFETCH(EnergyManager::EnergyError, expectedError);
|
|
|
|
QVariantMap params;
|
|
QVariant response, notification;
|
|
QSignalSpy notificationSpy(m_mockTcpServer, &MockTcpServer::outgoingData);;
|
|
|
|
// Set initial acquisition tolerance
|
|
params.clear(); response.clear(); notification.clear(); notificationSpy.clear();
|
|
params.insert("acquisitionTolerance", initialAcquisitionTolerance);
|
|
response = injectAndWait("NymeaEnergy.SetAcquisitionTolerance", params);
|
|
verifyEnergyError(response);
|
|
|
|
bool testNotificationReceived = (initialAcquisitionTolerance != acquisitionTolerance);
|
|
|
|
// Set acquisition tolerance
|
|
params.clear(); response.clear(); notification.clear(); notificationSpy.clear();
|
|
params.insert("acquisitionTolerance", acquisitionTolerance);
|
|
response = injectAndWait("NymeaEnergy.SetAcquisitionTolerance", params);
|
|
verifyEnergyError(response, expectedError);
|
|
|
|
// If the value has changed, verify the notification
|
|
if (testNotificationReceived && expectedError == EnergyManager::EnergyErrorNoError) {
|
|
if (notificationSpy.count() == 0) notificationSpy.wait();
|
|
notification = checkNotification(notificationSpy, "NymeaEnergy.AcquisitionToleranceChanged");
|
|
QCOMPARE(notification.toMap().value("params").toMap().value("acquisitionTolerance").toDouble(), acquisitionTolerance);
|
|
}
|
|
|
|
// Get acquisition tolerance
|
|
params.clear(); response.clear(); notification.clear(); notificationSpy.clear();
|
|
response = injectAndWait("NymeaEnergy.GetAcquisitionTolerance", params);
|
|
QVERIFY2(response.toMap().value("params").toMap().contains("acquisitionTolerance"), "Did not get aquisition tolerance.");
|
|
}
|
|
|
|
void TestCharging::getSetBatteryLevelConsideration_data()
|
|
{
|
|
QTest::addColumn<double>("initialBatteryLevelConsideration");
|
|
QTest::addColumn<double>("batteryLevelConsideration");
|
|
QTest::addColumn<EnergyManager::EnergyError>("expectedError");
|
|
|
|
QTest::newRow("Default") << 0.1 << 0.5 << EnergyManager::EnergyErrorNoError;
|
|
QTest::newRow("Unchanged") << 0.5 << 0.5 << EnergyManager::EnergyErrorNoError;
|
|
QTest::newRow("< 0.0") << 0.5 << -1.0 << EnergyManager::EnergyErrorInvalidParameter;
|
|
QTest::newRow("> 1.0") << 0.5 << 2.0 << EnergyManager::EnergyErrorInvalidParameter;
|
|
}
|
|
|
|
void TestCharging::getSetBatteryLevelConsideration()
|
|
{
|
|
QFETCH(double, initialBatteryLevelConsideration);
|
|
QFETCH(double, batteryLevelConsideration);
|
|
QFETCH(EnergyManager::EnergyError, expectedError);
|
|
|
|
QVariantMap params;
|
|
QVariant response, notification;
|
|
QSignalSpy notificationSpy(m_mockTcpServer, &MockTcpServer::outgoingData);;
|
|
|
|
// Set initial acquisition tolerance
|
|
params.clear(); response.clear(); notification.clear(); notificationSpy.clear();
|
|
params.insert("batteryLevelConsideration", initialBatteryLevelConsideration);
|
|
response = injectAndWait("NymeaEnergy.SetBatteryLevelConsideration", params);
|
|
verifyEnergyError(response);
|
|
|
|
bool testNotificationReceived = (initialBatteryLevelConsideration != batteryLevelConsideration);
|
|
|
|
// Set acquisition tolerance
|
|
params.clear(); response.clear(); notification.clear(); notificationSpy.clear();
|
|
params.insert("batteryLevelConsideration", batteryLevelConsideration);
|
|
response = injectAndWait("NymeaEnergy.SetBatteryLevelConsideration", params);
|
|
verifyEnergyError(response, expectedError);
|
|
|
|
// If the value has changed, verify the notification
|
|
if (testNotificationReceived && expectedError == EnergyManager::EnergyErrorNoError) {
|
|
if (notificationSpy.count() == 0) notificationSpy.wait();
|
|
notification = checkNotification(notificationSpy, "NymeaEnergy.BatteryLevelConsiderationChanged");
|
|
QCOMPARE(notification.toMap().value("params").toMap().value("batteryLevelConsideration").toDouble(), batteryLevelConsideration);
|
|
}
|
|
|
|
// Get acquisition tolerance
|
|
params.clear(); response.clear(); notification.clear(); notificationSpy.clear();
|
|
response = injectAndWait("NymeaEnergy.GetBatteryLevelConsideration", params);
|
|
QVERIFY2(response.toMap().value("params").toMap().contains("batteryLevelConsideration"), "Did not get battery level consideration.");
|
|
}
|
|
|
|
void TestCharging::addChargingInfo_data()
|
|
{
|
|
QTest::addColumn<bool>("addingCharger");
|
|
QTest::addColumn<bool>("addingCar");
|
|
QTest::addColumn<bool>("carValid");
|
|
QTest::addColumn<QString>("chargingMode");
|
|
QTest::addColumn<QDateTime>("endDateTime");
|
|
QTest::addColumn<QList<int>>("repeatDays");
|
|
QTest::addColumn<int>("targetPercentage");
|
|
QTest::addColumn<double>("acquisitionTolerance");
|
|
QTest::addColumn<bool>("jsonSuccess");
|
|
QTest::addColumn<EnergyManager::EnergyError>("expectedError");
|
|
|
|
QDateTime endDateTime = QDateTime::currentDateTime().addDays(1);
|
|
endDateTime.setTime(QTime(7, 0));
|
|
QTest::newRow("Default") << true << true << true << "ChargingModeNormal" << endDateTime << QList<int>{} << 100 << 0.5 << true << EnergyManager::EnergyErrorNoError;
|
|
QTest::newRow("Default Eco") << true << true << true << "ChargingModeEco" << endDateTime << QList<int>{} << 100 << 0.5 << true << EnergyManager::EnergyErrorNoError;
|
|
QTest::newRow("Invalid mode") << true << true << true << "ChargingModeBlablaa" << endDateTime << QList<int>{} << 100 << 0.5 << false << EnergyManager::EnergyErrorNoError;
|
|
QTest::newRow("Invalid charger") << false << true << true << "ChargingModeNormal" << endDateTime << QList<int>{} << 100 << 0.5 << true << EnergyManager::EnergyErrorInvalidParameter;
|
|
QTest::newRow("with repeatdays") << true << false << true << "ChargingModeNormal" << endDateTime << QList<int>{1,3,6} << 100 << 0.5 << true << EnergyManager::EnergyErrorNoError;
|
|
QTest::newRow("Invalid repeatDays") << true << false << true << "ChargingModeNormal" << endDateTime << QList<int>{0,1,2} << 100 << 0.5 << true << EnergyManager::EnergyErrorInvalidParameter;
|
|
QTest::newRow("Invalid car") << true << true << false << "ChargingModeEco" << endDateTime << QList<int>{} << 100 << 0.5 << true << EnergyManager::EnergyErrorInvalidParameter;
|
|
QTest::newRow("Invalid %") << true << true << true << "ChargingModeNormal" << endDateTime << QList<int>{} << 200 << 0.5 << true << EnergyManager::EnergyErrorInvalidParameter;
|
|
}
|
|
|
|
void TestCharging::addChargingInfo()
|
|
{
|
|
QFETCH(bool, addingCharger);
|
|
QFETCH(bool, addingCar);
|
|
QFETCH(bool, carValid);
|
|
QFETCH(QString, chargingMode);
|
|
QFETCH(QDateTime, endDateTime);
|
|
QFETCH(QList<int>, repeatDays);
|
|
QFETCH(int, targetPercentage);
|
|
QFETCH(double, acquisitionTolerance);
|
|
QFETCH(bool, jsonSuccess);
|
|
QFETCH(EnergyManager::EnergyError, expectedError);
|
|
|
|
QVariant response, notification;
|
|
QSignalSpy notificationSpy(m_mockTcpServer, &MockTcpServer::outgoingData);;
|
|
|
|
// Set phase power limit
|
|
uint phasePowerLimit = 25000;
|
|
response = injectAndWait("NymeaEnergy.SetPhasePowerLimit", QVariantMap({{"phasePowerLimit", phasePowerLimit}}));
|
|
verifyEnergyError(response);
|
|
|
|
removeDevices();
|
|
|
|
// Add mock meter
|
|
QUuid meterThingId = addMeter();
|
|
QVERIFY2(!meterThingId.isNull(), "Did not receive valid ThingId");
|
|
if (notificationSpy.count() == 0) notificationSpy.wait();
|
|
notification = checkNotification(notificationSpy, "Energy.RootMeterChanged");
|
|
QCOMPARE(notification.toMap().value("params").toMap().value("rootMeterThingId").toUuid(), meterThingId);
|
|
|
|
// Make sure this is our root meter now
|
|
response = injectAndWait("Energy.GetRootMeter");
|
|
QCOMPARE(response.toMap().value("params").toMap().value("rootMeterThingId").toUuid(), meterThingId);
|
|
notificationSpy.clear();
|
|
|
|
// Add the charger
|
|
QUuid evChargerId;
|
|
if (addingCharger) {
|
|
evChargerId = addCharger("ABC", 16);
|
|
QVERIFY2(!evChargerId.isNull(), "Did not receive valid ThingId");
|
|
if (notificationSpy.count() == 0) notificationSpy.wait();
|
|
checkNotification(notificationSpy, "Integrations.ThingAdded");
|
|
} else {
|
|
evChargerId = QUuid::createUuid();
|
|
}
|
|
|
|
// Add the car
|
|
QUuid assignedCarId;
|
|
if (addingCar) {
|
|
assignedCarId = addCar();
|
|
QVERIFY2(!assignedCarId.isNull(), "Did not receive valid ThingId");
|
|
if (notificationSpy.count() == 0) notificationSpy.wait();
|
|
checkNotification(notificationSpy, "Integrations.ThingAdded");
|
|
}
|
|
|
|
if (!carValid)
|
|
assignedCarId = QUuid::createUuid();
|
|
|
|
// Set aquisition tolerance
|
|
QVariantMap params;
|
|
params.insert("acquisitionTolerance", acquisitionTolerance);
|
|
response = injectAndWait("NymeaEnergy.SetAcquisitionTolerance", params);
|
|
verifyEnergyError(response);
|
|
notificationSpy.clear();
|
|
|
|
// Set charging info with our charger and car
|
|
QVariantMap chargingInfoMap;
|
|
chargingInfoMap.insert("evChargerId", evChargerId);
|
|
if (!assignedCarId.isNull())
|
|
chargingInfoMap.insert("assignedCarId", assignedCarId);
|
|
|
|
chargingInfoMap.insert("chargingMode", chargingMode);
|
|
chargingInfoMap.insert("endDateTime", endDateTime.toMSecsSinceEpoch() / 1000);
|
|
QVariantList repDaysList;
|
|
foreach (int day, repeatDays) {
|
|
repDaysList.append(day);
|
|
}
|
|
chargingInfoMap.insert("repeatDays", repDaysList);
|
|
chargingInfoMap.insert("targetPercentage", targetPercentage);
|
|
//qCDebug(dcTests()) << "Sending charging info:" << qUtf8Printable(QJsonDocument::fromVariant(chargingInfoMap).toJson(QJsonDocument::Indented));
|
|
response = injectAndWait("NymeaEnergy.SetChargingInfo", QVariantMap({{"chargingInfo", chargingInfoMap}}));
|
|
|
|
if (jsonSuccess) {
|
|
QCOMPARE(response.toMap().value("status").toString(), QString("success"));
|
|
verifyEnergyError(response, expectedError);
|
|
|
|
// Get the charging infos
|
|
params.clear(); response.clear(); notification.clear(); notificationSpy.clear();
|
|
response = injectAndWait("NymeaEnergy.GetChargingInfos", params);
|
|
QVERIFY2(response.toMap().value("params").toMap().contains("chargingInfos"), "Did not get charginginfos.");
|
|
QVariantList chargingInfos = response.toMap().value("params").toMap().value("chargingInfos").toList();
|
|
qDebug() << chargingInfos;
|
|
|
|
if (expectedError == EnergyManager::EnergyErrorNoError) {
|
|
QCOMPARE(chargingInfos.count(), 1);
|
|
}
|
|
|
|
} else {
|
|
QCOMPARE(response.toMap().value("status").toString(), QString("error"));
|
|
}
|
|
}
|
|
|
|
void TestCharging::testEcoMode_data()
|
|
{
|
|
// Houshold info
|
|
QTest::addColumn<int>("phasePowerLimit");
|
|
|
|
// ChargingInfo
|
|
QTest::addColumn<double>("acquisitionTolerance");
|
|
QTest::addColumn<int>("targetPercentage");
|
|
QTest::addColumn<int>("hoursLeftToTargetTime");
|
|
|
|
// Car information and states
|
|
QTest::addColumn<int>("carBatteryLevel");
|
|
QTest::addColumn<int>("carCapacity");
|
|
QTest::addColumn<int>("carMinChargingCurrent");
|
|
QTest::addColumn<int>("carPhaseCount");
|
|
|
|
// Charger information and states
|
|
QTest::addColumn<bool>("chargerConnected");
|
|
QTest::addColumn<bool>("chargerPower");
|
|
QTest::addColumn<QString>("chargerPhases");
|
|
QTest::addColumn<int>("chargerMaxChargingCurrent");
|
|
QTest::addColumn<int>("chargerMaxChargingCurrentMaxValue");
|
|
|
|
// Current meter power states
|
|
QTest::addColumn<QVariantMap>("meterPhasesPower");
|
|
|
|
// Desired result on the evCharger
|
|
QTest::addColumn<int>("expectedChargerMaxChargingCurrent");
|
|
QTest::addColumn<bool>("expectedChargerPower");
|
|
|
|
QTest::newRow("Default: 1 phase, p: 4kW, 16A, ON")
|
|
<< 32 // phase limit (A)
|
|
|
|
<< 0.5 // acquisitionTolerance
|
|
<< 100 // targetPercentage
|
|
<< 6 // hoursLeftToTargetTime
|
|
|
|
<< 80 // carBatteryLevel
|
|
<< 48 // carCapacity
|
|
<< 6 // carMinChargingCurrent
|
|
<< 1 // carPhaseCount
|
|
|
|
<< true // chargerConnected
|
|
<< false // chargerPower
|
|
<< "A" // chargerPhases
|
|
<< 6 // chargerMaxChargingCurrent
|
|
<< 16 //chargerMaxChargingCurrentMaxValue
|
|
|
|
<< QVariantMap({ {"A", 0000}, // W
|
|
{"B", 2000}, // W
|
|
{"C", -6000} }) // W
|
|
|
|
<< 16 // expectedChargerMaxChargingCurrent
|
|
<< true; // expectedChargerPower
|
|
|
|
|
|
QTest::newRow("Default: 1 phase, p: 2kW, 7A, ON")
|
|
<< 16 // phase limit (A)
|
|
|
|
<< 0.5 // acquisitionTolerance
|
|
<< 100 // targetPercentage
|
|
<< 6 // hoursLeftToTargetTime
|
|
|
|
<< 80 // carBatteryLevel
|
|
<< 48 // carCapacity
|
|
<< 6 // carMinChargingCurrent
|
|
<< 1 // carPhaseCount
|
|
|
|
<< true // chargerConnected
|
|
<< false // chargerPower
|
|
<< "A" // chargerPhases
|
|
<< 6 // chargerMaxChargingCurrent
|
|
<< 16 //chargerMaxChargingCurrentMaxValue
|
|
|
|
<< QVariantMap({ {"A", 2000}, // W
|
|
{"B", -2000}, // W
|
|
{"C", -2000} }) // W
|
|
|
|
<< 7 // expectedChargerMaxChargingCurrent
|
|
<< true; // expectedChargerPower
|
|
|
|
|
|
QTest::newRow("Default: 1 phase, p: -6kW, 13A, ON")
|
|
<< 16 // phase limit (A)
|
|
|
|
<< 0.5 // acquisitionTolerance
|
|
<< 100 // targetPercentage
|
|
<< 6 // hoursLeftToTargetTime
|
|
|
|
<< 80 // carBatteryLevel
|
|
<< 48 // carCapacity
|
|
<< 6 // carMinChargingCurrent
|
|
<< 1 // carPhaseCount
|
|
|
|
<< true // chargerConnected
|
|
<< false // chargerPower
|
|
<< "A" // chargerPhases
|
|
<< 6 // chargerMaxChargingCurrent
|
|
<< 32 //chargerMaxChargingCurrentMaxValue
|
|
|
|
<< QVariantMap({ {"A", -3000}, // W
|
|
{"B", 3000}, // W
|
|
{"C", -3000} }) // W
|
|
|
|
<< 13 // expectedChargerMaxChargingCurrent
|
|
<< true; // expectedChargerPower
|
|
|
|
QTest::newRow("Default: 2 phase, p: -8kW, 17A, ON")
|
|
<< 32 // phase limit (A)
|
|
|
|
<< 0.5 // acquisitionTolerance
|
|
<< 100 // targetPercentage
|
|
<< 6 // hoursLeftToTargetTime
|
|
|
|
<< 80 // carBatteryLevel
|
|
<< 48 // carCapacity
|
|
<< 6 // carMinChargingCurrent
|
|
<< 2 // carPhaseCount
|
|
|
|
<< true // chargerConnected
|
|
<< false // chargerPower
|
|
<< "AB" // chargerPhases
|
|
<< 6 // chargerMaxChargingCurrent
|
|
<< 32 //chargerMaxChargingCurrentMaxValue
|
|
|
|
<< QVariantMap({ {"A", -4000}, // W
|
|
{"B", -5000}, // W
|
|
{"C", 1000} }) // W
|
|
|
|
<< 17 // expectedChargerMaxChargingCurrent
|
|
<< true; // expectedChargerPower
|
|
|
|
QTest::newRow("Default: 3 phase, p: -3kW, 6A, ON")
|
|
<< 32 // phase limit (A)
|
|
|
|
<< 0.5 // acquisitionTolerance
|
|
<< 100 // targetPercentage
|
|
<< 6 // hoursLeftToTargetTime
|
|
|
|
<< 80 // carBatteryLevel
|
|
<< 48 // carCapacity
|
|
<< 6 // carMinChargingCurrent
|
|
<< 3 // carPhaseCount
|
|
|
|
<< true // chargerConnected
|
|
<< false // chargerPower
|
|
<< "ABC" // chargerPhases
|
|
<< 6 // chargerMaxChargingCurrent
|
|
<< 32 //chargerMaxChargingCurrentMaxValue
|
|
|
|
<< QVariantMap({ {"A", -1000}, // W
|
|
{"B", -1000}, // W
|
|
{"C", -1000} }) // W
|
|
|
|
<< 6 // expectedChargerMaxChargingCurrent
|
|
<< true; // expectedChargerPower
|
|
}
|
|
|
|
void TestCharging::testEcoMode()
|
|
{
|
|
QFETCH(int, phasePowerLimit);
|
|
|
|
QFETCH(double, acquisitionTolerance);
|
|
QFETCH(int, targetPercentage);
|
|
QFETCH(int, hoursLeftToTargetTime);
|
|
|
|
QFETCH(int, carBatteryLevel);
|
|
QFETCH(int, carCapacity);
|
|
QFETCH(int, carMinChargingCurrent);
|
|
QFETCH(int, carPhaseCount);
|
|
|
|
QFETCH(bool, chargerConnected);
|
|
QFETCH(bool, chargerPower);
|
|
QFETCH(QString, chargerPhases);
|
|
QFETCH(int, chargerMaxChargingCurrent);
|
|
QFETCH(int, chargerMaxChargingCurrentMaxValue);
|
|
|
|
QFETCH(QVariantMap, meterPhasesPower);
|
|
|
|
QFETCH(int, expectedChargerMaxChargingCurrent);
|
|
QFETCH(bool, expectedChargerPower);
|
|
|
|
QVariantMap params;
|
|
QVariant response, notification;
|
|
QNetworkReply *reply = nullptr;
|
|
QSignalSpy notificationSpy(m_mockTcpServer, &MockTcpServer::outgoingData);;
|
|
|
|
// Set phase power limit
|
|
params.clear(); response.clear(); notification.clear(); notificationSpy.clear();
|
|
response = injectAndWait("NymeaEnergy.SetPhasePowerLimit", QVariantMap({{"phasePowerLimit", phasePowerLimit}}));
|
|
QVERIFY(response.toMap().value("params").toMap().value("energyError").toString() == "EnergyErrorNoError");
|
|
|
|
// Add mock meter
|
|
params.clear(); response.clear(); notification.clear(); notificationSpy.clear();
|
|
QUuid meterThingId = addMeter();
|
|
QVERIFY2(!meterThingId.isNull(), "Did not receive valid ThingId");
|
|
if (notificationSpy.count() == 0) notificationSpy.wait();
|
|
notification = checkNotification(notificationSpy, "Energy.RootMeterChanged");
|
|
QCOMPARE(notification.toMap().value("params").toMap().value("rootMeterThingId").toUuid(), meterThingId);
|
|
|
|
// Make sure this is our root meter now
|
|
params.clear(); response.clear(); notification.clear(); notificationSpy.clear();
|
|
response = injectAndWait("Energy.GetRootMeter");
|
|
QCOMPARE(response.toMap().value("params").toMap().value("rootMeterThingId").toUuid(), meterThingId);
|
|
notificationSpy.clear();
|
|
|
|
// Set the meter values first so we have the desired execution o charging info changed, and not an extra iteration with 0 W on root meter
|
|
params.clear(); response.clear(); notification.clear(); notificationSpy.clear();
|
|
reply = setMeterStates(meterPhasesPower);
|
|
QSignalSpy setStatesReplySpy(reply, &QNetworkReply::finished);
|
|
if (setStatesReplySpy.count() == 0) setStatesReplySpy.wait();
|
|
QCOMPARE(reply->error(), QNetworkReply::NoError);
|
|
|
|
// No evaluation, there is no meter configured yet
|
|
|
|
// Add the charger
|
|
params.clear(); response.clear(); notification.clear(); notificationSpy.clear();
|
|
QUuid evChargerId = addCharger(chargerPhases, chargerMaxChargingCurrentMaxValue);
|
|
QVERIFY2(!evChargerId.isNull(), "Did not receive valid ThingId");
|
|
if (notificationSpy.count() == 0) notificationSpy.wait();
|
|
checkNotification(notificationSpy, "Integrations.ThingAdded");
|
|
|
|
// Add the car
|
|
params.clear(); response.clear(); notification.clear(); notificationSpy.clear();
|
|
QUuid assignedCarId = addCar();
|
|
QVERIFY2(!assignedCarId.isNull(), "Did not receive valid ThingId");
|
|
if (notificationSpy.count() == 0) notificationSpy.wait();
|
|
checkNotification(notificationSpy, "Integrations.ThingAdded");
|
|
|
|
// Set aquisition tolerance
|
|
params.clear(); response.clear(); notification.clear(); notificationSpy.clear();
|
|
params.insert("acquisitionTolerance", acquisitionTolerance);
|
|
response = injectAndWait("NymeaEnergy.SetAcquisitionTolerance", params);
|
|
verifyEnergyError(response);
|
|
|
|
// Set states of car
|
|
params.clear(); response.clear(); notification.clear(); notificationSpy.clear();
|
|
reply = setCarStates(carBatteryLevel, carCapacity, carMinChargingCurrent, carPhaseCount);
|
|
QSignalSpy setCarStatesReplySpy(reply, &QNetworkReply::finished);
|
|
if (setCarStatesReplySpy.count() == 0) setCarStatesReplySpy.wait();
|
|
QCOMPARE(reply->error(), QNetworkReply::NoError);
|
|
|
|
// Set states of the charger
|
|
|
|
uint effectivePhaseCount = qMin((uint)carPhaseCount, Electricity::getPhaseCount(Electricity::convertPhasesFromString(chargerPhases)));
|
|
QString usedPhases;
|
|
if (effectivePhaseCount >= 1)
|
|
usedPhases.append("A");
|
|
|
|
if (effectivePhaseCount >= 2)
|
|
usedPhases.append("B");
|
|
|
|
if (effectivePhaseCount >= 3)
|
|
usedPhases.append("C");
|
|
|
|
params.clear(); response.clear(); notification.clear(); notificationSpy.clear();
|
|
reply = setChargerStates(chargerConnected, chargerPower, true, usedPhases, chargerMaxChargingCurrent, chargerMaxChargingCurrentMaxValue);
|
|
QSignalSpy setChargerStatesReplySpy(reply, &QNetworkReply::finished);
|
|
if (setChargerStatesReplySpy.count() == 0) setChargerStatesReplySpy.wait();
|
|
QCOMPARE(reply->error(), QNetworkReply::NoError);
|
|
|
|
|
|
|
|
// Set charging info with our charger and car, this should trigger the evaluation
|
|
QVariantMap chargingInfoMap;
|
|
chargingInfoMap.insert("evChargerId", evChargerId);
|
|
if (!assignedCarId.isNull())
|
|
chargingInfoMap.insert("assignedCarId", assignedCarId);
|
|
|
|
chargingInfoMap.insert("chargingMode", "ChargingModeEcoWithTargetTime");
|
|
chargingInfoMap.insert("endDateTime", QDateTime::currentDateTime().addSecs(hoursLeftToTargetTime * 3600).toMSecsSinceEpoch() / 1000);
|
|
chargingInfoMap.insert("targetPercentage", targetPercentage);
|
|
|
|
response = injectAndWait("NymeaEnergy.SetChargingInfo", QVariantMap({{"chargingInfo", chargingInfoMap}}));
|
|
verifyEnergyError(response);
|
|
|
|
// Verify if the charger has been set correctly
|
|
reply = getActionHistory(m_mockChargerDefaultPort);
|
|
QSignalSpy actionHistoryReplySpy(reply, &QNetworkReply::finished);
|
|
if (actionHistoryReplySpy.count() == 0) actionHistoryReplySpy.wait();
|
|
QCOMPARE(reply->error(), QNetworkReply::NoError);
|
|
QByteArray data = reply->readAll();
|
|
QJsonDocument jsonDoc = QJsonDocument::fromJson(data);
|
|
QVariantList actionHistory = jsonDoc.toVariant().toList();
|
|
//qCDebug(dcTests()) << qUtf8Printable(jsonDoc.toJson(QJsonDocument::Indented));
|
|
|
|
// Make sure we got the correct actions
|
|
if (expectedChargerMaxChargingCurrent != 0)
|
|
QVERIFY(verifyActionExecuted(actionHistory, "maxChargingCurrent"));
|
|
|
|
QVERIFY(verifyActionExecuted(actionHistory, "power"));
|
|
|
|
if (expectedChargerMaxChargingCurrent != 0)
|
|
QCOMPARE(getLastValueFromExecutedAction(actionHistory, "maxChargingCurrent", "maxChargingCurrent"), QVariant(expectedChargerMaxChargingCurrent));
|
|
|
|
QCOMPARE(getLastValueFromExecutedAction(actionHistory, "power", "power"), QVariant(expectedChargerPower));
|
|
|
|
removeDevices();
|
|
}
|
|
|
|
void TestCharging::testEcoModePhaseSwitching_data()
|
|
{
|
|
// Houshold info
|
|
QTest::addColumn<int>("phasePowerLimit");
|
|
|
|
// ChargingInfo
|
|
QTest::addColumn<double>("acquisitionTolerance");
|
|
QTest::addColumn<int>("targetPercentage");
|
|
QTest::addColumn<int>("hoursLeftToTargetTime");
|
|
|
|
// Car information and states
|
|
QTest::addColumn<int>("carBatteryLevel");
|
|
QTest::addColumn<int>("carCapacity");
|
|
QTest::addColumn<int>("carMinChargingCurrent");
|
|
QTest::addColumn<int>("carPhaseCount");
|
|
|
|
// Charger information and states
|
|
QTest::addColumn<bool>("chargerConnected");
|
|
QTest::addColumn<bool>("chargerPower");
|
|
QTest::addColumn<QString>("chargerPhases");
|
|
QTest::addColumn<int>("chargerMaxChargingCurrent");
|
|
QTest::addColumn<int>("chargerMaxChargingCurrentMaxValue");
|
|
QTest::addColumn<int>("chargerDesiredPhaseCount");
|
|
|
|
// Current meter power states
|
|
QTest::addColumn<QVariantMap>("meterPhasesPower");
|
|
|
|
// Desired result on the evCharger
|
|
QTest::addColumn<int>("expectedChargerMaxChargingCurrent");
|
|
QTest::addColumn<bool>("expectedChargerPower");
|
|
QTest::addColumn<int>("expectedChargerDesiredPhaseCount");
|
|
|
|
|
|
QTest::newRow("1 phase, p: 3,8kW, 10A, ON, No phase switch, no aquisition")
|
|
<< 32 // phase limit (A)
|
|
|
|
<< 1.0 // acquisitionTolerance
|
|
<< 100 // targetPercentage
|
|
<< 6 // hoursLeftToTargetTime
|
|
|
|
<< 80 // carBatteryLevel
|
|
<< 48 // carCapacity
|
|
<< 6 // carMinChargingCurrent
|
|
<< 3 // carPhaseCount
|
|
|
|
<< true // chargerConnected
|
|
<< true // chargerPower
|
|
<< "ABC" // chargerPhases
|
|
<< 10 // chargerMaxChargingCurrent
|
|
<< 16 // chargerMaxChargingCurrentMaxValue
|
|
<< 1 // chargerDesiredPhaseCount
|
|
|
|
// meterPhasesPower [W]
|
|
<< QVariantMap({ {"A", 2300},
|
|
{"B", -1900},
|
|
{"C", -2000} })
|
|
|
|
<< 16 // expectedChargerMaxChargingCurrent
|
|
<< true // expectedChargerPower
|
|
<< 1; // expectedChargerDesiredPhaseCount
|
|
|
|
|
|
QTest::newRow("1 phase, p: 3,8kW, 6A, ON, Switch 3 phase with aquisition")
|
|
<< 32 // phase limit (A)
|
|
|
|
<< 0.5 // acquisitionTolerance
|
|
<< 100 // targetPercentage
|
|
<< 6 // hoursLeftToTargetTime
|
|
|
|
<< 80 // carBatteryLevel
|
|
<< 48 // carCapacity
|
|
<< 6 // carMinChargingCurrent
|
|
<< 3 // carPhaseCount
|
|
|
|
<< true // chargerConnected
|
|
<< false // chargerPower
|
|
<< "ABC" // chargerPhases
|
|
<< 6 // chargerMaxChargingCurrent
|
|
<< 16 // chargerMaxChargingCurrentMaxValue
|
|
<< 1 // chargerDesiredPhaseCount
|
|
|
|
// meterPhasesPower [W]
|
|
<< QVariantMap({ {"A", 0000},
|
|
{"B", 2000},
|
|
{"C", -5800} })
|
|
|
|
<< 6 // expectedChargerMaxChargingCurrent
|
|
<< true // expectedChargerPower
|
|
<< 3; // expectedChargerDesiredPhaseCount
|
|
|
|
|
|
QTest::newRow("3 phase, p: 4kW, 6A, ON, Switch 1 -> 3")
|
|
<< 32 // phase limit (A)
|
|
|
|
<< 0.5 // acquisitionTolerance
|
|
<< 100 // targetPercentage
|
|
<< 6 // hoursLeftToTargetTime
|
|
|
|
<< 80 // carBatteryLevel
|
|
<< 48 // carCapacity
|
|
<< 6 // carMinChargingCurrent
|
|
<< 3 // carPhaseCount
|
|
|
|
<< true // chargerConnected
|
|
<< false // chargerPower
|
|
<< "ABC" // chargerPhases
|
|
<< 6 // chargerMaxChargingCurrent
|
|
<< 16 // chargerMaxChargingCurrentMaxValue
|
|
<< 1 // chargerDesiredPhaseCount
|
|
|
|
// meterPhasesPower [W]
|
|
<< QVariantMap({ {"A", 0000},
|
|
{"B", 2000},
|
|
{"C", -6000} })
|
|
|
|
<< 6 // expectedChargerMaxChargingCurrent
|
|
<< true // expectedChargerPower
|
|
<< 3; // expectedChargerDesiredPhaseCount
|
|
|
|
QTest::newRow("3 phase, p: 800W, 6A, ON, Switch 3 -> 1")
|
|
<< 32 // phase limit (A)
|
|
|
|
<< 0.5 // acquisitionTolerance
|
|
<< 100 // targetPercentage
|
|
<< 6 // hoursLeftToTargetTime
|
|
|
|
<< 80 // carBatteryLevel
|
|
<< 48 // carCapacity
|
|
<< 6 // carMinChargingCurrent
|
|
<< 3 // carPhaseCount
|
|
|
|
<< true // chargerConnected
|
|
<< false // chargerPower
|
|
<< "ABC" // chargerPhases
|
|
<< 10 // chargerMaxChargingCurrent
|
|
<< 16 // chargerMaxChargingCurrentMaxValue
|
|
<< 3 // chargerDesiredPhaseCount
|
|
|
|
// meterPhasesPower [W]
|
|
<< QVariantMap({ {"A", 0000},
|
|
{"B", 900},
|
|
{"C", -1700} })
|
|
|
|
<< 6 // expectedChargerMaxChargingCurrent
|
|
<< true // expectedChargerPower
|
|
<< 1; // expectedChargerDesiredPhaseCount
|
|
|
|
QTest::newRow("3 phase charger, p: 1,38kW, 6A, ON, Start 1-phase")
|
|
<< 32 // phase limit (A)
|
|
|
|
<< 0.01 // acquisitionTolerance
|
|
<< 100 // targetPercentage
|
|
<< 6 // hoursLeftToTargetTime
|
|
|
|
<< 80 // carBatteryLevel
|
|
<< 48 // carCapacity
|
|
<< 6 // carMinChargingCurrent
|
|
<< 3 // carPhaseCount
|
|
|
|
<< true // chargerConnected
|
|
<< false // chargerPower
|
|
<< "ABC" // chargerPhases
|
|
<< 6 // chargerMaxChargingCurrent
|
|
<< 16 // chargerMaxChargingCurrentMaxValue
|
|
<< 3 // chargerDesiredPhaseCount
|
|
|
|
// meterPhasesPower [W]
|
|
<< QVariantMap({ {"A", -1400},
|
|
{"B", 2000},
|
|
{"C", -2000} })
|
|
|
|
<< 6 // expectedChargerMaxChargingCurrent
|
|
<< true // expectedChargerPower
|
|
<< 1; // expectedChargerDesiredPhaseCount
|
|
|
|
QTest::newRow("3 phase charger, p: 200W, 6A, ON, Start 1-phase")
|
|
<< 32 // phase limit (A)
|
|
|
|
<< 0.01 // acquisitionTolerance
|
|
<< 100 // targetPercentage
|
|
<< 6 // hoursLeftToTargetTime
|
|
|
|
<< 80 // carBatteryLevel
|
|
<< 48 // carCapacity
|
|
<< 6 // carMinChargingCurrent
|
|
<< 3 // carPhaseCount
|
|
|
|
<< true // chargerConnected
|
|
<< false // chargerPower
|
|
<< "ABC" // chargerPhases
|
|
<< 6 // chargerMaxChargingCurrent
|
|
<< 16 // chargerMaxChargingCurrentMaxValue
|
|
<< 3 // chargerDesiredPhaseCount
|
|
|
|
// meterPhasesPower [W]
|
|
<< QVariantMap({ {"A", -200},
|
|
{"B", 2000},
|
|
{"C", -2000} })
|
|
|
|
<< 6 // expectedChargerMaxChargingCurrent
|
|
<< true // expectedChargerPower
|
|
<< 1; // expectedChargerDesiredPhaseCount
|
|
|
|
QTest::newRow("3 phase charger, p: 3,7kW, 6A, ON, Start 3-phase")
|
|
<< 32 // phase limit (A)
|
|
|
|
<< 0.01 // acquisitionTolerance
|
|
<< 100 // targetPercentage
|
|
<< 6 // hoursLeftToTargetTime
|
|
|
|
<< 80 // carBatteryLevel
|
|
<< 48 // carCapacity
|
|
<< 6 // carMinChargingCurrent
|
|
<< 3 // carPhaseCount
|
|
|
|
<< true // chargerConnected
|
|
<< false // chargerPower
|
|
<< "ABC" // chargerPhases
|
|
<< 6 // chargerMaxChargingCurrent
|
|
<< 16 // chargerMaxChargingCurrentMaxValue
|
|
<< 3 // chargerDesiredPhaseCount
|
|
|
|
// meterPhasesPower [W]
|
|
<< QVariantMap({ {"A", -3700},
|
|
{"B", 2000},
|
|
{"C", -2000} })
|
|
|
|
<< 6 // expectedChargerMaxChargingCurrent
|
|
<< true // expectedChargerPower
|
|
<< 3; // expectedChargerDesiredPhaseCount
|
|
}
|
|
|
|
void TestCharging::testEcoModePhaseSwitching()
|
|
{
|
|
QFETCH(int, phasePowerLimit);
|
|
|
|
QFETCH(double, acquisitionTolerance);
|
|
QFETCH(int, targetPercentage);
|
|
QFETCH(int, hoursLeftToTargetTime);
|
|
|
|
QFETCH(int, carBatteryLevel);
|
|
QFETCH(int, carCapacity);
|
|
QFETCH(int, carMinChargingCurrent);
|
|
QFETCH(int, carPhaseCount);
|
|
|
|
QFETCH(bool, chargerConnected);
|
|
QFETCH(bool, chargerPower);
|
|
QFETCH(QString, chargerPhases);
|
|
QFETCH(int, chargerMaxChargingCurrent);
|
|
QFETCH(int, chargerMaxChargingCurrentMaxValue);
|
|
QFETCH(int, chargerDesiredPhaseCount);
|
|
|
|
QFETCH(QVariantMap, meterPhasesPower);
|
|
|
|
QFETCH(int, expectedChargerMaxChargingCurrent);
|
|
QFETCH(bool, expectedChargerPower);
|
|
QFETCH(int, expectedChargerDesiredPhaseCount);
|
|
|
|
Q_UNUSED(expectedChargerDesiredPhaseCount)
|
|
|
|
QVariantMap params;
|
|
QVariant response, notification;
|
|
QNetworkReply *reply = nullptr;
|
|
QSignalSpy notificationSpy(m_mockTcpServer, &MockTcpServer::outgoingData);;
|
|
|
|
// Set phase power limit
|
|
params.clear(); response.clear(); notification.clear(); notificationSpy.clear();
|
|
response = injectAndWait("NymeaEnergy.SetPhasePowerLimit", QVariantMap({{"phasePowerLimit", phasePowerLimit}}));
|
|
QVERIFY(response.toMap().value("params").toMap().value("energyError").toString() == "EnergyErrorNoError");
|
|
|
|
// Add mock meter
|
|
params.clear(); response.clear(); notification.clear(); notificationSpy.clear();
|
|
QUuid meterThingId = addMeter();
|
|
QVERIFY2(!meterThingId.isNull(), "Did not receive valid ThingId");
|
|
if (notificationSpy.count() == 0) notificationSpy.wait();
|
|
notification = checkNotification(notificationSpy, "Energy.RootMeterChanged");
|
|
QCOMPARE(notification.toMap().value("params").toMap().value("rootMeterThingId").toUuid(), meterThingId);
|
|
|
|
// Make sure this is our root meter now
|
|
params.clear(); response.clear(); notification.clear(); notificationSpy.clear();
|
|
response = injectAndWait("Energy.GetRootMeter");
|
|
QCOMPARE(response.toMap().value("params").toMap().value("rootMeterThingId").toUuid(), meterThingId);
|
|
notificationSpy.clear();
|
|
|
|
// Set the meter values first so we have the desired execution o charging info changed, and not an extra iteration with 0 W on root meter
|
|
params.clear(); response.clear(); notification.clear(); notificationSpy.clear();
|
|
reply = setMeterStates(meterPhasesPower);
|
|
QSignalSpy setStatesReplySpy(reply, &QNetworkReply::finished);
|
|
if (setStatesReplySpy.count() == 0) setStatesReplySpy.wait();
|
|
QCOMPARE(reply->error(), QNetworkReply::NoError);
|
|
|
|
// No evaluation, there is no meter configured yet
|
|
|
|
// Add the charger
|
|
params.clear(); response.clear(); notification.clear(); notificationSpy.clear();
|
|
QUuid evChargerId = addChargerWithPhaseCountSwitching(chargerPhases, chargerMaxChargingCurrentMaxValue);
|
|
QVERIFY2(!evChargerId.isNull(), "Did not receive valid ThingId");
|
|
if (notificationSpy.count() == 0) notificationSpy.wait();
|
|
checkNotification(notificationSpy, "Integrations.ThingAdded");
|
|
|
|
// Add the car
|
|
params.clear(); response.clear(); notification.clear(); notificationSpy.clear();
|
|
QUuid assignedCarId = addCar();
|
|
QVERIFY2(!assignedCarId.isNull(), "Did not receive valid ThingId");
|
|
if (notificationSpy.count() == 0) notificationSpy.wait();
|
|
checkNotification(notificationSpy, "Integrations.ThingAdded");
|
|
|
|
// Set aquisition tolerance
|
|
params.clear(); response.clear(); notification.clear(); notificationSpy.clear();
|
|
params.insert("acquisitionTolerance", acquisitionTolerance);
|
|
response = injectAndWait("NymeaEnergy.SetAcquisitionTolerance", params);
|
|
verifyEnergyError(response);
|
|
|
|
// Set states of car
|
|
params.clear(); response.clear(); notification.clear(); notificationSpy.clear();
|
|
reply = setCarStates(carBatteryLevel, carCapacity, carMinChargingCurrent, carPhaseCount);
|
|
QSignalSpy setCarStatesReplySpy(reply, &QNetworkReply::finished);
|
|
if (setCarStatesReplySpy.count() == 0) setCarStatesReplySpy.wait();
|
|
QCOMPARE(reply->error(), QNetworkReply::NoError);
|
|
|
|
// Set states of the charger
|
|
|
|
uint effectivePhaseCount = qMin((uint)carPhaseCount, Electricity::getPhaseCount(Electricity::convertPhasesFromString(chargerPhases)));
|
|
QString usedPhases;
|
|
if (effectivePhaseCount >= 1)
|
|
usedPhases.append("A");
|
|
|
|
if (effectivePhaseCount >= 2)
|
|
usedPhases.append("B");
|
|
|
|
if (effectivePhaseCount >= 3)
|
|
usedPhases.append("C");
|
|
|
|
params.clear(); response.clear(); notification.clear(); notificationSpy.clear();
|
|
reply = setChargerWithPhaseCountSwitchingStates(chargerConnected, chargerPower, true, usedPhases, chargerMaxChargingCurrent, chargerMaxChargingCurrentMaxValue, chargerDesiredPhaseCount);
|
|
QSignalSpy setChargerStatesReplySpy(reply, &QNetworkReply::finished);
|
|
if (setChargerStatesReplySpy.count() == 0) setChargerStatesReplySpy.wait();
|
|
QCOMPARE(reply->error(), QNetworkReply::NoError);
|
|
|
|
// Set charging info with our charger and car, this should trigger the evaluation
|
|
QVariantMap chargingInfoMap;
|
|
chargingInfoMap.insert("evChargerId", evChargerId);
|
|
if (!assignedCarId.isNull())
|
|
chargingInfoMap.insert("assignedCarId", assignedCarId);
|
|
|
|
chargingInfoMap.insert("chargingMode", "ChargingModeEcoWithTargetTime");
|
|
chargingInfoMap.insert("endDateTime", QDateTime::currentDateTime().addSecs(hoursLeftToTargetTime * 3600).toMSecsSinceEpoch() / 1000);
|
|
chargingInfoMap.insert("targetPercentage", targetPercentage);
|
|
|
|
// Trigger evaluation
|
|
|
|
response = injectAndWait("NymeaEnergy.SetChargingInfo", QVariantMap({{"chargingInfo", chargingInfoMap}}));
|
|
verifyEnergyError(response);
|
|
|
|
// Verify if the charger has been set correctly
|
|
reply = getActionHistory(m_mockChargerWithPhaseCountSwitchingDefaultPort);
|
|
QSignalSpy actionHistoryReplySpy(reply, &QNetworkReply::finished);
|
|
if (actionHistoryReplySpy.count() == 0) actionHistoryReplySpy.wait();
|
|
QCOMPARE(reply->error(), QNetworkReply::NoError);
|
|
QByteArray data = reply->readAll();
|
|
QJsonDocument jsonDoc = QJsonDocument::fromJson(data);
|
|
QVariantList actionHistory = jsonDoc.toVariant().toList();
|
|
//qCDebug(dcTests()) << qUtf8Printable(jsonDoc.toJson(QJsonDocument::Indented));
|
|
|
|
// Make sure we got the correct actions
|
|
if (expectedChargerMaxChargingCurrent != 0)
|
|
QVERIFY(verifyActionExecuted(actionHistory, "maxChargingCurrent"));
|
|
|
|
QVERIFY(verifyActionExecuted(actionHistory, "power"));
|
|
|
|
if (expectedChargerMaxChargingCurrent != 0)
|
|
QCOMPARE(getLastValueFromExecutedAction(actionHistory, "maxChargingCurrent", "maxChargingCurrent"), QVariant(expectedChargerMaxChargingCurrent));
|
|
|
|
if (expectedChargerDesiredPhaseCount != chargerDesiredPhaseCount)
|
|
QCOMPARE(getLastValueFromExecutedAction(actionHistory, "desiredPhaseCount", "desiredPhaseCount"), QVariant(expectedChargerDesiredPhaseCount));
|
|
|
|
QCOMPARE(getLastValueFromExecutedAction(actionHistory, "power", "power"), QVariant(expectedChargerPower));
|
|
|
|
removeDevices();
|
|
}
|
|
|
|
void TestCharging::testBatteryLevelConsideration_data()
|
|
{
|
|
// Houshold info
|
|
QTest::addColumn<int>("phasePowerLimit");
|
|
|
|
// ChargingInfo
|
|
QTest::addColumn<double>("acquisitionTolerance");
|
|
QTest::addColumn<double>("batteryLevelConsideration");
|
|
|
|
// Car information and states
|
|
QTest::addColumn<int>("carBatteryLevel");
|
|
QTest::addColumn<int>("carCapacity");
|
|
QTest::addColumn<int>("carMinChargingCurrent");
|
|
QTest::addColumn<int>("carPhaseCount");
|
|
|
|
// Charger information and states
|
|
QTest::addColumn<bool>("chargerConnected");
|
|
QTest::addColumn<bool>("chargerPower");
|
|
QTest::addColumn<QString>("chargerPhases");
|
|
QTest::addColumn<int>("chargerMaxChargingCurrent");
|
|
QTest::addColumn<int>("chargerMaxChargingCurrentMaxValue");
|
|
QTest::addColumn<int>("chargerDesiredPhaseCount");
|
|
|
|
// Current meter power states
|
|
QTest::addColumn<QVariantMap>("meterPhasesPower");
|
|
|
|
// Current energy storage states
|
|
QTest::addColumn<double>("energyStorageBatteryLevel");
|
|
QTest::addColumn<double>("energyStorageCurrentPower");
|
|
|
|
// Desired result on the evCharger
|
|
QTest::addColumn<int>("expectedChargerMaxChargingCurrent"); // 0 for Unchanged / not executed
|
|
QTest::addColumn<bool>("expectedChargerPower");
|
|
QTest::addColumn<int>("expectedChargerDesiredPhaseCount");
|
|
|
|
QTest::newRow("Default: 1 phase, battery: 80% 3,6kW -> 16A, ON, 1P")
|
|
<< 32 // phase limit (A)
|
|
|
|
<< 0.5 // acquisitionTolerance
|
|
<< 0.8 // batteryLevelConsideration
|
|
|
|
<< 80 // carBatteryLevel
|
|
<< 48 // carCapacity
|
|
<< 6 // carMinChargingCurrent
|
|
<< 1 // carPhaseCount
|
|
|
|
<< true // chargerConnected
|
|
<< false // chargerPower
|
|
<< "A" // chargerPhases
|
|
<< 6 // chargerMaxChargingCurrent
|
|
<< 16 // chargerMaxChargingCurrentMaxValue
|
|
<< 1 // chargerDesiredPhaseCount
|
|
|
|
// meterPhasesPower [W]
|
|
<< QVariantMap({ {"A", 0},
|
|
{"B", 0 },
|
|
{"C", 0} })
|
|
|
|
<< 80.0 // energyStorageBatteryLevel
|
|
<< 3680.0 // energyStorageCurrentPower
|
|
|
|
<< 16 // expectedChargerMaxChargingCurrent
|
|
<< true // expectedChargerPower
|
|
<< 1; // expectedChargerDesiredPhaseCount
|
|
|
|
|
|
QTest::newRow("Default: 1 phase, battery: 79% 3,6kW -> 6A, OFF, 1P")
|
|
<< 32 // phase limit (A)
|
|
|
|
<< 0.5 // acquisitionTolerance
|
|
<< 0.8 // batteryLevelConsideration
|
|
|
|
<< 80 // carBatteryLevel
|
|
<< 48 // carCapacity
|
|
<< 6 // carMinChargingCurrent
|
|
<< 1 // carPhaseCount
|
|
|
|
<< true // chargerConnected
|
|
<< false // chargerPower
|
|
<< "A" // chargerPhases
|
|
<< 6 // chargerMaxChargingCurrent
|
|
<< 16 // chargerMaxChargingCurrentMaxValue
|
|
<< 1 // chargerDesiredPhaseCount
|
|
|
|
// meterPhasesPower [W]
|
|
<< QVariantMap({ {"A", 0},
|
|
{"B", 0 },
|
|
{"C", 0} })
|
|
|
|
<< 79.0 // energyStorageBatteryLevel
|
|
<< 3680.0 // energyStorageCurrentPower
|
|
|
|
<< 0 // expectedChargerMaxChargingCurrent
|
|
<< false // expectedChargerPower
|
|
<< 1; // expectedChargerDesiredPhaseCount
|
|
|
|
|
|
QTest::newRow("Default: 1 phase, battery: 81% -600W -> 6A, OFF, 1P")
|
|
<< 32 // phase limit (A)
|
|
|
|
<< 0.5 // acquisitionTolerance
|
|
<< 0.8 // batteryLevelConsideration
|
|
|
|
<< 80 // carBatteryLevel
|
|
<< 48 // carCapacity
|
|
<< 6 // carMinChargingCurrent
|
|
<< 1 // carPhaseCount
|
|
|
|
<< true // chargerConnected
|
|
<< false // chargerPower
|
|
<< "A" // chargerPhases
|
|
<< 6 // chargerMaxChargingCurrent
|
|
<< 16 // chargerMaxChargingCurrentMaxValue
|
|
<< 1 // chargerDesiredPhaseCount
|
|
|
|
// meterPhasesPower [W]
|
|
<< QVariantMap({ {"A", -600},
|
|
{"B", 0 },
|
|
{"C", 0} })
|
|
|
|
<< 81.0 // energyStorageBatteryLevel
|
|
<< -600.0 // energyStorageCurrentPower
|
|
|
|
<< 0 // expectedChargerMaxChargingCurrent
|
|
<< false // expectedChargerPower
|
|
<< 1; // expectedChargerDesiredPhaseCount
|
|
|
|
|
|
QTest::newRow("Default: 1 phase, battery: 81% -600W -> 6A, OFF, 1P")
|
|
<< 32 // phase limit (A)
|
|
|
|
<< 0.5 // acquisitionTolerance
|
|
<< 0.8 // batteryLevelConsideration
|
|
|
|
<< 80 // carBatteryLevel
|
|
<< 48 // carCapacity
|
|
<< 6 // carMinChargingCurrent
|
|
<< 1 // carPhaseCount
|
|
|
|
<< true // chargerConnected
|
|
<< false // chargerPower
|
|
<< "A" // chargerPhases
|
|
<< 6 // chargerMaxChargingCurrent
|
|
<< 16 // chargerMaxChargingCurrentMaxValue
|
|
<< 1 // chargerDesiredPhaseCount
|
|
|
|
// meterPhasesPower [W]
|
|
<< QVariantMap({ {"A", -600},
|
|
{"B", 0 },
|
|
{"C", 0} })
|
|
|
|
<< 81.5 // energyStorageBatteryLevel
|
|
<< 100.0 // energyStorageCurrentPower
|
|
|
|
<< 6 // expectedChargerMaxChargingCurrent
|
|
<< true // expectedChargerPower
|
|
<< 1; // expectedChargerDesiredPhaseCount
|
|
|
|
|
|
}
|
|
|
|
void TestCharging::testBatteryLevelConsideration()
|
|
{
|
|
QFETCH(int, phasePowerLimit);
|
|
|
|
QFETCH(double, acquisitionTolerance);
|
|
QFETCH(double, batteryLevelConsideration);
|
|
|
|
QFETCH(int, carBatteryLevel);
|
|
QFETCH(int, carCapacity);
|
|
QFETCH(int, carMinChargingCurrent);
|
|
QFETCH(int, carPhaseCount);
|
|
|
|
QFETCH(bool, chargerConnected);
|
|
QFETCH(bool, chargerPower);
|
|
QFETCH(QString, chargerPhases);
|
|
QFETCH(int, chargerMaxChargingCurrent);
|
|
QFETCH(int, chargerMaxChargingCurrentMaxValue);
|
|
QFETCH(int, chargerDesiredPhaseCount);
|
|
|
|
QFETCH(QVariantMap, meterPhasesPower);
|
|
|
|
QFETCH(double, energyStorageBatteryLevel);
|
|
QFETCH(double, energyStorageCurrentPower);
|
|
|
|
QFETCH(int, expectedChargerMaxChargingCurrent); // 0 for Unchanged / not executed
|
|
QFETCH(bool, expectedChargerPower);
|
|
QFETCH(int, expectedChargerDesiredPhaseCount);
|
|
|
|
Q_UNUSED(expectedChargerDesiredPhaseCount)
|
|
|
|
QVariantMap params;
|
|
QVariant response, notification;
|
|
QNetworkReply *reply = nullptr;
|
|
QSignalSpy notificationSpy(m_mockTcpServer, &MockTcpServer::outgoingData);;
|
|
|
|
// Set phase power limit
|
|
params.clear(); response.clear(); notification.clear(); notificationSpy.clear();
|
|
response = injectAndWait("NymeaEnergy.SetPhasePowerLimit", QVariantMap({{"phasePowerLimit", phasePowerLimit}}));
|
|
QVERIFY(response.toMap().value("params").toMap().value("energyError").toString() == "EnergyErrorNoError");
|
|
|
|
// Add mock meter
|
|
params.clear(); response.clear(); notification.clear(); notificationSpy.clear();
|
|
QUuid meterThingId = addMeter();
|
|
QVERIFY2(!meterThingId.isNull(), "Did not receive valid ThingId");
|
|
if (notificationSpy.count() == 0) notificationSpy.wait();
|
|
notification = checkNotification(notificationSpy, "Energy.RootMeterChanged");
|
|
QCOMPARE(notification.toMap().value("params").toMap().value("rootMeterThingId").toUuid(), meterThingId);
|
|
|
|
// Make sure this is our root meter now
|
|
params.clear(); response.clear(); notification.clear(); notificationSpy.clear();
|
|
response = injectAndWait("Energy.GetRootMeter");
|
|
QCOMPARE(response.toMap().value("params").toMap().value("rootMeterThingId").toUuid(), meterThingId);
|
|
notificationSpy.clear();
|
|
|
|
|
|
// Set the meter values first so we have the desired execution o charging info changed, and not an extra iteration with 0 W on root meter
|
|
params.clear(); response.clear(); notification.clear(); notificationSpy.clear();
|
|
reply = setMeterStates(meterPhasesPower);
|
|
QSignalSpy setStatesReplySpy(reply, &QNetworkReply::finished);
|
|
if (setStatesReplySpy.count() == 0) setStatesReplySpy.wait();
|
|
QCOMPARE(reply->error(), QNetworkReply::NoError);
|
|
|
|
// No evaluation, there is no meter configured yet
|
|
|
|
// Add the charger
|
|
params.clear(); response.clear(); notification.clear(); notificationSpy.clear();
|
|
QUuid evChargerId = addChargerWithPhaseCountSwitching(chargerPhases, chargerMaxChargingCurrentMaxValue);
|
|
QVERIFY2(!evChargerId.isNull(), "Did not receive valid ThingId");
|
|
if (notificationSpy.count() == 0) notificationSpy.wait();
|
|
checkNotification(notificationSpy, "Integrations.ThingAdded");
|
|
|
|
// Add the car
|
|
params.clear(); response.clear(); notification.clear(); notificationSpy.clear();
|
|
QUuid assignedCarId = addCar();
|
|
QVERIFY2(!assignedCarId.isNull(), "Did not receive valid ThingId");
|
|
if (notificationSpy.count() == 0) notificationSpy.wait();
|
|
checkNotification(notificationSpy, "Integrations.ThingAdded");
|
|
|
|
// Add energy storage
|
|
params.clear(); response.clear(); notification.clear(); notificationSpy.clear();
|
|
QUuid energyStorageId = addEnergyStorage();
|
|
QVERIFY2(!energyStorageId.isNull(), "Did not receive valid ThingId");
|
|
if (notificationSpy.count() == 0) notificationSpy.wait();
|
|
checkNotification(notificationSpy, "Integrations.ThingAdded");
|
|
|
|
// Set aquisition tolerance
|
|
params.clear(); response.clear(); notification.clear(); notificationSpy.clear();
|
|
params.insert("acquisitionTolerance", acquisitionTolerance);
|
|
response = injectAndWait("NymeaEnergy.SetAcquisitionTolerance", params);
|
|
verifyEnergyError(response);
|
|
|
|
// Set battery level consideration
|
|
params.clear(); response.clear(); notification.clear(); notificationSpy.clear();
|
|
params.insert("batteryLevelConsideration", batteryLevelConsideration);
|
|
response = injectAndWait("NymeaEnergy.SetBatteryLevelConsideration", params);
|
|
verifyEnergyError(response);
|
|
|
|
// Set states of car
|
|
params.clear(); response.clear(); notification.clear(); notificationSpy.clear();
|
|
reply = setCarStates(carBatteryLevel, carCapacity, carMinChargingCurrent, carPhaseCount);
|
|
QSignalSpy setCarStatesReplySpy(reply, &QNetworkReply::finished);
|
|
if (setCarStatesReplySpy.count() == 0) setCarStatesReplySpy.wait();
|
|
QCOMPARE(reply->error(), QNetworkReply::NoError);
|
|
|
|
// Set states of the charger
|
|
|
|
uint effectivePhaseCount = qMin((uint)carPhaseCount, Electricity::getPhaseCount(Electricity::convertPhasesFromString(chargerPhases)));
|
|
QString usedPhases;
|
|
if (effectivePhaseCount >= 1)
|
|
usedPhases.append("A");
|
|
|
|
if (effectivePhaseCount >= 2)
|
|
usedPhases.append("B");
|
|
|
|
if (effectivePhaseCount >= 3)
|
|
usedPhases.append("C");
|
|
|
|
params.clear(); response.clear(); notification.clear(); notificationSpy.clear();
|
|
reply = setChargerWithPhaseCountSwitchingStates(chargerConnected, chargerPower, true, usedPhases, chargerMaxChargingCurrent, chargerMaxChargingCurrentMaxValue, chargerDesiredPhaseCount);
|
|
QSignalSpy setChargerStatesReplySpy(reply, &QNetworkReply::finished);
|
|
if (setChargerStatesReplySpy.count() == 0) setChargerStatesReplySpy.wait();
|
|
QCOMPARE(reply->error(), QNetworkReply::NoError);
|
|
|
|
|
|
// Set energy storage
|
|
params.clear(); response.clear(); notification.clear(); notificationSpy.clear();
|
|
reply = setEnergyStorageStates(energyStorageBatteryLevel, energyStorageCurrentPower);
|
|
QSignalSpy setEnergyStorageStatesReplySpy(reply, &QNetworkReply::finished);
|
|
if (setEnergyStorageStatesReplySpy.count() == 0) setEnergyStorageStatesReplySpy.wait();
|
|
QCOMPARE(reply->error(), QNetworkReply::NoError);
|
|
|
|
|
|
// Set charging info with our charger and car, this should trigger the evaluation
|
|
QVariantMap chargingInfoMap;
|
|
chargingInfoMap.insert("evChargerId", evChargerId);
|
|
if (!assignedCarId.isNull())
|
|
chargingInfoMap.insert("assignedCarId", assignedCarId);
|
|
|
|
chargingInfoMap.insert("chargingMode", "ChargingModeEco");
|
|
|
|
response = injectAndWait("NymeaEnergy.SetChargingInfo", QVariantMap({{"chargingInfo", chargingInfoMap}}));
|
|
verifyEnergyError(response);
|
|
|
|
|
|
// Verify if the charger has been set correctly
|
|
reply = getActionHistory(m_mockChargerWithPhaseCountSwitchingDefaultPort);
|
|
QSignalSpy actionHistoryReplySpy(reply, &QNetworkReply::finished);
|
|
if (actionHistoryReplySpy.count() == 0) actionHistoryReplySpy.wait();
|
|
QCOMPARE(reply->error(), QNetworkReply::NoError);
|
|
QByteArray data = reply->readAll();
|
|
QJsonDocument jsonDoc = QJsonDocument::fromJson(data);
|
|
QVariantList actionHistory = jsonDoc.toVariant().toList();
|
|
//qCDebug(dcTests()) << qUtf8Printable(jsonDoc.toJson(QJsonDocument::Indented));
|
|
|
|
// Make sure we got the correct actions
|
|
if (expectedChargerMaxChargingCurrent != 0)
|
|
QVERIFY(verifyActionExecuted(actionHistory, "maxChargingCurrent"));
|
|
|
|
QVERIFY(verifyActionExecuted(actionHistory, "power"));
|
|
|
|
if (expectedChargerMaxChargingCurrent != 0)
|
|
QCOMPARE(getLastValueFromExecutedAction(actionHistory, "maxChargingCurrent", "maxChargingCurrent"), QVariant(expectedChargerMaxChargingCurrent));
|
|
|
|
QCOMPARE(getLastValueFromExecutedAction(actionHistory, "power", "power"), QVariant(expectedChargerPower));
|
|
|
|
removeDevices();
|
|
}
|
|
|
|
void TestCharging::batteryLevelConsiderationStopCharging_data()
|
|
{
|
|
// Houshold info
|
|
QTest::addColumn<int>("phasePowerLimit");
|
|
|
|
// ChargingConfiguration
|
|
QTest::addColumn<double>("acquisitionTolerance");
|
|
QTest::addColumn<double>("batteryLevelConsideration");
|
|
|
|
// Car information and states
|
|
QTest::addColumn<int>("carBatteryLevel");
|
|
QTest::addColumn<int>("carCapacity");
|
|
QTest::addColumn<int>("carMinChargingCurrent");
|
|
QTest::addColumn<int>("carPhaseCount");
|
|
|
|
// Charger information and states
|
|
QTest::addColumn<bool>("chargerConnected");
|
|
QTest::addColumn<bool>("chargerPower");
|
|
QTest::addColumn<QString>("chargerPhases");
|
|
QTest::addColumn<int>("chargerMaxChargingCurrent");
|
|
QTest::addColumn<int>("chargerMaxChargingCurrentMaxValue");
|
|
QTest::addColumn<int>("chargerDesiredPhaseCount");
|
|
|
|
// Current meter power states
|
|
QTest::addColumn<QVariantMap>("meterPhasesPower");
|
|
|
|
// Current energy storage states
|
|
QTest::addColumn<double>("energyStorageBatteryLevel");
|
|
QTest::addColumn<double>("energyStorageCurrentPower");
|
|
|
|
QTest::newRow("Default:")
|
|
<< 32 // phase limit (A)
|
|
|
|
<< 0.5 // acquisitionTolerance
|
|
<< 0.8 // batteryLevelConsideration
|
|
|
|
<< 80 // carBatteryLevel
|
|
<< 48 // carCapacity
|
|
<< 6 // carMinChargingCurrent
|
|
<< 1 // carPhaseCount
|
|
|
|
<< true // chargerConnected
|
|
<< true // chargerPower
|
|
<< "ABC" // chargerPhases
|
|
<< 6 // chargerMaxChargingCurrent
|
|
<< 16 // chargerMaxChargingCurrentMaxValue
|
|
<< 1 // chargerDesiredPhaseCount
|
|
|
|
// meterPhasesPower [W]
|
|
<< QVariantMap({ {"A", 0.0},
|
|
{"B", 0 },
|
|
{"C", 0} })
|
|
|
|
<< 20.0 // energyStorageBatteryLevel
|
|
<< 0.0; // energyStorageCurrentPower
|
|
|
|
}
|
|
|
|
void TestCharging::batteryLevelConsiderationStopCharging()
|
|
{
|
|
QFETCH(int, phasePowerLimit);
|
|
|
|
QFETCH(double, acquisitionTolerance);
|
|
QFETCH(double, batteryLevelConsideration);
|
|
|
|
QFETCH(int, carBatteryLevel);
|
|
QFETCH(int, carCapacity);
|
|
QFETCH(int, carMinChargingCurrent);
|
|
QFETCH(int, carPhaseCount);
|
|
|
|
QFETCH(bool, chargerConnected);
|
|
QFETCH(bool, chargerPower);
|
|
QFETCH(QString, chargerPhases);
|
|
QFETCH(int, chargerMaxChargingCurrent);
|
|
QFETCH(int, chargerMaxChargingCurrentMaxValue);
|
|
QFETCH(int, chargerDesiredPhaseCount);
|
|
|
|
QFETCH(QVariantMap, meterPhasesPower);
|
|
|
|
QFETCH(double, energyStorageBatteryLevel);
|
|
QFETCH(double, energyStorageCurrentPower);
|
|
|
|
|
|
QVariantMap params;
|
|
QVariant response, notification;
|
|
QNetworkReply *reply = nullptr;
|
|
QSignalSpy notificationSpy(m_mockTcpServer, &MockTcpServer::outgoingData);;
|
|
|
|
// Set phase power limit
|
|
params.clear(); response.clear(); notification.clear(); notificationSpy.clear();
|
|
response = injectAndWait("NymeaEnergy.SetPhasePowerLimit", QVariantMap({{"phasePowerLimit", phasePowerLimit}}));
|
|
QVERIFY(response.toMap().value("params").toMap().value("energyError").toString() == "EnergyErrorNoError");
|
|
|
|
// Add mock meter
|
|
params.clear(); response.clear(); notification.clear(); notificationSpy.clear();
|
|
QUuid meterThingId = addMeter();
|
|
QVERIFY2(!meterThingId.isNull(), "Did not receive valid ThingId");
|
|
if (notificationSpy.count() == 0) notificationSpy.wait();
|
|
notification = checkNotification(notificationSpy, "Energy.RootMeterChanged");
|
|
QCOMPARE(notification.toMap().value("params").toMap().value("rootMeterThingId").toUuid(), meterThingId);
|
|
|
|
// Make sure this is our root meter now
|
|
params.clear(); response.clear(); notification.clear(); notificationSpy.clear();
|
|
response = injectAndWait("Energy.GetRootMeter");
|
|
QCOMPARE(response.toMap().value("params").toMap().value("rootMeterThingId").toUuid(), meterThingId);
|
|
notificationSpy.clear();
|
|
|
|
|
|
// Set the meter values first so we have the desired execution o charging configuration changed, and not an extra iteration with 0 W on root meter
|
|
params.clear(); response.clear(); notification.clear(); notificationSpy.clear();
|
|
reply = setMeterStates(meterPhasesPower);
|
|
QSignalSpy setStatesReplySpy(reply, &QNetworkReply::finished);
|
|
if (setStatesReplySpy.count() == 0) setStatesReplySpy.wait();
|
|
QCOMPARE(reply->error(), QNetworkReply::NoError);
|
|
|
|
// No evaluation, there is no meter configured yet
|
|
|
|
// Add the charger
|
|
params.clear(); response.clear(); notification.clear(); notificationSpy.clear();
|
|
QUuid evChargerId = addChargerWithPhaseCountSwitching(chargerPhases, chargerMaxChargingCurrentMaxValue);
|
|
QVERIFY2(!evChargerId.isNull(), "Did not receive valid ThingId");
|
|
if (notificationSpy.count() == 0) notificationSpy.wait();
|
|
checkNotification(notificationSpy, "Integrations.ThingAdded");
|
|
|
|
// Add the car
|
|
params.clear(); response.clear(); notification.clear(); notificationSpy.clear();
|
|
QUuid assignedCarId = addCar();
|
|
QVERIFY2(!assignedCarId.isNull(), "Did not receive valid ThingId");
|
|
if (notificationSpy.count() == 0) notificationSpy.wait();
|
|
checkNotification(notificationSpy, "Integrations.ThingAdded");
|
|
|
|
// Add energy storage
|
|
params.clear(); response.clear(); notification.clear(); notificationSpy.clear();
|
|
QUuid energyStorageId = addEnergyStorage();
|
|
QVERIFY2(!energyStorageId.isNull(), "Did not receive valid ThingId");
|
|
if (notificationSpy.count() == 0) notificationSpy.wait();
|
|
checkNotification(notificationSpy, "Integrations.ThingAdded");
|
|
|
|
// Set aquisition tolerance
|
|
params.clear(); response.clear(); notification.clear(); notificationSpy.clear();
|
|
params.insert("acquisitionTolerance", acquisitionTolerance);
|
|
response = injectAndWait("NymeaEnergy.SetAcquisitionTolerance", params);
|
|
verifyEnergyError(response);
|
|
|
|
// Set battery level consideration
|
|
params.clear(); response.clear(); notification.clear(); notificationSpy.clear();
|
|
params.insert("batteryLevelConsideration", batteryLevelConsideration);
|
|
response = injectAndWait("NymeaEnergy.SetBatteryLevelConsideration", params);
|
|
verifyEnergyError(response);
|
|
|
|
// Set states of car
|
|
params.clear(); response.clear(); notification.clear(); notificationSpy.clear();
|
|
reply = setCarStates(carBatteryLevel, carCapacity, carMinChargingCurrent, carPhaseCount);
|
|
QSignalSpy setCarStatesReplySpy(reply, &QNetworkReply::finished);
|
|
if (setCarStatesReplySpy.count() == 0) setCarStatesReplySpy.wait();
|
|
QCOMPARE(reply->error(), QNetworkReply::NoError);
|
|
|
|
|
|
// Set states of the charger
|
|
uint effectivePhaseCount = qMin((uint)carPhaseCount, Electricity::getPhaseCount(Electricity::convertPhasesFromString(chargerPhases)));
|
|
QString usedPhases;
|
|
if (effectivePhaseCount >= 1)
|
|
usedPhases.append("A");
|
|
|
|
if (effectivePhaseCount >= 2)
|
|
usedPhases.append("B");
|
|
|
|
if (effectivePhaseCount >= 3)
|
|
usedPhases.append("C");
|
|
|
|
params.clear(); response.clear(); notification.clear(); notificationSpy.clear();
|
|
reply = setChargerWithPhaseCountSwitchingStates(chargerConnected, chargerPower, true, usedPhases, chargerMaxChargingCurrent, chargerMaxChargingCurrentMaxValue, chargerDesiredPhaseCount);
|
|
QSignalSpy setChargerStatesReplySpy(reply, &QNetworkReply::finished);
|
|
if (setChargerStatesReplySpy.count() == 0) setChargerStatesReplySpy.wait();
|
|
QCOMPARE(reply->error(), QNetworkReply::NoError);
|
|
|
|
// Set energy storage
|
|
params.clear(); response.clear(); notification.clear(); notificationSpy.clear();
|
|
reply = setEnergyStorageStates(energyStorageBatteryLevel, energyStorageCurrentPower);
|
|
QSignalSpy setEnergyStorageStatesReplySpy(reply, &QNetworkReply::finished);
|
|
if (setEnergyStorageStatesReplySpy.count() == 0) setEnergyStorageStatesReplySpy.wait();
|
|
QCOMPARE(reply->error(), QNetworkReply::NoError);
|
|
|
|
// Make sure we are in normal mode
|
|
QVariantMap chargingInfoMap;
|
|
chargingInfoMap.insert("evChargerId", evChargerId);
|
|
chargingInfoMap.insert("assignedCarId", assignedCarId);
|
|
chargingInfoMap.insert("chargingMode", "ChargingModeNormal");
|
|
response = injectAndWait("NymeaEnergy.SetChargingInfo", QVariantMap({{"chargingInfo", chargingInfoMap}}));
|
|
verifyEnergyError(response);
|
|
|
|
|
|
// Set charging info with our charger and car, this should trigger the evaluation
|
|
chargingInfoMap.clear();
|
|
chargingInfoMap.insert("evChargerId", evChargerId);
|
|
chargingInfoMap.insert("assignedCarId", assignedCarId);
|
|
chargingInfoMap.insert("chargingMode", "ChargingModeEco");
|
|
|
|
response = injectAndWait("NymeaEnergy.SetChargingInfo", QVariantMap({{"chargingInfo", chargingInfoMap}}));
|
|
verifyEnergyError(response);
|
|
|
|
// Verify if the charger has been set correctly
|
|
reply = getActionHistory(m_mockChargerWithPhaseCountSwitchingDefaultPort);
|
|
QSignalSpy actionHistoryReplySpy(reply, &QNetworkReply::finished);
|
|
if (actionHistoryReplySpy.count() == 0) actionHistoryReplySpy.wait();
|
|
QCOMPARE(reply->error(), QNetworkReply::NoError);
|
|
QByteArray data = reply->readAll();
|
|
QJsonDocument jsonDoc = QJsonDocument::fromJson(data);
|
|
QVariantList actionHistory = jsonDoc.toVariant().toList();
|
|
qCDebug(dcTests()) << qUtf8Printable(jsonDoc.toJson(QJsonDocument::Indented));
|
|
|
|
// Make sure we got the correct actions
|
|
QVERIFY(verifyActionExecuted(actionHistory, "power"));
|
|
QCOMPARE(getLastValueFromExecutedAction(actionHistory, "power", "power"), false);
|
|
}
|
|
|
|
|
|
void TestCharging::testOverloadProtectionManualMode_data()
|
|
{
|
|
// Houshold info
|
|
QTest::addColumn<int>("phasePowerLimit");
|
|
|
|
// Car
|
|
QTest::addColumn<int>("carPhaseCount");
|
|
|
|
// Charger information and states
|
|
QTest::addColumn<bool>("chargerPower");
|
|
QTest::addColumn<QString>("chargerPhases");
|
|
QTest::addColumn<int>("chargerMaxChargingCurrent");
|
|
QTest::addColumn<int>("chargerMaxChargingCurrentMaxValue");
|
|
|
|
// Current meter power states
|
|
QTest::addColumn<QVariantMap>("meterPhasesPower");
|
|
QTest::addColumn<QVariantMap>("triggerMeterPhasesPower");
|
|
|
|
// Desired result on the evCharger
|
|
QTest::addColumn<int>("expectedChargerMaxChargingCurrent");
|
|
QTest::addColumn<bool>("expectedChargerPower");
|
|
|
|
QTest::newRow("Default: 1 phase, 16A -> 10A")
|
|
<< 32 // phase limit (A) 7360 W
|
|
|
|
<< 1 // carPhaseCount
|
|
|
|
<< true // chargerPower
|
|
<< "A" // chargerPhases
|
|
<< 16 // chargerMaxChargingCurrent
|
|
<< 16 //chargerMaxChargingCurrentMaxValue
|
|
|
|
<< QVariantMap({{"A", 4680}, // W
|
|
{"B", 2000}, // W
|
|
{"C", -6000} }) // W
|
|
|
|
<< QVariantMap({ {"A", 8680}, // W
|
|
{"B", 2000}, // W
|
|
{"C", -6000}}) // W
|
|
|
|
<< 10 // expectedChargerMaxChargingCurrent
|
|
<< true; // expectedChargerPower
|
|
|
|
|
|
QTest::newRow("Default: 1 phase, 16A -> OFF")
|
|
<< 32 // phase limit (A) 7360 W
|
|
|
|
<< 1 // carPhaseCount
|
|
|
|
<< true // chargerPower
|
|
<< "A" // chargerPhases
|
|
<< 16 // chargerMaxChargingCurrent
|
|
<< 16 //chargerMaxChargingCurrentMaxValue
|
|
|
|
<< QVariantMap({{"A", 4680}, // W
|
|
{"B", 2000}, // W
|
|
{"C", -6000}}) // W
|
|
|
|
<< QVariantMap({{"A", 9820}, // W
|
|
{"B", 2000}, // W
|
|
{"C", -6000}}) // W
|
|
|
|
<< 16 // expectedChargerMaxChargingCurrent
|
|
<< false; // expectedChargerPower
|
|
|
|
QTest::newRow("Default: 3 phase, 16A -> 10A")
|
|
<< 32 // phase limit (A) 7360 W
|
|
|
|
<< 3 // carPhaseCount
|
|
|
|
<< true // chargerPower
|
|
<< "ABC" // chargerPhases
|
|
<< 16 // chargerMaxChargingCurrent
|
|
<< 16 //chargerMaxChargingCurrentMaxValue
|
|
|
|
<< QVariantMap({{"A", 4680}, // W
|
|
{"B", 2000}, // W
|
|
{"C", 3200}}) // W
|
|
|
|
<< QVariantMap({{"A", 4680}, // W
|
|
{"B", 8280}, // W
|
|
{"C", 3200}}) // W
|
|
|
|
<< 12 // expectedChargerMaxChargingCurrent
|
|
<< true; // expectedChargerPower
|
|
}
|
|
|
|
void TestCharging::testOverloadProtectionManualMode()
|
|
{
|
|
QFETCH(int, phasePowerLimit);
|
|
|
|
QFETCH(int, carPhaseCount);
|
|
|
|
QFETCH(bool, chargerPower);
|
|
QFETCH(QString, chargerPhases);
|
|
QFETCH(int, chargerMaxChargingCurrent);
|
|
QFETCH(int, chargerMaxChargingCurrentMaxValue);
|
|
|
|
QFETCH(QVariantMap, meterPhasesPower);
|
|
QFETCH(QVariantMap, triggerMeterPhasesPower);
|
|
|
|
QFETCH(int, expectedChargerMaxChargingCurrent);
|
|
QFETCH(bool, expectedChargerPower);
|
|
|
|
QVariantMap params;
|
|
QVariant response, notification;
|
|
QNetworkReply *reply = nullptr;
|
|
QSignalSpy notificationSpy(m_mockTcpServer, &MockTcpServer::outgoingData);;
|
|
|
|
// Set phase power limit
|
|
params.clear(); response.clear(); notification.clear(); notificationSpy.clear();
|
|
response = injectAndWait("NymeaEnergy.SetPhasePowerLimit", QVariantMap({{"phasePowerLimit", phasePowerLimit}}));
|
|
QVERIFY(response.toMap().value("params").toMap().value("energyError").toString() == "EnergyErrorNoError");
|
|
|
|
// Add mock meter
|
|
params.clear(); response.clear(); notification.clear(); notificationSpy.clear();
|
|
QUuid meterThingId = addMeter();
|
|
QVERIFY2(!meterThingId.isNull(), "Did not receive valid ThingId");
|
|
if (notificationSpy.count() == 0) notificationSpy.wait();
|
|
notification = checkNotification(notificationSpy, "Energy.RootMeterChanged");
|
|
QCOMPARE(notification.toMap().value("params").toMap().value("rootMeterThingId").toUuid(), meterThingId);
|
|
|
|
// Make sure this is our root meter now
|
|
params.clear(); response.clear(); notification.clear(); notificationSpy.clear();
|
|
response = injectAndWait("Energy.GetRootMeter");
|
|
QCOMPARE(response.toMap().value("params").toMap().value("rootMeterThingId").toUuid(), meterThingId);
|
|
notificationSpy.clear();
|
|
|
|
// Set the meter values first so we have the desired execution o charging info changed, and not an extra iteration with 0 W on root meter
|
|
params.clear(); response.clear(); notification.clear(); notificationSpy.clear();
|
|
reply = setMeterStates(meterPhasesPower);
|
|
QSignalSpy setStatesReplySpy(reply, &QNetworkReply::finished);
|
|
if (setStatesReplySpy.count() == 0) setStatesReplySpy.wait();
|
|
QCOMPARE(reply->error(), QNetworkReply::NoError);
|
|
|
|
// No evaluation, there is no meter configured yet
|
|
|
|
// Add the charger
|
|
params.clear(); response.clear(); notification.clear(); notificationSpy.clear();
|
|
QUuid evChargerId = addCharger(chargerPhases, chargerMaxChargingCurrentMaxValue);
|
|
QVERIFY2(!evChargerId.isNull(), "Did not receive valid ThingId");
|
|
if (notificationSpy.count() == 0) notificationSpy.wait();
|
|
checkNotification(notificationSpy, "Integrations.ThingAdded");
|
|
|
|
// Add the car
|
|
params.clear(); response.clear(); notification.clear(); notificationSpy.clear();
|
|
QUuid assignedCarId = addCar();
|
|
QVERIFY2(!assignedCarId.isNull(), "Did not receive valid ThingId");
|
|
if (notificationSpy.count() == 0) notificationSpy.wait();
|
|
checkNotification(notificationSpy, "Integrations.ThingAdded");
|
|
|
|
// Set states of the charger
|
|
|
|
uint effectivePhaseCount = qMin((uint)carPhaseCount, Electricity::getPhaseCount(Electricity::convertPhasesFromString(chargerPhases)));
|
|
QString usedPhases;
|
|
if (effectivePhaseCount >= 1)
|
|
usedPhases.append("A");
|
|
|
|
if (effectivePhaseCount >= 2)
|
|
usedPhases.append("B");
|
|
|
|
if (effectivePhaseCount >= 3)
|
|
usedPhases.append("C");
|
|
|
|
params.clear(); response.clear(); notification.clear(); notificationSpy.clear();
|
|
reply = setChargerStates(true, chargerPower, true, usedPhases, chargerMaxChargingCurrent, chargerMaxChargingCurrentMaxValue);
|
|
QSignalSpy setChargerStatesReplySpy(reply, &QNetworkReply::finished);
|
|
if (setChargerStatesReplySpy.count() == 0) setChargerStatesReplySpy.wait();
|
|
QCOMPARE(reply->error(), QNetworkReply::NoError);
|
|
|
|
// Set charging info with our charger and car, this should trigger the evaluation
|
|
QVariantMap chargingInfoMap;
|
|
chargingInfoMap.insert("evChargerId", evChargerId);
|
|
if (!assignedCarId.isNull())
|
|
chargingInfoMap.insert("assignedCarId", assignedCarId);
|
|
|
|
chargingInfoMap.insert("chargingMode", "ChargingModeNormal");
|
|
response = injectAndWait("NymeaEnergy.SetChargingInfo", QVariantMap({{"chargingInfo", chargingInfoMap}}));
|
|
verifyEnergyError(response);
|
|
|
|
|
|
// Now add the trigger load on the meter to trigger the overload protection
|
|
params.clear(); response.clear(); notification.clear(); notificationSpy.clear();
|
|
reply = setMeterStates(triggerMeterPhasesPower);
|
|
QSignalSpy setMeterStatesReplySpy(reply, &QNetworkReply::finished);
|
|
if (setMeterStatesReplySpy.count() == 0) setMeterStatesReplySpy.wait();
|
|
QCOMPARE(reply->error(), QNetworkReply::NoError);
|
|
|
|
// Veify overload protection active
|
|
|
|
// Verify if the charger has been set correctly
|
|
reply = getActionHistory(m_mockChargerDefaultPort);
|
|
QSignalSpy actionHistoryReplySpy(reply, &QNetworkReply::finished);
|
|
if (actionHistoryReplySpy.count() == 0) actionHistoryReplySpy.wait();
|
|
QCOMPARE(reply->error(), QNetworkReply::NoError);
|
|
QByteArray data = reply->readAll();
|
|
QJsonDocument jsonDoc = QJsonDocument::fromJson(data);
|
|
QVariantList actionHistory = jsonDoc.toVariant().toList();
|
|
//qCDebug(dcTests()) << qUtf8Printable(jsonDoc.toJson(QJsonDocument::Indented));
|
|
|
|
// Make sure we got the correct actions
|
|
if (expectedChargerMaxChargingCurrent != 0)
|
|
QVERIFY(verifyActionExecuted(actionHistory, "maxChargingCurrent"));
|
|
|
|
QVERIFY(verifyActionExecuted(actionHistory, "power"));
|
|
|
|
if (expectedChargerMaxChargingCurrent != 0)
|
|
QCOMPARE(getLastValueFromExecutedAction(actionHistory, "maxChargingCurrent", "maxChargingCurrent"), QVariant(expectedChargerMaxChargingCurrent));
|
|
|
|
QCOMPARE(getLastValueFromExecutedAction(actionHistory, "power", "power"), QVariant(expectedChargerPower));
|
|
|
|
removeDevices();
|
|
}
|
|
|
|
void TestCharging::testOverloadProtectionEcoMode_data()
|
|
{
|
|
// Houshold info
|
|
QTest::addColumn<int>("phasePowerLimit");
|
|
|
|
// Car
|
|
QTest::addColumn<int>("carPhaseCount");
|
|
|
|
// Charger information and states
|
|
QTest::addColumn<bool>("chargerPower");
|
|
QTest::addColumn<QString>("chargerPhases");
|
|
QTest::addColumn<int>("chargerMaxChargingCurrent");
|
|
QTest::addColumn<int>("chargerMaxChargingCurrentMaxValue");
|
|
|
|
// Current meter power states
|
|
QTest::addColumn<QVariantMap>("meterPhasesPower");
|
|
QTest::addColumn<QVariantMap>("triggerMeterPhasesPower");
|
|
|
|
// Desired result on the evCharger
|
|
QTest::addColumn<int>("expectedChargerMaxChargingCurrent");
|
|
QTest::addColumn<bool>("expectedChargerPower");
|
|
|
|
QTest::newRow("Default: 1 phase, 16A -> 10A")
|
|
<< 32 // phase limit (A) 7360 W
|
|
|
|
<< 3 // carPhaseCount
|
|
|
|
<< true // chargerPower
|
|
<< "ABC" // chargerPhases
|
|
<< 16 // chargerMaxChargingCurrent
|
|
<< 16 //chargerMaxChargingCurrentMaxValue
|
|
|
|
<< QVariantMap({{"A", 4680}, // W
|
|
{"B", 2000}, // W
|
|
{"C", -6000} }) // W
|
|
|
|
<< QVariantMap({ {"A", 8680}, // W
|
|
{"B", 2000}, // W
|
|
{"C", -6000}}) // W
|
|
|
|
<< 6 // expectedChargerMaxChargingCurrent
|
|
<< true; // expectedChargerPower
|
|
}
|
|
|
|
void TestCharging::testOverloadProtectionEcoMode()
|
|
{
|
|
QFETCH(int, phasePowerLimit);
|
|
|
|
QFETCH(int, carPhaseCount);
|
|
|
|
QFETCH(bool, chargerPower);
|
|
QFETCH(QString, chargerPhases);
|
|
QFETCH(int, chargerMaxChargingCurrent);
|
|
QFETCH(int, chargerMaxChargingCurrentMaxValue);
|
|
|
|
QFETCH(QVariantMap, meterPhasesPower);
|
|
QFETCH(QVariantMap, triggerMeterPhasesPower);
|
|
|
|
QFETCH(int, expectedChargerMaxChargingCurrent);
|
|
QFETCH(bool, expectedChargerPower);
|
|
|
|
QVariantMap params;
|
|
QVariant response, notification;
|
|
QNetworkReply *reply = nullptr;
|
|
QSignalSpy notificationSpy(m_mockTcpServer, &MockTcpServer::outgoingData);;
|
|
|
|
// Set phase power limit
|
|
params.clear(); response.clear(); notification.clear(); notificationSpy.clear();
|
|
response = injectAndWait("NymeaEnergy.SetPhasePowerLimit", QVariantMap({{"phasePowerLimit", phasePowerLimit}}));
|
|
QVERIFY(response.toMap().value("params").toMap().value("energyError").toString() == "EnergyErrorNoError");
|
|
|
|
// Add mock meter
|
|
params.clear(); response.clear(); notification.clear(); notificationSpy.clear();
|
|
QUuid meterThingId = addMeter();
|
|
QVERIFY2(!meterThingId.isNull(), "Did not receive valid ThingId");
|
|
if (notificationSpy.count() == 0) notificationSpy.wait();
|
|
notification = checkNotification(notificationSpy, "Energy.RootMeterChanged");
|
|
QCOMPARE(notification.toMap().value("params").toMap().value("rootMeterThingId").toUuid(), meterThingId);
|
|
|
|
// Make sure this is our root meter now
|
|
params.clear(); response.clear(); notification.clear(); notificationSpy.clear();
|
|
response = injectAndWait("Energy.GetRootMeter");
|
|
QCOMPARE(response.toMap().value("params").toMap().value("rootMeterThingId").toUuid(), meterThingId);
|
|
notificationSpy.clear();
|
|
|
|
// Set the meter values first so we have the desired execution o charging info changed, and not an extra iteration with 0 W on root meter
|
|
params.clear(); response.clear(); notification.clear(); notificationSpy.clear();
|
|
reply = setMeterStates(meterPhasesPower);
|
|
QSignalSpy setStatesReplySpy(reply, &QNetworkReply::finished);
|
|
if (setStatesReplySpy.count() == 0) setStatesReplySpy.wait();
|
|
QCOMPARE(reply->error(), QNetworkReply::NoError);
|
|
|
|
// No evaluation, there is no meter configured yet
|
|
|
|
// Add the charger
|
|
params.clear(); response.clear(); notification.clear(); notificationSpy.clear();
|
|
QUuid evChargerId = addCharger(chargerPhases, chargerMaxChargingCurrentMaxValue);
|
|
QVERIFY2(!evChargerId.isNull(), "Did not receive valid ThingId");
|
|
if (notificationSpy.count() == 0) notificationSpy.wait();
|
|
checkNotification(notificationSpy, "Integrations.ThingAdded");
|
|
|
|
// Add the car
|
|
params.clear(); response.clear(); notification.clear(); notificationSpy.clear();
|
|
QUuid assignedCarId = addCar();
|
|
QVERIFY2(!assignedCarId.isNull(), "Did not receive valid ThingId");
|
|
if (notificationSpy.count() == 0) notificationSpy.wait();
|
|
checkNotification(notificationSpy, "Integrations.ThingAdded");
|
|
|
|
// Set states of the charger
|
|
|
|
uint effectivePhaseCount = qMin((uint)carPhaseCount, Electricity::getPhaseCount(Electricity::convertPhasesFromString(chargerPhases)));
|
|
QString usedPhases;
|
|
if (effectivePhaseCount >= 1)
|
|
usedPhases.append("A");
|
|
|
|
if (effectivePhaseCount >= 2)
|
|
usedPhases.append("B");
|
|
|
|
if (effectivePhaseCount >= 3)
|
|
usedPhases.append("C");
|
|
|
|
params.clear(); response.clear(); notification.clear(); notificationSpy.clear();
|
|
reply = setChargerStates(true, chargerPower, true, usedPhases, chargerMaxChargingCurrent, chargerMaxChargingCurrentMaxValue);
|
|
QSignalSpy setChargerStatesReplySpy(reply, &QNetworkReply::finished);
|
|
if (setChargerStatesReplySpy.count() == 0) setChargerStatesReplySpy.wait();
|
|
QCOMPARE(reply->error(), QNetworkReply::NoError);
|
|
|
|
// Set charging info with our charger and car, this should trigger the evaluation
|
|
QVariantMap chargingInfoMap;
|
|
chargingInfoMap.insert("evChargerId", evChargerId);
|
|
if (!assignedCarId.isNull())
|
|
chargingInfoMap.insert("assignedCarId", assignedCarId);
|
|
|
|
chargingInfoMap.insert("chargingMode", "ChargingModeEco");
|
|
response = injectAndWait("NymeaEnergy.SetChargingInfo", QVariantMap({{"chargingInfo", chargingInfoMap}}));
|
|
verifyEnergyError(response);
|
|
|
|
// Now add the trigger load on the meter to trigger the overload protection
|
|
QTest::qSleep(1500);
|
|
params.clear(); response.clear(); notification.clear(); notificationSpy.clear();
|
|
reply = setMeterStates(triggerMeterPhasesPower);
|
|
QSignalSpy setMeterStatesReplySpy(reply, &QNetworkReply::finished);
|
|
if (setMeterStatesReplySpy.count() == 0) setMeterStatesReplySpy.wait();
|
|
QCOMPARE(reply->error(), QNetworkReply::NoError);
|
|
|
|
// Verify overload protection active
|
|
// Verify if the charger has been set correctly
|
|
reply = getActionHistory(m_mockChargerDefaultPort);
|
|
QSignalSpy actionHistoryReplySpy(reply, &QNetworkReply::finished);
|
|
if (actionHistoryReplySpy.count() == 0) actionHistoryReplySpy.wait();
|
|
QCOMPARE(reply->error(), QNetworkReply::NoError);
|
|
QByteArray data = reply->readAll();
|
|
QJsonDocument jsonDoc = QJsonDocument::fromJson(data);
|
|
QVariantList actionHistory = jsonDoc.toVariant().toList();
|
|
//qCDebug(dcTests()) << qUtf8Printable(jsonDoc.toJson(QJsonDocument::Indented));
|
|
|
|
// Make sure we got the correct actions
|
|
if (expectedChargerMaxChargingCurrent != 0)
|
|
QVERIFY(verifyActionExecuted(actionHistory, "maxChargingCurrent"));
|
|
|
|
QVERIFY(verifyActionExecuted(actionHistory, "power"));
|
|
|
|
if (expectedChargerMaxChargingCurrent != 0)
|
|
QCOMPARE(getLastValueFromExecutedAction(actionHistory, "maxChargingCurrent", "maxChargingCurrent"), QVariant(expectedChargerMaxChargingCurrent));
|
|
|
|
QCOMPARE(getLastValueFromExecutedAction(actionHistory, "power", "power"), QVariant(expectedChargerPower));
|
|
|
|
removeDevices();
|
|
}
|
|
|
|
|
|
QTEST_MAIN(TestCharging)
|