Compare commits

...

4 commits

24 changed files with 419 additions and 141 deletions

View file

@ -10,7 +10,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
add_compile_options(-Wall)
# Find Qt packages
find_package(Qt6 COMPONENTS Core Gui Widgets Network Multimedia SerialPort Mqtt REQUIRED)
find_package(Qt6 COMPONENTS Core Gui Widgets Network Multimedia SerialPort Mqtt WebSockets REQUIRED)
# Find dependencies using pkg-config
find_package(PkgConfig REQUIRED)
@ -49,8 +49,12 @@ target_sources(SHinterface
src/service/service.cpp
src/service/tcpclient.h
src/service/tcpclient.cpp
src/service/server.h
src/service/server.cpp
src/service/tcpserver.h
src/service/tcpserver.cpp
src/service/websocketserver.h
src/service/websocketserver.cpp
src/actors/actor.h
src/actors/actor.cpp
@ -166,6 +170,7 @@ target_link_libraries(SHinterface
Qt6::Multimedia
Qt6::SerialPort
Qt6::Mqtt
Qt6::WebSockets
${PIPEWIRE_LIBRARIES}
${LIBNL3_LIBRARIES}
)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

After

Width:  |  Height:  |  Size: 112 KiB

Before After
Before After

View file

@ -65,7 +65,7 @@ void PolynomalActor::load(const QJsonObject& json, bool preserve)
pow2_ = json["Pow2"].toDouble(0);
pow1_ = json["Pow1"].toDouble(1);
pow0_ = json["Pow0"].toDouble(0);
sensor_.type = json["SensorType"].toInt(0);
sensor_.type = static_cast<Sensor::sensor_type_t>(json["SensorType"].toInt(0));
sensor_.id = json["SensorId"].toInt(0);
sensor_.field = json["SensorField"].toInt(0);
sensor_.name = json["SensorName"].toString("Sensor");

View file

@ -100,7 +100,7 @@ void Regulator::load(const QJsonObject& json, bool preserve)
setPoint_ = json["SetPoint"].toDouble(22);
safeValue_ = json["SafeValue"].toDouble(0);
timeout_ = json["Timeout"].toDouble(1800);
sensor_.type = json["SensorType"].toInt(0);
sensor_.type = static_cast<Sensor::sensor_type_t>(json["SensorType"].toInt(0));
sensor_.id = json["SensorId"].toInt(0);
sensor_.field = json["SensorField"].toInt(0);
sensor_.name = json["SensorName"].toString("Sensor");

View file

@ -65,7 +65,7 @@ void SensorActor::load(const QJsonObject& json, bool preserve)
Actor::load(json, preserve);
sloap_ = json["Sloap"].toInt(0);
threshold_ = json["Threshold"].toDouble(0);
sensor_.type = json["SensorType"].toInt(0);
sensor_.type = static_cast<Sensor::sensor_type_t>(json["SensorType"].toInt(0));
sensor_.id = json["SensorId"].toInt(0);
sensor_.field = json["SensorField"].toInt(0);
sensor_.name = json["SensorName"].toString("Sensor");

View file

@ -79,7 +79,7 @@ bool ItemData::hasChanged(const ItemData& other)
return false;
}
bool ItemData::isHidden()
bool ItemData::isHidden() const
{
return hidden_;
}
@ -126,6 +126,7 @@ void Item::store(QJsonObject &json)
}
}
json["Actors"] = actorsArray;
json["ValueType"] = type_;
}
void Item::load(const QJsonObject &json, const bool preserve)
@ -144,6 +145,30 @@ void Item::load(const QJsonObject &json, const bool preserve)
}
}
Item& Item::operator=(const ItemData& other)
{
name_ = other.getName();
value_ = other.getValue();
itemId_ = other.id();
hidden_ = other.isHidden();
return *this;
}
void Item::requestUpdate(ItemUpdateRequest update)
{
if(!hasChanged(update.data))
return;
if(update.type == ITEM_UPDATE_USER || (update.type == ITEM_UPDATE_ACTOR && !override_) || update.type == ITEM_UPDATE_REMOTE)
{
if(programMode == PROGRAM_MODE_PRIMARY || programMode == PROGRAM_MODE_HEADLESS_PRIMARY)
enactValue(update.data.getValue());
*this = update.data;
update.data = *this;
updated(update);
}
}
void Item::actorSetValue(uint8_t value)
{
if(!override_ && (programMode == PROGRAM_MODE_PRIMARY || programMode == PROGRAM_MODE_HEADLESS_PRIMARY))
@ -154,6 +179,7 @@ void Item::setValue(uint8_t value)
{
qDebug()<<__func__;
informValue(value);
updated(*this);
if(programMode == PROGRAM_MODE_PRIMARY || programMode == PROGRAM_MODE_HEADLESS_PRIMARY)
enactValue(value);
}
@ -164,7 +190,6 @@ void Item::informValue(uint8_t value)
{
value_ = value;
valueChanged(value_);
updated(*this);
}
}
@ -249,30 +274,22 @@ void Item::mergeLoaded(Item& item)
std::shared_ptr<Item> Item::loadItem(const QJsonObject& json)
{
std::shared_ptr<Item> newItem = nullptr;
if(json["Type"].toString("") == "Relay")
{
if(json["Type"].toString("Item") == "Item")
newItem = std::shared_ptr<Item>(new Item);
else if(json["Type"].toString("") == "Relay")
newItem = std::shared_ptr<Relay>(new Relay);
}
else if(json["Type"].toString("") == "Message")
{
newItem = std::shared_ptr<MessageItem>(new MessageItem);
}
else if(json["Type"].toString("") == "System")
{
newItem = std::shared_ptr<SystemItem>(new SystemItem);
}
else if(json["Type"].toString("") == "Aux")
{
newItem = std::shared_ptr<AuxItem>(new AuxItem);
}
else if(json["Type"].toString("") == "Power")
{
newItem = std::shared_ptr<PowerItem>(new PowerItem);
}
else if(json["Type"].toString("") == "Rgb")
{
newItem = std::shared_ptr<RgbItem>(new RgbItem);
}
else
qWarning()<<"Unable to load unkown item type: "<<json["Type"].toString();
if(newItem)
{
newItem->load(json);

View file

@ -14,6 +14,12 @@ typedef enum {
ITEM_VALUE_NO_VALUE
} item_value_type_t;
typedef enum {
ITEM_UPDATE_USER = 0,
ITEM_UPDATE_ACTOR,
ITEM_UPDATE_REMOTE
} item_update_type_t;
class ItemData
{
protected:
@ -48,7 +54,7 @@ public:
uint8_t getValue() const;
bool getLoaded() const;
void setLoaded(bool loaded);
bool isHidden();
bool isHidden() const;
void setHidden(bool hidden);
item_value_type_t getValueType();
virtual QString getName() const;
@ -56,24 +62,26 @@ public:
virtual void load(const QJsonObject& json, const bool preserve = false);
};
struct ItemUpdateRequest
{
item_update_type_t type;
ItemData data;
bool valueOnly;
};
class Item: public QObject, public ItemData
{
Q_OBJECT
private:
std::vector< std::shared_ptr<Actor> > actors_;
bool override_ = false;
signals:
void valueChanged(uint8_t value);
void updated(ItemData data);
private slots:
virtual void actorSetValue(uint8_t value);
void updated(ItemUpdateRequest update);
public slots:
void setValue(uint8_t value);
void requestUpdate(ItemUpdateRequest update);
public:
@ -83,6 +91,7 @@ public:
virtual ~Item();
Item& operator=(const ItemData& other);
std::vector< std::shared_ptr<Actor> >& getActors();
bool hasActors();
void addActor(std::shared_ptr<Actor> actor);
@ -91,7 +100,6 @@ public:
void setActorsActive(bool in);
void setOverride(const bool in);
bool getOverride();
void informValue(uint8_t value);
void mergeLoaded(Item& item);
virtual void store(QJsonObject& json);

View file

@ -95,7 +95,8 @@ void ItemStore::updateItem(const ItemData& item, bool inform)
else
items_[i]->setValue(item.getValue());
}
qDebug()<<"Item"<<items_[i]->getName()<<"updated";
qDebug()<<"Item"<<items_[i]->getName()<<"updated"<<(inform ? "with inform" : "");
if(!inform)
itemUpdated(items_[i]);
}
}
@ -119,8 +120,11 @@ void ItemStore::itemUpdateSlot(ItemData data)
for(std::shared_ptr<Item>& item: items_)
{
if(*item == data)
{
qDebug()<<"Item"<<data.getName()<<"updated from update slot";
itemUpdated(std::weak_ptr<Item>(item));
}
}
}
std::shared_ptr<Item> ItemStore::getItem(uint32_t id)

