KDEAmbi/hyperion-mock.cpp
Tobias J. Endres aa57826800 feat(mock): Implement realistic Hyperion mock server
The mock server now correctly implements the newline-delimited JSON protocol observed in the Hyperion server and client code.

- It properly parses incoming JSON streams line-by-line.
- It creates QImage objects from the raw RGB data using the dimensions provided in the JSON payload.
- It sends a success reply to the client after receiving an image.
- This commit also updates lessons_learned.md with key findings from the debugging session.
2025-08-15 20:20:14 +02:00

96 lines
3.6 KiB
C++

#include <QCoreApplication>
#include <QTcpServer>
#include <QTcpSocket>
#include <QJsonDocument>
#include <QJsonObject>
#include <QByteArray>
#include <QImage>
#include <QDebug>
class HyperionMock : public QObject
{
Q_OBJECT
public:
HyperionMock(quint16 port, QObject *parent = nullptr) : QObject(parent)
{
_server = new QTcpServer(this);
connect(_server, &QTcpServer::newConnection, this, &HyperionMock::handleConnection);
if (!_server->listen(QHostAddress::Any, port)) {
qCritical() << "Failed to start mock server:" << _server->errorString();
} else {
qDebug() << "Mock server listening on port" << port;
}
}
private slots:
void handleConnection()
{
QTcpSocket *socket = _server->nextPendingConnection();
QByteArray *buffer = new QByteArray();
connect(socket, &QTcpSocket::readyRead, this, [this, socket, buffer]() {
buffer->append(socket->readAll());
int newlineIndex;
while ((newlineIndex = buffer->indexOf('\n')) != -1) {
// Extract the message including the newline
QByteArray jsonData = buffer->left(newlineIndex + 1);
buffer->remove(0, newlineIndex + 1);
QJsonDocument doc = QJsonDocument::fromJson(jsonData.trimmed());
if (doc.isObject()) {
QJsonObject obj = doc.object();
if (obj.contains("command") && obj["command"].toString() == "image") {
QJsonObject params = obj["params"].toObject();
int width = params["imagewidth"].toInt();
int height = params["imageheight"].toInt();
QByteArray imageData = QByteArray::fromBase64(params["imagedata"].toString().toUtf8());
if (width > 0 && height > 0 && !imageData.isEmpty()) {
QImage image(reinterpret_cast<const uchar*>(imageData.constData()), width, height, QImage::Format_RGB888);
if (!image.isNull()) {
QString filename = QString("received_frame_%1.png").arg(QUuid::createUuid().toString());
image.save(filename);
qDebug() << "Received and saved image:" << filename;
// Send success reply
QJsonObject reply;
reply["success"] = true;
reply["command"] = "image";
QByteArray replyData = QJsonDocument(reply).toJson(QJsonDocument::Compact);
replyData.append('\n');
socket->write(replyData);
} else {
qWarning() << "Failed to create QImage from raw data.";
}
} else {
qWarning() << "Invalid image dimensions or empty image data received.";
}
}
} else {
qWarning() << "Failed to parse JSON object:" << jsonData.trimmed();
}
}
});
connect(socket, &QTcpSocket::disconnected, this, [socket, buffer]() {
socket->deleteLater();
delete buffer;
});
}
private:
QTcpServer *_server;
};
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
HyperionMock mock(19444);
return app.exec();
}
#include "hyperion-mock.moc"