diff --git a/CMakeLists.txt b/CMakeLists.txt index 924e5f0..ea99bbe 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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} ) diff --git a/UVOSicon.bmp b/UVOSicon.bmp index 9565919..fd0d9a2 100644 Binary files a/UVOSicon.bmp and b/UVOSicon.bmp differ diff --git a/src/actors/polynomalactor.cpp b/src/actors/polynomalactor.cpp index 053dc30..f846c0e 100644 --- a/src/actors/polynomalactor.cpp +++ b/src/actors/polynomalactor.cpp @@ -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(json["SensorType"].toInt(0)); sensor_.id = json["SensorId"].toInt(0); sensor_.field = json["SensorField"].toInt(0); sensor_.name = json["SensorName"].toString("Sensor"); diff --git a/src/actors/regulator.cpp b/src/actors/regulator.cpp index b3ea5f6..2287b7b 100644 --- a/src/actors/regulator.cpp +++ b/src/actors/regulator.cpp @@ -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(json["SensorType"].toInt(0)); sensor_.id = json["SensorId"].toInt(0); sensor_.field = json["SensorField"].toInt(0); sensor_.name = json["SensorName"].toString("Sensor"); diff --git a/src/actors/sensoractor.cpp b/src/actors/sensoractor.cpp index 0be3ffa..758b761 100644 --- a/src/actors/sensoractor.cpp +++ b/src/actors/sensoractor.cpp @@ -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(json["SensorType"].toInt(0)); sensor_.id = json["SensorId"].toInt(0); sensor_.field = json["SensorField"].toInt(0); sensor_.name = json["SensorName"].toString("Sensor"); diff --git a/src/items/item.cpp b/src/items/item.cpp index e297094..53790ae 100644 --- a/src/items/item.cpp +++ b/src/items/item.cpp @@ -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::loadItem(const QJsonObject& json) { std::shared_ptr newItem = nullptr; - if(json["Type"].toString("") == "Relay") - { + if(json["Type"].toString("Item") == "Item") + newItem = std::shared_ptr(new Item); + else if(json["Type"].toString("") == "Relay") newItem = std::shared_ptr(new Relay); - } else if(json["Type"].toString("") == "Message") - { newItem = std::shared_ptr(new MessageItem); - } else if(json["Type"].toString("") == "System") - { newItem = std::shared_ptr(new SystemItem); - } else if(json["Type"].toString("") == "Aux") - { newItem = std::shared_ptr(new AuxItem); - } else if(json["Type"].toString("") == "Power") - { newItem = std::shared_ptr(new PowerItem); - } else if(json["Type"].toString("") == "Rgb") - { newItem = std::shared_ptr(new RgbItem); - } + else + qWarning()<<"Unable to load unkown item type: "<load(json); diff --git a/src/items/item.h b/src/items/item.h index 1aaa090..4fbf23f 100644 --- a/src/items/item.h +++ b/src/items/item.h @@ -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 > 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 >& getActors(); bool hasActors(); void addActor(std::shared_ptr 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); diff --git a/src/items/itemstore.cpp b/src/items/itemstore.cpp index 1242afc..e601aee 100644 --- a/src/items/itemstore.cpp +++ b/src/items/itemstore.cpp @@ -95,8 +95,9 @@ void ItemStore::updateItem(const ItemData& item, bool inform) else items_[i]->setValue(item.getValue()); } - qDebug()<<"Item"<getName()<<"updated"; - itemUpdated(items_[i]); + qDebug()<<"Item"<getName()<<"updated"<<(inform ? "with inform" : ""); + if(!inform) + itemUpdated(items_[i]); } } } @@ -119,7 +120,10 @@ void ItemStore::itemUpdateSlot(ItemData data) for(std::shared_ptr& item: items_) { if(*item == data) + { + qDebug()<<"Item"<(item)); + } } } diff --git a/src/main.cpp b/src/main.cpp index 8e5ca4c..75c8e3d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -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"); diff --git a/src/mainobject.cpp b/src/mainobject.cpp index 0f41d19..eed22d7 100644 --- a/src/mainobject.cpp +++ b/src/mainobject.cpp @@ -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(µ) { //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(µ, &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(µ); 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() diff --git a/src/mainobject.h b/src/mainobject.h index 7f582d8..48835dc 100644 --- a/src/mainobject.h +++ b/src/mainobject.h @@ -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; diff --git a/src/sensors/sensor.cpp b/src/sensors/sensor.cpp index e2a262f..576d1db 100644 --- a/src/sensors/sensor.cpp +++ b/src/sensors/sensor.cpp @@ -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) diff --git a/src/sensors/sensor.h b/src/sensors/sensor.h index bc5e748..86fa5cb 100644 --- a/src/sensors/sensor.h +++ b/src/sensors/sensor.h @@ -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(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(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 diff --git a/src/service/server.cpp b/src/service/server.cpp new file mode 100644 index 0000000..e01ba4c --- /dev/null +++ b/src/service/server.cpp @@ -0,0 +1,93 @@ +#include +#include +#include + +#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> items; + for(QJsonValueRef itemjson : data) + { + QJsonObject jsonobject = itemjson.toObject(); + std::shared_ptr item = Item::loadItem(jsonobject); + if(item) + { + qDebug()<<"Server got 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(obj)); + } +} + +void Server::handleSocketDisconnect() +{ + QObject* obj = sender(); + if (obj) { + removeClient(static_cast(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; + } + } +} diff --git a/src/service/server.h b/src/service/server.h new file mode 100644 index 0000000..6c27062 --- /dev/null +++ b/src/service/server.h @@ -0,0 +1,49 @@ +#ifndef SERVER_BASE_H +#define SERVER_BASE_H + +#include +#include +#include + +#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 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 diff --git a/src/service/service.cpp b/src/service/service.cpp index 5129512..eb27ee0 100644 --- a/src/service/service.cpp +++ b/src/service/service.cpp @@ -1,7 +1,6 @@ #include #include #include -#include #include "items/item.h" #include "items/itemstore.h" @@ -32,7 +31,7 @@ void Service::sensorEvent(Sensor sensor) void Service::itemUpdated(std::weak_ptr item) { - qDebug()<<__func__; + qDebug()<<"Service sending item"<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:"<> items; for(QJsonValueRef itemjson : data) @@ -42,6 +41,7 @@ void TcpClient::processIncomeingJson(const QByteArray& jsonbytes) std::shared_ptr item = Item::loadItem(jsonobject); if(item) { + qDebug()<<"Client got item"<getName(); item->setLoaded(false); items.push_back(item); } diff --git a/src/service/tcpserver.cpp b/src/service/tcpserver.cpp index b5b780f..ed1b620 100644 --- a/src/service/tcpserver.cpp +++ b/src/service/tcpserver.cpp @@ -4,11 +4,11 @@ #include #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) { - QByteArray jsonData = QJsonDocument(json).toJson(); - client.socket->write(QString("MSG JSON LEN " + QString::number(jsonData.size()) + "\n").toLatin1() + jsonData); + if(client.socket.tcpSocket) { + QByteArray jsonData = QJsonDocument(json).toJson(); + 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> items; - for(QJsonValueRef itemjson : data) - { - QJsonObject jsonobject = itemjson.toObject(); - std::shared_ptr 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"<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()< #include -#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 clients; QTcpServer server; public: @@ -31,8 +22,6 @@ signals: private slots: void incomingConnection(); - void socketError(QAbstractSocket::SocketError socketError); - void socketDisconnect(); void socketReadyRead(); protected: diff --git a/src/service/websocketserver.cpp b/src/service/websocketserver.cpp new file mode 100644 index 0000000..2a34bc1 --- /dev/null +++ b/src/service/websocketserver.cpp @@ -0,0 +1,83 @@ +#include +#include +#include +#include + +#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"<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); +} diff --git a/src/service/websocketserver.h b/src/service/websocketserver.h new file mode 100644 index 0000000..73d74de --- /dev/null +++ b/src/service/websocketserver.h @@ -0,0 +1,32 @@ +#ifndef WEBSOCKETSERVER_SERVER_H +#define WEBSOCKETSERVER_SERVER_H + +#include +#include + +#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 diff --git a/src/ui/itemwidget.ui b/src/ui/itemwidget.ui index 91d92f5..96b625e 100644 --- a/src/ui/itemwidget.ui +++ b/src/ui/itemwidget.ui @@ -46,11 +46,17 @@ 255 - false + true Qt::Orientation::Horizontal + + false + + + false + diff --git a/src/ui/mainwindow.ui b/src/ui/mainwindow.ui index 0e6732c..baa31c8 100644 --- a/src/ui/mainwindow.ui +++ b/src/ui/mainwindow.ui @@ -94,7 +94,7 @@ - 300 + 400 0 diff --git a/src/ui/sensorlistwidget.cpp b/src/ui/sensorlistwidget.cpp index 827ab2e..b732362 100644 --- a/src/ui/sensorlistwidget.cpp +++ b/src/ui/sensorlistwidget.cpp @@ -55,6 +55,11 @@ void SensorListWidget::sensorsChanged(std::vector sensors) itemString.append("\"Playing\""); else itemString.append("\"Silent\""); } + else if(!sensors[i].getUnit().isEmpty()) + { + itemString.append(" "); + itemString.append(sensors[i].getUnit()); + } setItem(static_cast(row), 0, new SensorListItem(sensors[i].name + (sensors[i].hidden ? " (H)" : ""), sensors[i])); setItem(static_cast(row), 1, new QTableWidgetItem(itemString));