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/actor.cpp b/src/actors/actor.cpp index 41688d7..51c3c84 100644 --- a/src/actors/actor.cpp +++ b/src/actors/actor.cpp @@ -17,14 +17,22 @@ Actor::~Actor() } -void Actor::performAction() +void Actor::performValueAction(uint8_t value) { if(active) { - sigValue(triggerValue); + ItemUpdateRequest request; + request.type = ITEM_UPDATE_ACTOR; + request.payload = ItemData(QRandomGenerator::global()->generate(), "Item", value); + sigItemUpdate(request); } } +void Actor::performAction() +{ + performValueAction(triggerValue); +} + void Actor::makeActive() { active = true; @@ -86,9 +94,9 @@ uint8_t Actor::getTriggerValue() return triggerValue; } -void Actor::onValueChanged(uint8_t value) +void Actor::onItemUpdated(ItemUpdateRequest update) { - (void)value; + (void) update; } std::shared_ptr Actor::createActor(const QString& type) @@ -112,9 +120,7 @@ std::shared_ptr Actor::loadActor(const QJsonObject &json) return actor; } -void Actor::setValue(uint8_t value) +void Actor::enactValue(uint8_t value) { - Item::setValue(value); setActive(value); } - diff --git a/src/actors/actor.h b/src/actors/actor.h index 7b81bde..e9e7698 100644 --- a/src/actors/actor.h +++ b/src/actors/actor.h @@ -19,18 +19,19 @@ protected: bool exausted = false; void performAction(); + void performValueAction(uint8_t value); + + virtual void enactValue(uint8_t value) override; signals: - void sigValue(uint8_t value); + void sigItemUpdate(ItemUpdateRequest update); public slots: virtual void makeActive(); virtual void makeInactive(); virtual void setActive(uint8_t state); - virtual void onValueChanged(uint8_t state); - - virtual void setValue(uint8_t value); + virtual void onItemUpdated(ItemUpdateRequest update); public: Actor(QObject* parent = nullptr); @@ -46,8 +47,8 @@ public: static std::shared_ptr createActor(const QString& type); - virtual void store(QJsonObject& json); - virtual void load(const QJsonObject& json, const bool preserve = false); + virtual void store(QJsonObject& json) override; + virtual void load(const QJsonObject& json, const bool preserve = false) override; static std::shared_ptr loadActor(const QJsonObject& json); }; diff --git a/src/actors/factoractor.cpp b/src/actors/factoractor.cpp index df4447f..cadd017 100644 --- a/src/actors/factoractor.cpp +++ b/src/actors/factoractor.cpp @@ -6,18 +6,19 @@ MultiFactorActor::MultiFactorActor(Actor* factorActor, const uint preCancleMin, preCancleMin_(preCancleMin) { activationTime.setMSecsSinceEpoch(0); - if(factorActor) connect(factorActor, &Actor::sigValue, this, &MultiFactorActor::factorActorSlot); + if(factorActor) + connect(factorActor, &Actor::sigItemUpdate, this, &MultiFactorActor::factorActorSlot); } -void MultiFactorActor::factorActorSlot(uint8_t value) +void MultiFactorActor::factorActorSlot(ItemUpdateRequest update) { - if(value == factorDirection) + if(update.payload.getValue() == factorDirection) { activationTime = QDateTime::currentDateTime(); } } -void MultiFactorActor::setValue(uint8_t value) +void MultiFactorActor::enactValue(uint8_t value) { if(value) { @@ -46,7 +47,7 @@ QString MultiFactorActor::getName() const void MultiFactorActor::setFactorActor(std::shared_ptr factorActor) { factorActor_=factorActor; - connect(factorActor_.get(), &Actor::sigValue, this, &MultiFactorActor::factorActorSlot); + connect(factorActor_.get(), &Actor::sigItemUpdate, this, &MultiFactorActor::factorActorSlot); } void MultiFactorActor::store(QJsonObject &json) @@ -73,7 +74,7 @@ void MultiFactorActor::load(const QJsonObject &json, bool preserve) } if(factorActor_) { - connect(factorActor_.get(), &Actor::sigValue, this, &MultiFactorActor::factorActorSlot); + connect(factorActor_.get(), &Actor::sigItemUpdate, this, &MultiFactorActor::factorActorSlot); } } diff --git a/src/actors/factoractor.h b/src/actors/factoractor.h index 104a2c4..97a6814 100644 --- a/src/actors/factoractor.h +++ b/src/actors/factoractor.h @@ -16,17 +16,17 @@ private: private slots: - void factorActorSlot(uint8_t value); + void factorActorSlot(ItemUpdateRequest update); public slots: - virtual void setValue(uint8_t value); + virtual void enactValue(uint8_t value) override; public: MultiFactorActor(Actor* FactorActor = nullptr, const uint preCancleMin = 10, QObject *parent = nullptr); - virtual QString getName() const; + virtual QString getName() const override; void setFactorActor(std::shared_ptr factorActor); std::shared_ptr getFactorActor() @@ -52,8 +52,8 @@ public: virtual ~MultiFactorActor() {} - virtual void store(QJsonObject& json); - virtual void load(const QJsonObject& json, bool preserve); + virtual void store(QJsonObject& json) override; + virtual void load(const QJsonObject& json, bool preserve) override; }; #endif // REMINDERACTOR_H diff --git a/src/actors/polynomalactor.cpp b/src/actors/polynomalactor.cpp index 053dc30..cc783fd 100644 --- a/src/actors/polynomalactor.cpp +++ b/src/actors/polynomalactor.cpp @@ -39,7 +39,8 @@ void PolynomalActor::sensorEvent(Sensor sensor) +pow0_; if(result < 0) result = 0; else if(result > 254) result = 255; - if(result != prevValue)sigValue(static_cast(result)); + if(result != prevValue) + performValueAction(static_cast(result)); prevValue = result; } } @@ -65,7 +66,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..94f8bb6 100644 --- a/src/actors/regulator.cpp +++ b/src/actors/regulator.cpp @@ -28,11 +28,11 @@ void Regulator::sensorEvent(Sensor sensor) timer.start(timeout_*1000); if( sensor.field < setPoint_-band_ && (sensor.field < sensor_.field || sensor_.field > setPoint_-band_ || first) ) { - sigValue(triggerValue); + performValueAction(triggerValue); } else if( sensor.field > setPoint_+band_ && (sensor.field > sensor_.field || sensor_.field < setPoint_+band_ || first) ) { - sigValue(!triggerValue); + performValueAction(!triggerValue); } first = false; sensor_ = sensor; @@ -42,15 +42,14 @@ void Regulator::sensorEvent(Sensor sensor) void Regulator::makeInactive() { first = true; - if(active) - sigValue(!triggerValue); + performValueAction(!triggerValue); timer.stop(); Actor::makeInactive(); } void Regulator::timeout() { - sigValue(safeValue_); + performValueAction(safeValue_); } void Regulator::setPoint(float setPoint) @@ -100,7 +99,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/fixeditemsource.cpp b/src/items/fixeditemsource.cpp index d1abc21..b4459ff 100644 --- a/src/items/fixeditemsource.cpp +++ b/src/items/fixeditemsource.cpp @@ -10,5 +10,5 @@ FixedItemSource::FixedItemSource(Microcontroller* micro, QObject *parent): void FixedItemSource::refresh() { - gotItems({powerItem, rgbItem, auxItem}); + gotItems({powerItem, rgbItem, auxItem}, ITEM_UPDATE_BACKEND); } diff --git a/src/items/item.cpp b/src/items/item.cpp index e297094..7f350a2 100644 --- a/src/items/item.cpp +++ b/src/items/item.cpp @@ -34,6 +34,11 @@ uint8_t ItemData::getValue() const return value_; } +void ItemData::setValueData(uint8_t value) +{ + value_ = value; +} + uint32_t ItemData::id() const { return itemId_; @@ -79,7 +84,7 @@ bool ItemData::hasChanged(const ItemData& other) return false; } -bool ItemData::isHidden() +bool ItemData::isHidden() const { return hidden_; } @@ -126,6 +131,7 @@ void Item::store(QJsonObject &json) } } json["Actors"] = actorsArray; + json["ValueType"] = type_; } void Item::load(const QJsonObject &json, const bool preserve) @@ -144,28 +150,46 @@ void Item::load(const QJsonObject &json, const bool preserve) } } -void Item::actorSetValue(uint8_t value) +Item& Item::operator=(const ItemData& other) { - if(!override_ && (programMode == PROGRAM_MODE_PRIMARY || programMode == PROGRAM_MODE_HEADLESS_PRIMARY)) - setValue(value); + name_ = other.getName(); + value_ = other.getValue(); + itemId_ = other.id(); + hidden_ = other.isHidden(); + return *this; } -void Item::setValue(uint8_t value) +void Item::requestUpdate(ItemUpdateRequest update) { - qDebug()<<__func__; - informValue(value); - if(programMode == PROGRAM_MODE_PRIMARY || programMode == PROGRAM_MODE_HEADLESS_PRIMARY) - enactValue(value); -} + assert(update.type != ITEM_UPDATE_INVALID); + if(update.type != ITEM_UPDATE_LOADED && value_ == update.payload.getValue()) + return; -void Item::informValue(uint8_t value) -{ - if(value_ != value) + if(update.type == ITEM_UPDATE_ACTOR && override_) + return; + + qDebug()<<"Item Update Request for"<& actor : update.newActors) + addActor(actor); + } + update.payload = *this; + updated(update); } void Item::enactValue(uint8_t value) @@ -178,8 +202,8 @@ void Item::addActor(std::shared_ptr actor) actor->setParent(this); actors_.push_back(actor); if(programMode == PROGRAM_MODE_PRIMARY || programMode == PROGRAM_MODE_HEADLESS_PRIMARY) - connect(actor.get(), &Actor::sigValue, this, &Item::actorSetValue); - connect(this, &Item::valueChanged, actor.get(), &Actor::onValueChanged); + connect(actor.get(), &Actor::sigItemUpdate, this, &Item::requestUpdate); + connect(this, &Item::updated, actor.get(), &Actor::onItemUpdated); std::shared_ptr sensorActor = std::dynamic_pointer_cast(actor); if(sensorActor) @@ -238,41 +262,25 @@ void Item::setActorsActive(bool in) in ? actors_[i]->makeActive() : actors_[i]->makeInactive(); } -void Item::mergeLoaded(Item& item) -{ - name_ = item.name_; - actors_.clear(); - for(std::shared_ptr actor : item.actors_) - addActor(actor); -} - 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); @@ -281,3 +289,15 @@ std::shared_ptr Item::loadItem(const QJsonObject& json) return newItem; } +ItemUpdateRequest Item::createValueUpdateRequest(uint8_t value, + item_update_type_t type, + bool withActors) +{ + ItemUpdateRequest update; + update.type = type; + update.payload = *this; + update.payload.setValueData(value); + if(withActors) + update.newActors = actors_; + return update; +} diff --git a/src/items/item.h b/src/items/item.h index 1aaa090..d461006 100644 --- a/src/items/item.h +++ b/src/items/item.h @@ -14,6 +14,15 @@ typedef enum { ITEM_VALUE_NO_VALUE } item_value_type_t; +typedef enum { + ITEM_UPDATE_USER = 0, + ITEM_UPDATE_ACTOR, + ITEM_UPDATE_REMOTE, + ITEM_UPDATE_LOADED, + ITEM_UPDATE_BACKEND, + ITEM_UPDATE_INVALID +} item_update_type_t; + class ItemData { protected: @@ -46,9 +55,10 @@ public: bool hasChanged(const ItemData& other); void setName(QString name); uint8_t getValue() const; + void setValueData(uint8_t value); 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 +66,26 @@ public: virtual void load(const QJsonObject& json, const bool preserve = false); }; +struct ItemUpdateRequest +{ + item_update_type_t type = ITEM_UPDATE_INVALID; + ItemData payload; + std::vector > newActors; +}; + 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); + virtual void requestUpdate(ItemUpdateRequest update); public: @@ -83,6 +95,7 @@ public: virtual ~Item(); + Item& operator=(const ItemData& other); std::vector< std::shared_ptr >& getActors(); bool hasActors(); void addActor(std::shared_ptr actor); @@ -91,8 +104,9 @@ public: void setActorsActive(bool in); void setOverride(const bool in); bool getOverride(); - void informValue(uint8_t value); - void mergeLoaded(Item& item); + ItemUpdateRequest createValueUpdateRequest(uint8_t value, + item_update_type_t type, + bool withActors = false); virtual void store(QJsonObject& json); virtual void load(const QJsonObject& json, const bool preserve = false); diff --git a/src/items/itemloadersource.cpp b/src/items/itemloadersource.cpp index d5e6afa..a24e5a5 100644 --- a/src/items/itemloadersource.cpp +++ b/src/items/itemloadersource.cpp @@ -25,7 +25,7 @@ void ItemLoaderSource::refresh() qDebug()<<"Loaded item"<getName(); } } - gotItems(items); + gotItems(items, ITEM_UPDATE_LOADED); } void ItemLoaderSource::updateJson(const QJsonObject& json) diff --git a/src/items/itemsource.h b/src/items/itemsource.h index 9fee9c1..5c2bba2 100644 --- a/src/items/itemsource.h +++ b/src/items/itemsource.h @@ -17,9 +17,9 @@ public slots: virtual void refresh() = 0; signals: - void gotItems(std::vector> items, bool inform = true); + void gotItems(std::vector> items, item_update_type_t updateType); void requestReplaceItems(std::vector> items); - void updateItems(std::vector items, bool inform = true); + void updateItems(std::vector updates); }; #endif // ITEMSOURCE_H diff --git a/src/items/itemstore.cpp b/src/items/itemstore.cpp index 1242afc..232d319 100644 --- a/src/items/itemstore.cpp +++ b/src/items/itemstore.cpp @@ -6,7 +6,7 @@ ItemStore::ItemStore(QObject *parent): QObject(parent) { } -void ItemStore::addItem(std::shared_ptr item, bool inform) +void ItemStore::addItem(const std::shared_ptr& item, item_update_type_t updateType) { std::shared_ptr matched = nullptr; for(unsigned i = 0; i < items_.size(); i++ ) @@ -26,17 +26,19 @@ void ItemStore::addItem(std::shared_ptr item, bool inform) } else { - if(item->getLoaded()) - matched->mergeLoaded(*item); - else if(item->getValue() != matched->getValue()) - updateItem(*item, inform); + ItemUpdateRequest request = item->createValueUpdateRequest(item->getValue(), + updateType, + updateType == ITEM_UPDATE_LOADED); + request.newActors = item->getActors(); + updateItem(request); } } -void ItemStore::addItems(const std::vector>& itemIn, bool inform) +void ItemStore::addItems(const std::vector>& itemIn, + item_update_type_t updateType) { for(unsigned j = 0; j < itemIn.size(); j++) - addItem(itemIn[j], inform); + addItem(itemIn[j], updateType); } void ItemStore::removeItem(const ItemData& item) @@ -55,7 +57,8 @@ void ItemStore::removeItem(const ItemData& item) void ItemStore::replaceItems(const std::vector>& items) { - addItems(items, true); + qDebug()<<__func__; + addItems(items, ITEM_UPDATE_LOADED); std::vector deletedItems; for(std::shared_ptr item : items_) { @@ -72,32 +75,20 @@ void ItemStore::clear() items_.clear(); } - -void ItemStore::updateItems(std::vector items, bool inform) +void ItemStore::updateItems(const std::vector& updates) { - for(const ItemData& item : items) - updateItem(item, inform); + for(const ItemUpdateRequest& update : updates) + updateItem(update); } -void ItemStore::updateItem(const ItemData& item, bool inform) +void ItemStore::updateItem(const ItemUpdateRequest& update) { for(unsigned i = 0; i < items_.size(); i++ ) { - if(items_[i]->operator==(item)) + if(items_[i]->operator==(update.payload)) { - if(items_[i]->hasChanged(item)) - { - if(items_[i]->getValue() != item.getValue()) - { - items_[i]->setLoaded(false); - if(inform) - items_[i]->informValue(item.getValue()); - else - items_[i]->setValue(item.getValue()); - } - qDebug()<<"Item"<getName()<<"updated"; - itemUpdated(items_[i]); - } + items_[i]->requestUpdate(update); + itemUpdated(update); } } } @@ -114,13 +105,9 @@ void ItemStore::store(QJsonObject& json) json["Items"] = itemsArray; } -void ItemStore::itemUpdateSlot(ItemData data) +void ItemStore::itemUpdateSlot(ItemUpdateRequest update) { - for(std::shared_ptr& item: items_) - { - if(*item == data) - itemUpdated(std::weak_ptr(item)); - } + itemUpdated(update); } std::shared_ptr ItemStore::getItem(uint32_t id) diff --git a/src/items/itemstore.h b/src/items/itemstore.h index 830cc40..b4bdeaa 100644 --- a/src/items/itemstore.h +++ b/src/items/itemstore.h @@ -33,21 +33,21 @@ signals: void itemDeleted(ItemData item); void itemAdded(std::weak_ptr Item); - void itemUpdated(std::weak_ptr Item); + void itemUpdated(ItemUpdateRequest update); void sigRefresh(); public slots: void removeItem(const ItemData& item); - void addItem(std::shared_ptr item, bool inform = true); - void addItems(const std::vector>& itemsIn, bool inform = true); + void addItem(const std::shared_ptr& item, item_update_type_t updateType); + void addItems(const std::vector>& itemsIn, item_update_type_t updateType); void replaceItems(const std::vector>& items); - void updateItems(std::vector items, bool inform = true); - void updateItem(const ItemData& item, bool inform = true); + void updateItems(const std::vector& updates); + void updateItem(const ItemUpdateRequest& update); void refresh(); private slots: - void itemUpdateSlot(ItemData data); + void itemUpdateSlot(ItemUpdateRequest update); }; extern ItemStore globalItems; diff --git a/src/items/poweritem.cpp b/src/items/poweritem.cpp index 2b2e4e1..aec7642 100644 --- a/src/items/poweritem.cpp +++ b/src/items/poweritem.cpp @@ -7,7 +7,7 @@ PowerItem::PowerItem(uint32_t itemIdIn, QString name, uint8_t value, QObject* p Item(itemIdIn, name, value, parent) { stateChanged(Sensor(Sensor::TYPE_SHUTDOWN_IMMINENT, 0, 0, "Shutdown Imminent", true)); - PowerItem::setValue(true); + value_ = true; hidden_ = true; type_ = ITEM_VALUE_NO_VALUE; } diff --git a/src/items/relay.cpp b/src/items/relay.cpp index f801e1b..17f4cb9 100644 --- a/src/items/relay.cpp +++ b/src/items/relay.cpp @@ -23,21 +23,6 @@ void Relay::enactValue(uint8_t value) } } -void Relay::on() -{ - setValue(true); -} - -void Relay::off() -{ - setValue(false); -} - -void Relay::toggle() -{ - value_ ? off() : on(); -} - void Relay::store(QJsonObject& json) { json["Type"] = "Relay"; diff --git a/src/items/relay.h b/src/items/relay.h index 3c3c4b4..946fa87 100644 --- a/src/items/relay.h +++ b/src/items/relay.h @@ -4,7 +4,6 @@ #include #include -#include "sensors/sensor.h" #include "item.h" class Microcontroller; @@ -21,11 +20,6 @@ private: protected: virtual void enactValue(uint8_t value) override; -public slots: - void on(); - void off(); - void toggle(); - public: Relay(uint8_t id = 0, QString name = "", uint16_t address = 0, bool state = false, QObject* parent = nullptr); diff --git a/src/main.cpp b/src/main.cpp index 8e5ca4c..e3a6e03 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"); @@ -115,7 +115,12 @@ int main(int argc, char *argv[]) QObject::connect(&mainObject.micro, SIGNAL(textRecived(QString)), w, SLOT(changeHeaderLableText(QString))); QObject::connect(w, &MainWindow::sigSetRgb, &mainObject.micro, &Microcontroller::changeRgbColor); QObject::connect(w, &MainWindow::sigSave, &mainObject, [&mainObject, settingsPath](){mainObject.storeToDisk(settingsPath);}); - QObject::connect(w, &MainWindow::createdItem, &globalItems, [](std::shared_ptr item){globalItems.addItem(item, false);}); + QObject::connect(w, + &MainWindow::createdItem, + &globalItems, + [](std::shared_ptr item) { + globalItems.addItem(item, ITEM_UPDATE_USER); + }); w->show(); } retVal = a.exec(); @@ -127,7 +132,9 @@ int main(int argc, char *argv[]) { SecondaryMainObject mainObject(parser.value(hostOption), parser.value(portOption).toInt()); MainWindow w(&mainObject); - QObject::connect(&w, &MainWindow::createdItem, &globalItems, [](std::shared_ptr item){globalItems.addItem(item, false);}); + QObject::connect(&w, &MainWindow::createdItem, &globalItems, [](std::shared_ptr item) { + globalItems.addItem(item, ITEM_UPDATE_USER); + }); QObject::connect(&w, &MainWindow::sigSave, mainObject.tcpClient, &TcpClient::sendItems); w.show(); 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/microcontroller.cpp b/src/microcontroller.cpp index 4833461..0bf8690 100644 --- a/src/microcontroller.cpp +++ b/src/microcontroller.cpp @@ -1,8 +1,5 @@ #include "microcontroller.h" -#include -#include - void Microcontroller::relayToggle(int state, int relay) { char buffer[8]; @@ -49,22 +46,34 @@ void Microcontroller::setAuxPwm(int duty) void Microcontroller::write(const QByteArray& buffer) { - if(_port != nullptr) + writeQue.enqueue(buffer); + if(!writeTimer.isActive()) { - _port->write(buffer); - _port->waitForBytesWritten(1000); + writeTimer.setInterval(0); + writeTimer.start(); } - std::this_thread::sleep_for(std::chrono::milliseconds(40)); } void Microcontroller::write(char* buffer, const size_t length) { - if(_port != nullptr) + write(QByteArray(buffer, length)); +} + +void Microcontroller::onWriteTimerTimeout() +{ + writeTimer.setInterval(50); + if(connected()) { - _port->write(buffer, length); - _port->waitForBytesWritten(1000); + if(!writeQue.empty()) + { + QByteArray data = writeQue.dequeue(); + _port->write(data); + } + else + { + writeTimer.stop(); + } } - std::this_thread::sleep_for(std::chrono::milliseconds(40)); } void Microcontroller::setPattern(int pattern) @@ -82,8 +91,10 @@ void Microcontroller::startSunrise() bool Microcontroller::connected() { - if(_port != nullptr) return _port->isOpen(); - else return false; + if(_port != nullptr) + return _port->isOpen(); + else + return false; } void Microcontroller::refresh() @@ -93,13 +104,17 @@ void Microcontroller::refresh() //housekeeping -Microcontroller::Microcontroller(QIODevice* port) +Microcontroller::Microcontroller(QIODevice* port): Microcontroller() { setIODevice(port); } Microcontroller::Microcontroller() { + writeTimer.setInterval(50); + writeTimer.setSingleShot(false); + connect(&writeTimer, &QTimer::timeout, this, &Microcontroller::onWriteTimerTimeout); + qDebug()<<__func__<(*processRelayLine(buffer))}); + ItemUpdateRequest update; + update.type = ITEM_UPDATE_BACKEND; + update.payload = static_cast(*processRelayLine(buffer)); + updateItems({update}); } void Microcontroller::processSensorState(const QString& buffer) diff --git a/src/microcontroller.h b/src/microcontroller.h index 1845dc0..c6d56bf 100644 --- a/src/microcontroller.h +++ b/src/microcontroller.h @@ -7,9 +7,9 @@ #include #include #include -#include #include -#include +#include +#include #include #include #include "items/item.h" @@ -32,13 +32,12 @@ private: bool listMode = false; - //uint8_t _auxState = 0; - QIODevice* _port = nullptr; + QQueue writeQue; + QTimer writeTimer; std::vector< std::shared_ptr > relayList; - QScopedPointer loop; QString _buffer; void processMicroReturn(); @@ -75,6 +74,7 @@ public slots: private slots: void isReadyRead(); + void onWriteTimerTimeout(); signals: void textRecived(const QString string); 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..8d900a2 --- /dev/null +++ b/src/service/server.cpp @@ -0,0 +1,104 @@ +#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, ITEM_UPDATE_REMOTE); + } + } + 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; + } + } +} + +void Server::itemUpdated(ItemUpdateRequest update) +{ + QJsonArray items; + QJsonObject itemjson; + update.payload.store(itemjson); + items.append(itemjson); + QJsonObject json = createMessage("ItemUpdate", items); + json["FullList"] = false; + sendJson(json); +} \ No newline at end of file diff --git a/src/service/server.h b/src/service/server.h new file mode 100644 index 0000000..5564108 --- /dev/null +++ b/src/service/server.h @@ -0,0 +1,52 @@ +#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 slots: + virtual void itemUpdated(ItemUpdateRequest update) override; + +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..ae75b7e 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" @@ -30,17 +29,7 @@ void Service::sensorEvent(Sensor sensor) sendJson(json); } -void Service::itemUpdated(std::weak_ptr 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::itemUpdated(ItemUpdateRequest update) {} void Service::refresh() { @@ -78,7 +67,6 @@ void Service::sendItems() void Service::processIncomeingJson(const QByteArray& jsonbytes) { QJsonDocument doc = QJsonDocument::fromJson(jsonbytes); - qDebug()<<"Got Json:"< item); + virtual void itemUpdated(ItemUpdateRequest update); virtual void refresh() override; public: diff --git a/src/service/tcpclient.cpp b/src/service/tcpclient.cpp index d4d8dd8..63729d1 100644 --- a/src/service/tcpclient.cpp +++ b/src/service/tcpclient.cpp @@ -33,7 +33,6 @@ void TcpClient::processIncomeingJson(const QByteArray& jsonbytes) QString type = json["MessageType"].toString(); if(type == "ItemUpdate") { - std::cout<<"Got item json:\n"<> items; for(QJsonValueRef itemjson : data) @@ -42,12 +41,13 @@ 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); } } if(!items.empty()) - gotItems(items, true); + gotItems(items, ITEM_UPDATE_REMOTE); } else { @@ -94,6 +94,20 @@ void TcpClient::socketReadyRead() } } +void TcpClient::itemUpdated(ItemUpdateRequest update) +{ + if(update.type == ITEM_UPDATE_USER) + { + QJsonArray items; + QJsonObject itemjson; + update.payload.store(itemjson); + items.append(itemjson); + QJsonObject json = createMessage("ItemUpdate", items); + json["FullList"] = false; + sendJson(json); + } +} + TcpClient::~TcpClient() { delete socket; diff --git a/src/service/tcpclient.h b/src/service/tcpclient.h index df28641..b67f7b4 100644 --- a/src/service/tcpclient.h +++ b/src/service/tcpclient.h @@ -16,6 +16,9 @@ class TcpClient : public Service long long recievebytes = 0; QByteArray buffer; +public slots: + virtual void itemUpdated(ItemUpdateRequest update) override; + public: TcpClient(QObject* parent = nullptr); ~TcpClient(); 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.cpp b/src/ui/itemwidget.cpp index d073e50..c91c5e8 100644 --- a/src/ui/itemwidget.cpp +++ b/src/ui/itemwidget.cpp @@ -38,7 +38,7 @@ ItemWidget::ItemWidget(std::weak_ptr item, QWidget *parent) : else connect(ui->checkBox, &QCheckBox::toggled, this, &ItemWidget::moveToState); connect(ui->pushButton, &QPushButton::clicked, this, &ItemWidget::showSettingsDialog); - connect(workingItem.get(), &Relay::valueChanged, this, &ItemWidget::stateChanged); + connect(workingItem.get(), &Item::updated, this, &ItemWidget::onItemUpdated); connect(ui->pushButton_Remove, &QPushButton::clicked, this, &ItemWidget::deleteItem); } @@ -59,17 +59,27 @@ void ItemWidget::deleteItem() void ItemWidget::moveToValue(int value) { if(auto workingItem = item_.lock()) - workingItem->setValue(value); + { + ItemUpdateRequest request = workingItem->createValueUpdateRequest(value, ITEM_UPDATE_USER); + workingItem->requestUpdate(request); + } else + { disable(); + } } void ItemWidget::moveToState(bool state) { if(auto workingItem = item_.lock()) - workingItem->setValue(state); + { + ItemUpdateRequest request = workingItem->createValueUpdateRequest(state, ITEM_UPDATE_USER); + workingItem->requestUpdate(request); + } else + { disable(); + } } void ItemWidget::disable() @@ -105,6 +115,11 @@ std::weak_ptr ItemWidget::getItem() return item_; } +void ItemWidget::onItemUpdated(ItemUpdateRequest update) +{ + stateChanged(update.payload.getValue()); +} + void ItemWidget::stateChanged(int state) { ui->slider->blockSignals(true); diff --git a/src/ui/itemwidget.h b/src/ui/itemwidget.h index 18d2861..90d5a97 100644 --- a/src/ui/itemwidget.h +++ b/src/ui/itemwidget.h @@ -38,6 +38,7 @@ public: public slots: void stateChanged(int state); + void onItemUpdated(ItemUpdateRequest update); private: Ui::ItemWidget *ui; 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));