487 lines
19 KiB
C++
487 lines
19 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()));
|
|
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::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::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();
|
|
}
|
|
|
|
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 ¶mName)
|
|
{
|
|
QVariant paramValue;
|
|
foreach (const QVariant &actionHistoryVariant, actionHistory) {
|
|
QVariantMap actionHistoryMap = actionHistoryVariant.toMap();
|
|
if (actionHistoryMap.value("name").toString() == actionName) {
|
|
foreach (const QVariant ¶mVariant, actionHistoryMap.value("params").toList()) {
|
|
if (paramVariant.toMap().value("name").toString() == paramName) {
|
|
paramValue = paramVariant.toMap().value("value");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return paramValue;
|
|
}
|