powersync-energy-plugin-etm/tests/mocks/plugins/energymocks/mockcontroller.cpp

180 lines
5.9 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 "mockcontroller.h"
#include <integrations/thing.h>
#include <QTcpSocket>
#include <QJsonDocument>
#include <QRegularExpression>
#include "extern-plugininfo.h"
EnergyMockController::EnergyMockController(Thing *thing, QObject *parent):
QTcpServer(parent),
m_thing(thing)
{
}
EnergyMockController::~EnergyMockController()
{
close();
}
void EnergyMockController::incomingConnection(qintptr socket)
{
QTcpSocket* s = new QTcpSocket(this);
connect(s, &QTcpSocket::readyRead, this, &EnergyMockController::onReadyRead);
connect(s, &QTcpSocket::disconnected, s, [s](){
s->deleteLater();
});
s->setSocketDescriptor(socket);
}
void EnergyMockController::logActionExecuted(const ActionType &actionType, const Action &action)
{
QVariantMap actionMap;
actionMap.insert("timestamp", static_cast<uint>(QDateTime::currentDateTime().toMSecsSinceEpoch() / 1000));
actionMap.insert("actionTypeId", action.actionTypeId());
actionMap.insert("name", actionType.name());
actionMap.insert("displayName", actionType.displayName());
QVariantList params;
foreach (const Param &param, action.params()) {
QVariantMap paramsMap;
paramsMap.insert("paramTypeId", param.paramTypeId());
paramsMap.insert("value", param.value());
paramsMap.insert("name", actionType.paramTypes().findById(param.paramTypeId()).name());
params.append(paramsMap);
}
actionMap.insert("params", params);
m_executedActions.append(actionMap);
//qCDebug(dcEnergyMocks()) << "Action logged" << actionMap;
}
void EnergyMockController::onReadyRead()
{
QTcpSocket *client = static_cast<QTcpSocket*>(sender());
QByteArray data = client->readAll();
// split the data into header and payload
int headerEndIndex = data.indexOf("\r\n\r\n");
if (headerEndIndex < 0) {
qCWarning(dcEnergyMocks()) << "Could not parse payload" << data;
return;
}
QByteArray rawHeader = data.left(headerEndIndex);
QByteArray payload = data.right(data.length() - headerEndIndex).simplified();
// parse status line
QStringList headerLines = QString(rawHeader).split(QRegularExpression("\r\n"));
QString statusLine = headerLines.takeFirst();
QStringList statusLineTokens = statusLine.split(QRegularExpression("[ \r\n][ \r\n]*"));
// verify http version
QString httpVersion = statusLineTokens.at(2).toUtf8().simplified();
if (!httpVersion.contains("HTTP")) {
qCWarning(dcEnergyMocks()) << "Unknown HTTP version:" << httpVersion;
return;
}
QString methodString = statusLineTokens.at(0).simplified();
QUrl url = QUrl("http://example.com" + statusLineTokens.at(1).simplified());
QUrlQuery query(url);
if (url.path() == "/setstates") {
qCDebug(dcEnergyMocks()) << "Set states called" << methodString << url.path() << query.toString();
emit updateStateRequestReceived(query);
QTextStream os(client);
os.setAutoDetectUnicode(true);
os << generateHeader();
client->close();
return;
} else if (url.path() == "/actionhistory") {
QTextStream os(client);
os.setAutoDetectUnicode(true);
os << generateJsonResponse(QJsonDocument::fromVariant(m_executedActions));
client->close();
return;
} else if (url.path() == "/clearactionhistory") {
m_executedActions.clear();
QTextStream os(client);
os.setAutoDetectUnicode(true);
os << generateHeader();
client->close();
return;
}
// Default website
QTextStream os(client);
os.setAutoDetectUnicode(true);
os << generateWebPage();
client->close();
}
QByteArray EnergyMockController::generateHeader()
{
QByteArray contentHeader(
"HTTP/1.0 200 Ok\r\n"
"Content-Type: text/html; charset=\"utf-8\"\r\n"
"\r\n"
);
return contentHeader;
}
QByteArray EnergyMockController::generateJsonResponse(const QJsonDocument &document)
{
QByteArray body = document.toJson(QJsonDocument::Compact);
QByteArray contentHeader("HTTP/1.0 200 Ok\r\n");
contentHeader.append("Content-Type: application/json\r\n");
contentHeader.append(QString("Content-Length: %1").arg(body.length()).toUtf8() + "\r\n");
contentHeader.append("\r\n");
QByteArray response = contentHeader + body;
//qCDebug(dcEnergyMocks()) << "Action histroy response:" << response;
return response;
}
QByteArray EnergyMockController::generateWebPage()
{
QString body = QString(
"<html>"
"<body>"
"<h1>Mock device Controller</h1>\n"
"<hr>"
"<h2>Device Information</h2>"
"Name: %1<br>"
"ID: %2<br>"
"DeviceClass ID: %3<br>").arg(m_thing->name()).arg(m_thing->id().toString()).arg(m_thing->thingClassId().toString());
body.append("</body>");
body.append("</html>\n");
return generateHeader() + body.toUtf8();
}