View file

@ -35,7 +35,7 @@ int main(int argc, char *argv[])
parser.addOption(masterOption);
QCommandLineOption hostOption(QStringList() << "H" << "host", QCoreApplication::translate("main", "Set server host ip addres"), "address", "0.0.0.0");
parser.addOption(hostOption);
QCommandLineOption portOption(QStringList() << "p" << "port", QCoreApplication::translate("main", "Set server Port"), "port", "104476");
QCommandLineOption portOption(QStringList() << "p" << "port", QCoreApplication::translate("main", "Set server Port"), "port", "38940");
parser.addOption(portOption);
QCommandLineOption settingsPathOption(QStringList()<<"c"<<"config", QCoreApplication::translate("main", "Set config file"), "configFilePath",
QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) + "/shinterface.json");

View file

@ -72,13 +72,15 @@ PrimaryMainObject::PrimaryMainObject(QIODevice* microDevice, const QString& sett
MainObject(parent),
settingsPath(settingsPath),
micro(microDevice),
tcpServer(new TcpServer),
tcpServer(new TcpServer(this)),
webServer(new WebSocketServer("shinterface", this)),
sunSensorSource(49.824972, 8.702194),
fixedItems(&micro)
{
//connect sensors subsystem
connect(&globalSensors, &SensorStore::sensorChangedState, tcpServer, &TcpServer::sensorEvent);
connect(tcpServer, &TcpServer::gotSensor, &globalSensors, &SensorStore::sensorGotState);
connect(webServer, &WebSocketServer::gotSensor, &globalSensors, &SensorStore::sensorGotState);
connect(&sunSensorSource, &SunSensorSource::stateChanged, &globalSensors, &SensorStore::sensorGotState);
connect(&micro, &Microcontroller::gotSensorState, &globalSensors, &SensorStore::sensorGotState);
connect(&mqttSensorSource, &MqttSensorSource::stateChanged, &globalSensors, &SensorStore::sensorGotState);
@ -87,6 +89,7 @@ PrimaryMainObject::PrimaryMainObject(QIODevice* microDevice, const QString& sett
globalItems.registerItemSource(&fixedItems);
globalItems.registerItemSource(tcpServer);
globalItems.registerItemSource(webServer);
globalItems.registerItemSource(&micro);
globalItems.registerItemSource(&itemLoader);
@ -98,7 +101,9 @@ PrimaryMainObject::PrimaryMainObject(QIODevice* microDevice, const QString& sett
mqttSensorSource.start(mqttJson);
tcpServer->launch(QHostAddress(host), port);
webServer->launch(QHostAddress(host), port+1);
connect(&globalItems, &ItemStore::itemUpdated, tcpServer, &TcpServer::itemUpdated);
connect(&globalItems, &ItemStore::itemUpdated, webServer, &WebSocketServer::itemUpdated);
}
PrimaryMainObject::~PrimaryMainObject()

View file

@ -19,6 +19,7 @@
#include "items/itemloadersource.h"
#include "service/tcpclient.h"
#include "service/tcpserver.h"
#include "service/websocketserver.h"
class MainObject : public QObject
{
@ -44,6 +45,7 @@ public:
Microcontroller micro;
TcpServer* tcpServer;
WebSocketServer* webServer;
//sensors
SunSensorSource sunSensorSource;

View file

@ -6,8 +6,8 @@ SensorStore globalSensors;
SensorStore::SensorStore(QObject *parent): QObject(parent)
{
sensors_.push_back(Sensor(0,1,0,"Front door"));
sensors_.push_back(Sensor(0,0,0,"Bedroom door"));
sensors_.push_back(Sensor(Sensor::TYPE_DOOR,1,0,"Front door"));
sensors_.push_back(Sensor(Sensor::TYPE_DOOR,0,0,"Bedroom door"));
}
void SensorStore::sensorGotState(const Sensor& sensor)

View file

@ -10,32 +10,34 @@ class Sensor
{
public:
static constexpr uint8_t TYPE_DOOR = 0;
static constexpr uint8_t TYPE_TEMPERATURE = 1;
static constexpr uint8_t TYPE_HUMIDITY = 2;
static constexpr uint8_t TYPE_PRESSURE = 3;
static constexpr uint8_t TYPE_BRIGHTNESS = 4;
static constexpr uint8_t TYPE_BUTTON = 5;
static constexpr uint8_t TYPE_ADC = 6;
static constexpr uint8_t TYPE_CO2 = 7;
static constexpr uint8_t TYPE_FORMALDEHYD= 8;
static constexpr uint8_t TYPE_PM25 = 9;
static constexpr uint8_t TYPE_TOTAL_VOC = 10;
static constexpr uint8_t TYPE_LOWBATTERY = 128;
static constexpr uint8_t TYPE_SHUTDOWN_IMMINENT = 251;
static constexpr uint8_t TYPE_OCUPANCY = 252;
static constexpr uint8_t TYPE_SUN_ALTITUDE = 253;
static constexpr uint8_t TYPE_AUDIO_OUTPUT = 254;
static constexpr uint8_t TYPE_DUMMY = 255;
typedef enum {
TYPE_DOOR = 0,
TYPE_TEMPERATURE,
TYPE_HUMIDITY,
TYPE_PRESSURE,
TYPE_BRIGHTNESS,
TYPE_BUTTON,
TYPE_ADC,
TYPE_CO2,
TYPE_FORMALDEHYD,
TYPE_PM25,
TYPE_TOTAL_VOC,
TYPE_LOWBATTERY = 128,
TYPE_SHUTDOWN_IMMINENT = 251,
TYPE_OCUPANCY,
TYPE_SUN_ALTITUDE,
TYPE_AUDIO_OUTPUT,
TYPE_DUMMY,
} sensor_type_t;
uint8_t type;
sensor_type_t type;
uint64_t id;
float field;
QString name;
QDateTime lastSeen;
bool hidden;
Sensor(uint64_t typeIn, uint8_t idIn, float fieldIn = 0, QString nameIn = "", bool hiddenIn = false): type(typeIn),
Sensor(sensor_type_t typeIn, uint64_t idIn, float fieldIn = 0, QString nameIn = "", bool hiddenIn = false): type(typeIn),
id(idIn), field(fieldIn), name(nameIn), hidden(hiddenIn)
{
lastSeen = QDateTime::currentDateTime();
@ -48,12 +50,14 @@ public:
}
Sensor(const QJsonObject& json)
{
type = json["SensorType"].toInt(0);
type = static_cast<sensor_type_t>(json["SensorType"].toInt(0));
id = json["Id"].toInt(0);
field = json["Field"].toDouble(0);
name = json["Name"].toString("Sensor");
lastSeen = QDateTime::fromString(json["LastSeen"].toString(""));
hidden = json["Hidden"].toBool(false);
name = json["Name"].toString();
if(name == "")
generateName();
}
inline bool operator==(const Sensor& in) const
{
@ -72,7 +76,7 @@ public:
QStringList bufferList = str.split(' ');
if(bufferList.size() >= 7)
{
Sensor sensor(bufferList[2].toUInt(), bufferList[4].toUInt(), bufferList[6].toUInt());
Sensor sensor(static_cast<sensor_type_t>(bufferList[2].toUInt()), bufferList[4].toUInt(), bufferList[6].toUInt());
if(sensor.type == Sensor::TYPE_HUMIDITY || sensor.type == Sensor::TYPE_TEMPERATURE)
sensor.field = sensor.field/10;
@ -100,6 +104,7 @@ public:
json["Name"] = name;
json["LastSeen"] = lastSeen.toString();
json["Hidden"] = hidden;
json["Unit"] = getUnit();
}
inline void generateName()
{
@ -119,6 +124,31 @@ public:
name = "Shutdown Imminent";
else name = "Sensor Type " + QString::number(type) + " Id " + QString::number(id);
}
QString getUnit()
{
switch(type)
{
case TYPE_TEMPERATURE:
return "°C";
case TYPE_HUMIDITY:
return "%";
case TYPE_PRESSURE:
return "hPa";
case TYPE_BRIGHTNESS:
return "lx";
case TYPE_CO2:
return "ppm";
case TYPE_FORMALDEHYD:
case TYPE_PM25:
return "µg/m³";
case TYPE_TOTAL_VOC:
return "ppb";
case TYPE_SUN_ALTITUDE:
return "°";
default:
return "";
}
}
};
class SensorStore: public QObject

93
src/service/server.cpp Normal file
View file

@ -0,0 +1,93 @@
#include <QAbstractSocket>
#include <QWebSocket>
#include <QJsonArray>
#include "items/item.h"
#include "service.h"
#include "server.h"
Server::Server(QObject* parent)
: Service(parent)
{
}
Server::~Server()
{
}
void Server::processIncomeingJson(const QByteArray& jsonbytes)
{
QJsonDocument doc = QJsonDocument::fromJson(jsonbytes);
QJsonObject json = doc.object();
QString type = json["MessageType"].toString();
if(type == "ItemUpdate")
{
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);
if(item)
{
qDebug()<<"Server got item"<<item->getName();
item->setLoaded(FullList);
items.push_back(item);
}
}
if(FullList && !items.empty())
{
requestReplaceItems(items);
sigRequestSave();
}
else if(!items.empty())
{
gotItems(items, false);
}
}
else
{
Service::processIncomeingJson(jsonbytes);
}
}
void Server::handleSocketError()
{
QObject* obj = sender();
if (obj) {
removeClient(static_cast<QTcpSocket*>(obj));
}
}
void Server::handleSocketDisconnect()
{
QObject* obj = sender();
if (obj) {
removeClient(static_cast<QTcpSocket*>(obj));
}
}
void Server::removeClient(QTcpSocket* socket)
{
for(size_t i = 0; i < clients.size(); i++)
{
if(clients[i].socket.tcpSocket == socket)
{
clients.erase(clients.begin() + i);
--i;
}
}
}
void Server::removeClient(QWebSocket* socket)
{
for(size_t i = 0; i < clients.size(); i++)
{
if(clients[i].socket.webSocket == socket)
{
clients.erase(clients.begin() + i);
--i;
}
}
}

49
src/service/server.h Normal file
View file

@ -0,0 +1,49 @@
#ifndef SERVER_BASE_H
#define SERVER_BASE_H
#include <QTcpServer>
#include <QWebSocket>
#include <vector>
#include "service.h"
class Server : public Service
{
Q_OBJECT
protected:
typedef enum
{
STATE_IDLE,
STATE_RECV_JSON,
} client_state_t;
struct Client
{
union {
QTcpSocket* tcpSocket;
QWebSocket* webSocket;
} socket;
QByteArray buffer;
client_state_t state = STATE_IDLE;
long long recievebytes = 0;
};
std::vector<Client> clients;
public:
Server(QObject* parent = nullptr);
virtual ~Server();
protected:
virtual void processIncomeingJson(const QByteArray& jsonbytes) override;
void handleSocketError();
void handleSocketDisconnect();
void removeClient(QTcpSocket* socket);
void removeClient(QWebSocket* socket);
signals:
void sigRequestSave();
};
#endif // SERVER_BASE_H

View file

@ -1,7 +1,6 @@
#include <QTcpSocket>
#include <QJsonDocument>
#include <QJsonArray>
#include <iostream>
#include "items/item.h"
#include "items/itemstore.h"
@ -32,7 +31,7 @@ void Service::sensorEvent(Sensor sensor)
void Service::itemUpdated(std::weak_ptr<Item> item)
{
qDebug()<<__func__;
qDebug()<<"Service sending item"<<item.lock()->getName();
QJsonArray items;
QJsonObject itemjson;
item.lock()->store(itemjson);
@ -78,7 +77,6 @@ void Service::sendItems()
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")
@ -94,6 +92,7 @@ void Service::processIncomeingJson(const QByteArray& jsonbytes)
else if(type == "SensorUpdate")
{
QJsonArray data = json["Data"].toArray();
qWarning()<<"Got sensor update with no/empty sensors array";
for(QJsonValueRef sensorjson : data)
{
QJsonObject jsonobject = sensorjson.toObject();

View file

@ -33,7 +33,6 @@ void TcpClient::processIncomeingJson(const QByteArray& jsonbytes)
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)
@ -42,6 +41,7 @@ void TcpClient::processIncomeingJson(const QByteArray& jsonbytes)
std::shared_ptr<Item> item = Item::loadItem(jsonobject);
if(item)
{
qDebug()<<"Client got item"<<item->getName();
item->setLoaded(false);
items.push_back(item);
}

View file

@ -4,11 +4,11 @@
#include <QJsonArray>
#include "items/item.h"
#include "service.h"
#include "server.h"
#include "tcpserver.h"
TcpServer::TcpServer(QObject* parent):
Service(parent),
Server(parent),
server(this)
{
connect(&server, &QTcpServer::newConnection, this, &TcpServer::incomingConnection);
@ -16,50 +16,23 @@ TcpServer::TcpServer(QObject* parent):
void TcpServer::sendJson(const QJsonObject& json)
{
for(auto client: clients)
for(auto& client: clients)
{
if(client.socket.tcpSocket) {
QByteArray jsonData = QJsonDocument(json).toJson();
client.socket->write(QString("MSG JSON LEN " + QString::number(jsonData.size()) + "\n").toLatin1() + jsonData);
client.socket.tcpSocket->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);
}
Server::processIncomeingJson(jsonbytes);
}
bool TcpServer::launch(const QHostAddress &address, quint16 port)
{
qInfo()<<"Tcp server launched on"<<address<<"port"<<port;
return server.listen(address, port);
}
@ -71,38 +44,16 @@ void TcpServer::incomingConnection()
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);
Client c;
c.socket.tcpSocket = client;
clients.push_back(c);
connect(client, &QTcpSocket::errorOccurred, this, [this, client]() { removeClient(client); });
connect(client, &QTcpSocket::disconnected, this, [this, client]() { removeClient(client); });
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 "))
@ -117,15 +68,15 @@ void TcpServer::socketReadyRead()
{
for(size_t i = 0; i < clients.size(); i++)
{
if(clients[i].socket == sender())
QTcpSocket* tcp = clients[i].socket.tcpSocket;
if(tcp && tcp == sender())
{
QByteArray newChars = clients[i].socket->readAll();
QByteArray newChars = tcp->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'))
{

View file

@ -4,21 +4,12 @@
#include <QTcpServer>
#include <vector>
#include "service.h"
#include "server.h"
class TcpServer : public Service
class TcpServer : public Server
{
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:
@ -31,8 +22,6 @@ signals:
private slots:
void incomingConnection();
void socketError(QAbstractSocket::SocketError socketError);
void socketDisconnect();
void socketReadyRead();
protected:

View file

@ -0,0 +1,83 @@
#include <QWebSocketServer>
#include <vector>
#include <QWebSocket>
#include <QJsonArray>
#include "items/item.h"
#include "server.h"
#include "websocketserver.h"
WebSocketServer::WebSocketServer(const QString &serverName, QObject* parent)
: Server(parent),
server(serverName, QWebSocketServer::NonSecureMode, this)
{
connect(&server, &QWebSocketServer::newConnection, this, &WebSocketServer::incomingConnection);
}
WebSocketServer::~WebSocketServer()
{
// Clean up WebSocket clients (they need special handling)
for(auto& client : clients)
{
if(client.socket.webSocket)
{
client.socket.webSocket->close();
delete client.socket.webSocket;
}
}
}
void WebSocketServer::sendJson(const QJsonObject& json)
{
QByteArray jsonData = QJsonDocument(json).toJson();
for(auto& client : clients)
{
if(client.socket.webSocket && client.socket.webSocket->state() == QAbstractSocket::ConnectedState)
{
client.socket.webSocket->sendTextMessage(QString::fromUtf8(jsonData));
}
}
}
void WebSocketServer::processIncomeingJson(const QByteArray& jsonbytes)
{
// Validate JSON first (WebSockets may receive malformed data)
QJsonDocument doc = QJsonDocument::fromJson(jsonbytes);
if(!doc.isObject())
{
qWarning() << "Invalid JSON received:" << QString::fromUtf8(jsonbytes);
return;
}
Server::processIncomeingJson(jsonbytes);
}
bool WebSocketServer::launch(const QHostAddress &address, quint16 port)
{
qInfo()<<"WebSocket server launched on"<<address<<"port"<<port;
return server.listen(address, port);
}
void WebSocketServer::incomingConnection()
{
while(server.hasPendingConnections())
{
QWebSocket* client = server.nextPendingConnection();
qDebug() << "Got new WebSocket client from" << client->peerAddress().toString();
if(client)
{
Client c;
c.socket.webSocket = client;
clients.push_back(c);
connect(client, &QWebSocket::errorOccurred, this, [this, client]() { removeClient(client); });
connect(client, &QWebSocket::disconnected, this, [this, client]() { removeClient(client); });
connect(client, &QWebSocket::textMessageReceived, this, &WebSocketServer::textMessageReceived);
}
}
}
void WebSocketServer::textMessageReceived(const QString &message)
{
QByteArray jsonData = message.toUtf8();
processIncomeingJson(jsonData);
}

View file

@ -0,0 +1,32 @@
#ifndef WEBSOCKETSERVER_SERVER_H
#define WEBSOCKETSERVER_SERVER_H
#include <QWebSocketServer>
#include <vector>
#include "server.h"
class WebSocketServer : public Server
{
Q_OBJECT
QWebSocketServer server;
public:
WebSocketServer(const QString &serverName, QObject* parent = nullptr);
virtual ~WebSocketServer();
virtual bool launch(const QHostAddress &address = QHostAddress::Any, quint16 port = 0) override;
virtual void sendJson(const QJsonObject& json) override;
private slots:
void incomingConnection();
void textMessageReceived(const QString &message);
protected:
virtual void processIncomeingJson(const QByteArray& jsonbytes) override;
signals:
void sigRequestSave();
};
#endif // WEBSOCKETSERVER_SERVER_H

View file

@ -46,11 +46,17 @@
<number>255</number>
</property>
<property name="tracking">
<bool>false</bool>
<bool>true</bool>
</property>
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="invertedAppearance">
<bool>false</bool>
</property>
<property name="invertedControls">
<bool>false</bool>
</property>
</widget>
</item>
<item>

View file

@ -94,7 +94,7 @@
</property>
<property name="minimumSize">
<size>
<width>300</width>
<width>400</width>
<height>0</height>
</size>
</property>

View file

@ -55,6 +55,11 @@ void SensorListWidget::sensorsChanged(std::vector<Sensor> sensors)
itemString.append("\"Playing\"");
else itemString.append("\"Silent\"");
}
else if(!sensors[i].getUnit().isEmpty())
{
itemString.append(" ");
itemString.append(sensors[i].getUnit());
}
setItem(static_cast<int>(row), 0, new SensorListItem(sensors[i].name + (sensors[i].hidden ? " (H)" : ""), sensors[i]));
setItem(static_cast<int>(row), 1, new QTableWidgetItem(itemString));