Refactor tcpserver into multiple files

This commit is contained in:
Carl Philipp Klemm 2026-03-27 20:11:23 +01:00
parent e3b6d5c3a6
commit 59b55d1868
10 changed files with 486 additions and 446 deletions

View file

@ -42,11 +42,16 @@ target_sources(SHinterface
src/sun.cpp
src/programmode.h
src/programmode.cpp
src/tcpserver.h
src/tcpserver.cpp
src/pipewire.h
src/pipewire.cpp
src/service/service.h
src/service/service.cpp
src/service/tcpclient.h
src/service/tcpclient.cpp
src/service/tcpserver.h
src/service/tcpserver.cpp
src/actors/actor.h
src/actors/actor.cpp
src/actors/factoractor.h

View file

@ -17,7 +17,8 @@
#include "sensors/mqttsensorsource.h"
#include "items/fixeditemsource.h"
#include "items/itemloadersource.h"
#include "tcpserver.h"
#include "service/tcpclient.h"
#include "service/tcpserver.h"
class MainObject : public QObject
{

104
src/service/service.cpp Normal file
View file

@ -0,0 +1,104 @@
#include <QTcpSocket>
#include <QJsonDocument>
#include <QJsonArray>
#include <iostream>
#include "items/item.h"
#include "items/itemstore.h"
#include "service.h"
Service::Service(QObject* parent):
ItemSource(parent)
{}
QJsonObject Service::createMessage(const QString& type, const QJsonArray& data)
{
QJsonObject json;
json["MessageType"] = type;
json["Data"] = data;
return json;
}
void Service::sensorEvent(Sensor sensor)
{
QJsonArray sensors;
QJsonObject sensorjson;
sensor.store(sensorjson);
sensors.append(sensorjson);
QJsonObject json = createMessage("SensorUpdate", sensors);
sendJson(json);
}
void Service::itemUpdated(std::weak_ptr<Item> item)
{
qDebug()<<__func__;
QJsonArray items;
QJsonObject itemjson;
item.lock()->store(itemjson);
items.append(itemjson);
QJsonObject json = createMessage("ItemUpdate", items);
json["FullList"] = false;
sendJson(json);
}
void Service::refresh()
{
sendJson(createMessage("GetSensors", QJsonArray()));
sendJson(createMessage("GetItems", QJsonArray()));
}
void Service::sendSensors()
{
QJsonArray sensors;
for(auto& sensor: *globalSensors.getSensors())
{
QJsonObject sensorjson;
sensor.store(sensorjson);
sensors.append(sensorjson);
}
sendJson(createMessage("SensorUpdate", sensors));
}
void Service::sendItems()
{
QJsonArray items;
for(auto& item: *globalItems.getItems())
{
QJsonObject itemjson;
item->store(itemjson);
items.append(itemjson);
}
QJsonObject json = createMessage("ItemUpdate", items);
json["FullList"] = true;
sendJson(json);
}
void Service::processIncomeingJson(const QByteArray& jsonbytes)
{
QJsonDocument doc = QJsonDocument::fromJson(jsonbytes);
qDebug()<<"Got Json:"<<QString::fromLatin1(doc.toJson(QJsonDocument::JsonFormat::Indented));
QJsonObject json = doc.object();
QString type = json["MessageType"].toString();
if(type == "GetSensors")
{
qDebug()<<"Sending sensors";
sendSensors();
}
else if(type == "GetItems")
{
qDebug()<<"Sending Items";
sendItems();
}
else if(type == "SensorUpdate")
{
QJsonArray data = json["Data"].toArray();
for(QJsonValueRef sensorjson : data)
{
QJsonObject jsonobject = sensorjson.toObject();
Sensor sensor(jsonobject);
gotSensor(sensor);
}
}
}

42
src/service/service.h Normal file
View file

@ -0,0 +1,42 @@
#ifndef TCPSERVER_BASE_H
#define TCPSERVER_BASE_H
#include <QTcpServer>
#include <vector>
#include "sensors/sensor.h"
#include "items/item.h"
#include "items/itemsource.h"
class Service : public ItemSource
{
Q_OBJECT
protected:
typedef enum
{
STATE_IDLE,
STATE_RECV_JSON,
} client_state_t;
signals:
void gotSensor(Sensor sensor);
public slots:
void sensorEvent(Sensor sensor);
void itemUpdated(std::weak_ptr<Item> item);
virtual void refresh() override;
public:
Service(QObject* parent = nullptr);
void sendSensors();
void sendItems();
virtual void sendJson(const QJsonObject& json) = 0;
virtual bool launch(const QHostAddress &address = QHostAddress::Any, quint16 port = 0) = 0;
protected:
static QJsonObject createMessage(const QString& type, const QJsonArray& data);
virtual void processIncomeingJson(const QByteArray& jsonbytes);
};
#endif // TCPSERVER_BASE_H

100
src/service/tcpclient.cpp Normal file
View file

@ -0,0 +1,100 @@
#include <QTcpSocket>
#include <QJsonDocument>
#include <QJsonArray>
#include <iostream>
#include "items/item.h"
#include "service.h"
#include "tcpclient.h"
TcpClient::TcpClient(QObject* parent):
Service(parent),
socket(new QTcpSocket(this))
{
connect(socket, &QTcpSocket::readyRead, this, &TcpClient::socketReadyRead);
}
void TcpClient::sendJson(const QJsonObject& json)
{
QByteArray jsonData = QJsonDocument(json).toJson();
socket->write(QString("MSG JSON LEN " + QString::number(jsonData.size()) + "\n").toLatin1() + jsonData);
}
bool TcpClient::launch(const QHostAddress &address, quint16 port)
{
socket->connectToHost(address, port);
return socket->waitForConnected(2000);
}
void TcpClient::processIncomeingJson(const QByteArray& jsonbytes)
{
QJsonDocument doc = QJsonDocument::fromJson(jsonbytes);
QJsonObject json = doc.object();
QString type = json["MessageType"].toString();
if(type == "ItemUpdate")
{
std::cout<<"Got item json:\n"<<QString::fromLatin1(jsonbytes).toStdString();
QJsonArray data = json["Data"].toArray();
std::vector<std::shared_ptr<Item>> items;
for(QJsonValueRef itemjson : data)
{
QJsonObject jsonobject = itemjson.toObject();
std::shared_ptr<Item> item = Item::loadItem(jsonobject);
if(item)
{
item->setLoaded(false);
items.push_back(item);
}
}
if(!items.empty())
gotItems(items, true);
}
else
{
Service::processIncomeingJson(jsonbytes);
}
}
void TcpClient::processComand(const QByteArray& command)
{
if(command.startsWith("MSG JSON LEN "))
{
state = STATE_RECV_JSON;
recievebytes = command.mid(13).toLongLong();
}
}
void TcpClient::socketReadyRead()
{
buffer += socket->readAll();
bool remianing = true;
while(remianing)
{
remianing = false;
while(state == STATE_IDLE && buffer.contains('\n'))
{
size_t newlineIndex = buffer.indexOf('\n');
QByteArray command = buffer.left(newlineIndex);
buffer.remove(0, newlineIndex+1);
processComand(command);
remianing = true;
}
if(state == STATE_RECV_JSON)
{
if(recievebytes <= buffer.size())
{
QByteArray json = buffer.left(recievebytes);
buffer.remove(0, recievebytes);
recievebytes = 0;
state = STATE_IDLE;
processIncomeingJson(json);
remianing = true;
}
}
}
}
TcpClient::~TcpClient()
{
delete socket;
}

33
src/service/tcpclient.h Normal file
View file

@ -0,0 +1,33 @@
#ifndef TCPSERVER_CLIENT_H
#define TCPSERVER_CLIENT_H
#include <QTcpSocket>
#include <QJsonDocument>
#include <QJsonArray>
#include "service.h"
class TcpClient : public Service
{
Q_OBJECT
QTcpSocket* socket;
client_state_t state = STATE_IDLE;
long long recievebytes = 0;
QByteArray buffer;
public:
TcpClient(QObject* parent = nullptr);
~TcpClient();
virtual bool launch(const QHostAddress &address = QHostAddress::Any, quint16 port = 0) override;
virtual void sendJson(const QJsonObject& json) override;
protected:
virtual void processIncomeingJson(const QByteArray& jsonbytes) override;
private slots:
void socketReadyRead();
void processComand(const QByteArray& command);
};
#endif // TCPSERVER_CLIENT_H

153
src/service/tcpserver.cpp Normal file
View file

@ -0,0 +1,153 @@
#include <QTcpServer>
#include <vector>
#include <QTcpSocket>
#include <QJsonArray>
#include "items/item.h"
#include "service.h"
#include "tcpserver.h"
TcpServer::TcpServer(QObject* parent):
Service(parent),
server(this)
{
connect(&server, &QTcpServer::newConnection, this, &TcpServer::incomingConnection);
}
void TcpServer::sendJson(const QJsonObject& json)
{
for(auto client: clients)
{
QByteArray jsonData = QJsonDocument(json).toJson();
client.socket->write(QString("MSG JSON LEN " + QString::number(jsonData.size()) + "\n").toLatin1() + jsonData);
}
}
void TcpServer::processIncomeingJson(const QByteArray& jsonbytes)
{
QJsonDocument doc = QJsonDocument::fromJson(jsonbytes);
QJsonObject json = doc.object();
QString type = json["MessageType"].toString();
if(type == "ItemUpdate")
{
qDebug()<<"Got Items";
QJsonArray data = json["Data"].toArray();
bool FullList = json["FullList"].toBool(false);
std::vector<std::shared_ptr<Item>> items;
for(QJsonValueRef itemjson : data)
{
QJsonObject jsonobject = itemjson.toObject();
std::shared_ptr<Item> item = Item::loadItem(jsonobject);
item->setLoaded(FullList);
if(item)
items.push_back(item);
}
if(FullList && !items.empty())
{
requestReplaceItems(items);
sigRequestSave();
}
else if(!items.empty())
{
gotItems(items, false);
}
}
else
{
Service::processIncomeingJson(jsonbytes);
}
}
bool TcpServer::launch(const QHostAddress &address, quint16 port)
{
return server.listen(address, port);
}
void TcpServer::incomingConnection()
{
while(server.hasPendingConnections())
{
QTcpSocket* client = server.nextPendingConnection();
qDebug()<<"Got new client from"<<client->peerAddress().toString();
if(client)
{
clients.push_back({client});
connect(client, &QTcpSocket::errorOccurred, this, &TcpServer::socketError);
connect(client, &QTcpSocket::disconnected, this, &TcpServer::socketDisconnect);
connect(client, &QTcpSocket::readyRead, this, &TcpServer::socketReadyRead);
}
}
}
void TcpServer::socketError(QAbstractSocket::SocketError socketError)
{
(void)socketError;
for(size_t i = 0; i < clients.size(); i++)
{
if(clients[i].socket == TcpServer::sender())
{
clients.erase(clients.begin()+i);
--i;
}
}
}
void TcpServer::socketDisconnect()
{
for(size_t i = 0; i < clients.size(); i++)
{
if(clients[i].socket == TcpServer::sender())
{
clients.erase(clients.begin()+i);
--i;
}
}
}
void TcpServer::processComand(const QByteArray& command, Client& client)
{
if(command.startsWith("MSG JSON LEN "))
{
client.state = STATE_RECV_JSON;
client.recievebytes = command.mid(13).toLongLong();
qDebug()<<"Got command:"<<QString::fromLatin1(command);
}
}
void TcpServer::socketReadyRead()
{
for(size_t i = 0; i < clients.size(); i++)
{
if(clients[i].socket == sender())
{
QByteArray newChars = clients[i].socket->readAll();
clients[i].buffer += newChars;
bool remianing = true;
while(remianing)
{
qDebug()<<clients[i].buffer;
remianing = false;
while(clients[i].state == STATE_IDLE && clients[i].buffer.contains('\n'))
{
size_t newlineIndex = clients[i].buffer.indexOf('\n');
QByteArray command = clients[i].buffer.left(newlineIndex);
clients[i].buffer.remove(0, newlineIndex+1);
processComand(command, clients[i]);
remianing = true;
}
if(clients[i].state == STATE_RECV_JSON)
{
if(clients[i].recievebytes <= clients[i].buffer.size())
{
QByteArray json = clients[i].buffer.left(clients[i].recievebytes);
clients[i].buffer.remove(0, clients[i].recievebytes);
clients[i].recievebytes = 0;
clients[i].state = STATE_IDLE;
processIncomeingJson(json);
remianing = true;
}
}
}
}
}
}

45
src/service/tcpserver.h Normal file
View file

@ -0,0 +1,45 @@
#ifndef TCPSERVER_SERVER_H
#define TCPSERVER_SERVER_H
#include <QTcpServer>
#include <vector>
#include "service.h"
class TcpServer : public Service
{
Q_OBJECT
struct Client
{
QTcpSocket* socket;
QByteArray buffer;
client_state_t state = STATE_IDLE;
long long recievebytes = 0;
};
std::vector<Client> clients;
QTcpServer server;
public:
TcpServer(QObject* parent = nullptr);
virtual bool launch(const QHostAddress &address = QHostAddress::Any, quint16 port = 0) override;
virtual void sendJson(const QJsonObject& json) override;
signals:
void sigRequestSave();
private slots:
void incomingConnection();
void socketError(QAbstractSocket::SocketError socketError);
void socketDisconnect();
void socketReadyRead();
protected:
virtual void processIncomeingJson(const QByteArray& jsonbytes) override;
private:
void processComand(const QByteArray& command, Client& client);
};
#endif // TCPSERVER_SERVER_H

View file

@ -1,342 +0,0 @@
#include <QTcpSocket>
#include <QJsonDocument>
#include <QJsonArray>
#include <iostream>
#include "items/item.h"
#include "items/itemstore.h"
#include "tcpserver.h"
TcpService::TcpService(QObject* parent):
ItemSource(parent)
{}
QJsonObject TcpService::createMessage(const QString& type, const QJsonArray& data)
{
QJsonObject json;
json["MessageType"] = type;
json["Data"] = data;
return json;
}
void TcpService::sensorEvent(Sensor sensor)
{
QJsonArray sensors;
QJsonObject sensorjson;
sensor.store(sensorjson);
sensors.append(sensorjson);
QJsonObject json = createMessage("SensorUpdate", sensors);
sendJson(json);
}
void TcpService::itemUpdated(std::weak_ptr<Item> item)
{
qDebug()<<__func__;
QJsonArray items;
QJsonObject itemjson;
item.lock()->store(itemjson);
items.append(itemjson);
QJsonObject json = createMessage("ItemUpdate", items);
json["FullList"] = false;
sendJson(json);
}
void TcpService::refresh()
{
sendJson(createMessage("GetSensors", QJsonArray()));
sendJson(createMessage("GetItems", QJsonArray()));
}
void TcpService::sendSensors()
{
QJsonArray sensors;
for(auto& sensor: *globalSensors.getSensors())
{
QJsonObject sensorjson;
sensor.store(sensorjson);
sensors.append(sensorjson);
}
sendJson(createMessage("SensorUpdate", sensors));
}
void TcpService::sendItems()
{
QJsonArray items;
for(auto& item: *globalItems.getItems())
{
QJsonObject itemjson;
item->store(itemjson);
items.append(itemjson);
}
QJsonObject json = createMessage("ItemUpdate", items);
json["FullList"] = true;
sendJson(json);
}
void TcpService::processIncomeingJson(const QByteArray& jsonbytes)
{
QJsonDocument doc = QJsonDocument::fromJson(jsonbytes);
qDebug()<<"Got Json:"<<QString::fromLatin1(doc.toJson(QJsonDocument::JsonFormat::Indented));
QJsonObject json = doc.object();
QString type = json["MessageType"].toString();
if(type == "GetSensors")
{
qDebug()<<"Sending sensors";
sendSensors();
}
else if(type == "GetItems")
{
qDebug()<<"Sending Items";
sendItems();
}
else if(type == "SensorUpdate")
{
QJsonArray data = json["Data"].toArray();
for(QJsonValueRef sensorjson : data)
{
QJsonObject jsonobject = sensorjson.toObject();
Sensor sensor(jsonobject);
gotSensor(sensor);
}
}
}
TcpClient::TcpClient(QObject* parent):
TcpService(parent),
socket(new QTcpSocket(this))
{
connect(socket, &QTcpSocket::readyRead, this, &TcpClient::socketReadyRead);
}
void TcpClient::sendJson(const QJsonObject& json)
{
QByteArray jsonData = QJsonDocument(json).toJson();
socket->write(QString("MSG JSON LEN " + QString::number(jsonData.size()) + "\n").toLatin1() + jsonData);
}
bool TcpClient::launch(const QHostAddress &address, quint16 port)
{
socket->connectToHost(address, port);
return socket->waitForConnected(2000);
}
void TcpClient::processIncomeingJson(const QByteArray& jsonbytes)
{
QJsonDocument doc = QJsonDocument::fromJson(jsonbytes);
QJsonObject json = doc.object();
QString type = json["MessageType"].toString();
if(type == "ItemUpdate")
{
std::cout<<"Got item json:\n"<<QString::fromLatin1(jsonbytes).toStdString();
QJsonArray data = json["Data"].toArray();
std::vector<std::shared_ptr<Item>> items;
for(QJsonValueRef itemjson : data)
{
QJsonObject jsonobject = itemjson.toObject();
std::shared_ptr<Item> item = Item::loadItem(jsonobject);
if(item)
{
item->setLoaded(false);
items.push_back(item);
}
}
if(!items.empty())
gotItems(items, true);
}
else
{
TcpService::processIncomeingJson(jsonbytes);
}
}
void TcpClient::processComand(const QByteArray& command)
{
if(command.startsWith("MSG JSON LEN "))
{
state = STATE_RECV_JSON;
recievebytes = command.mid(13).toLongLong();
}
}
void TcpClient::socketReadyRead()
{
buffer += socket->readAll();
bool remianing = true;
while(remianing)
{
remianing = false;
while(state == STATE_IDLE && buffer.contains('\n'))
{
size_t newlineIndex = buffer.indexOf('\n');
QByteArray command = buffer.left(newlineIndex);
buffer.remove(0, newlineIndex+1);
processComand(command);
remianing = true;
}
if(state == STATE_RECV_JSON)
{
if(recievebytes <= buffer.size())
{
QByteArray json = buffer.left(recievebytes);
buffer.remove(0, recievebytes);
recievebytes = 0;
state = STATE_IDLE;
processIncomeingJson(json);
remianing = true;
}
}
}
}
TcpClient::~TcpClient()
{
delete socket;
}
TcpServer::TcpServer(QObject* parent):
TcpService(parent),
server(this)
{
connect(&server, &QTcpServer::newConnection, this, &TcpServer::incomingConnection);
}
void TcpServer::sendJson(const QJsonObject& json)
{
for(auto client: clients)
{
QByteArray jsonData = QJsonDocument(json).toJson();
client.socket->write(QString("MSG JSON LEN " + QString::number(jsonData.size()) + "\n").toLatin1() + jsonData);
}
}
void TcpServer::processIncomeingJson(const QByteArray& jsonbytes)
{
QJsonDocument doc = QJsonDocument::fromJson(jsonbytes);
QJsonObject json = doc.object();
QString type = json["MessageType"].toString();
if(type == "ItemUpdate")
{
qDebug()<<"Got Items";
QJsonArray data = json["Data"].toArray();
bool FullList = json["FullList"].toBool(false);
std::vector<std::shared_ptr<Item>> items;
for(QJsonValueRef itemjson : data)
{
QJsonObject jsonobject = itemjson.toObject();
std::shared_ptr<Item> item = Item::loadItem(jsonobject);
item->setLoaded(FullList);
if(item)
items.push_back(item);
}
if(FullList && !items.empty())
{
requestReplaceItems(items);
sigRequestSave();
}
else if(!items.empty())
{
gotItems(items, false);
}
}
else
{
TcpService::processIncomeingJson(jsonbytes);
}
}
bool TcpServer::launch(const QHostAddress &address, quint16 port)
{
return server.listen(address, port);
}
void TcpServer::incomingConnection()
{
while(server.hasPendingConnections())
{
QTcpSocket* client = server.nextPendingConnection();
qDebug()<<"Got new client from"<<client->peerAddress().toString();
if(client)
{
clients.push_back({client});
connect(client, &QTcpSocket::errorOccurred, this, &TcpServer::socketError);
connect(client, &QTcpSocket::disconnected, this, &TcpServer::socketDisconnect);
connect(client, &QTcpSocket::readyRead, this, &TcpServer::socketReadyRead);
}
}
}
void TcpServer::socketError(QAbstractSocket::SocketError socketError)
{
(void)socketError;
for(size_t i = 0; i < clients.size(); i++)
{
if(clients[i].socket == TcpServer::sender())
{
clients.erase(clients.begin()+i);
--i;
}
}
}
void TcpServer::socketDisconnect()
{
for(size_t i = 0; i < clients.size(); i++)
{
if(clients[i].socket == TcpServer::sender())
{
clients.erase(clients.begin()+i);
--i;
}
}
}
void TcpServer::processComand(const QByteArray& command, Client& client)
{
if(command.startsWith("MSG JSON LEN "))
{
client.state = STATE_RECV_JSON;
client.recievebytes = command.mid(13).toLongLong();
qDebug()<<"Got command:"<<QString::fromLatin1(command);
}
}
void TcpServer::socketReadyRead()
{
for(size_t i = 0; i < clients.size(); i++)
{
if(clients[i].socket == sender())
{
QByteArray newChars = clients[i].socket->readAll();
clients[i].buffer += newChars;
bool remianing = true;
while(remianing)
{
qDebug()<<clients[i].buffer;
remianing = false;
while(clients[i].state == STATE_IDLE && clients[i].buffer.contains('\n'))
{
size_t newlineIndex = clients[i].buffer.indexOf('\n');
QByteArray command = clients[i].buffer.left(newlineIndex);
clients[i].buffer.remove(0, newlineIndex+1);
processComand(command, clients[i]);
remianing = true;
}
if(clients[i].state == STATE_RECV_JSON)
{
if(clients[i].recievebytes <= clients[i].buffer.size())
{
QByteArray json = clients[i].buffer.left(clients[i].recievebytes);
clients[i].buffer.remove(0, clients[i].recievebytes);
clients[i].recievebytes = 0;
clients[i].state = STATE_IDLE;
processIncomeingJson(json);
remianing = true;
}
}
}
}
}
}

View file

@ -1,101 +0,0 @@
#ifndef TCPSERVER_H
#define TCPSERVER_H
#include <QTcpServer>
#include <vector>
#include "sensors/sensor.h"
#include "items/item.h"
#include "items/itemsource.h"
class TcpService : public ItemSource
{
Q_OBJECT
protected:
typedef enum
{
STATE_IDLE,
STATE_RECV_JSON,
} client_state_t;
signals:
void gotSensor(Sensor sensor);
public slots:
void sensorEvent(Sensor sensor);
void itemUpdated(std::weak_ptr<Item> item);
virtual void refresh() override;
public:
TcpService(QObject* parent = nullptr);
void sendSensors();
void sendItems();
virtual void sendJson(const QJsonObject& json) = 0;
virtual bool launch(const QHostAddress &address = QHostAddress::Any, quint16 port = 0) = 0;
protected:
static QJsonObject createMessage(const QString& type, const QJsonArray& data);
virtual void processIncomeingJson(const QByteArray& jsonbytes);
};
class TcpClient : public TcpService
{
Q_OBJECT
QTcpSocket* socket;
client_state_t state = STATE_IDLE;
long long recievebytes = 0;
QByteArray buffer;
public:
TcpClient(QObject* parent = nullptr);
~TcpClient();
virtual bool launch(const QHostAddress &address = QHostAddress::Any, quint16 port = 0) override;
virtual void sendJson(const QJsonObject& json) override;
protected:
virtual void processIncomeingJson(const QByteArray& jsonbytes) override;
private slots:
void socketReadyRead();
void processComand(const QByteArray& command);
};
class TcpServer : public TcpService
{
Q_OBJECT
struct Client
{
QTcpSocket* socket;
QByteArray buffer;
client_state_t state = STATE_IDLE;
long long recievebytes = 0;
};
std::vector<Client> clients;
QTcpServer server;
public:
TcpServer(QObject* parent = nullptr);
virtual bool launch(const QHostAddress &address = QHostAddress::Any, quint16 port = 0) override;
virtual void sendJson(const QJsonObject& json) override;
signals:
void sigRequestSave();
private slots:
void incomingConnection();
void socketError(QAbstractSocket::SocketError socketError);
void socketDisconnect();
void socketReadyRead();
protected:
virtual void processIncomeingJson(const QByteArray& jsonbytes) override;
private:
void processComand(const QByteArray& command, Client& client);
};
#endif // TCPSERVER_H