Compare commits

..

8 commits

41 changed files with 616 additions and 297 deletions

View file

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

@ -17,14 +17,22 @@ Actor::~Actor()
} }
void Actor::performAction() void Actor::performValueAction(uint8_t value)
{ {
if(active) 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() void Actor::makeActive()
{ {
active = true; active = true;
@ -86,9 +94,9 @@ uint8_t Actor::getTriggerValue()
return triggerValue; return triggerValue;
} }
void Actor::onValueChanged(uint8_t value) void Actor::onItemUpdated(ItemUpdateRequest update)
{ {
(void)value; (void) update;
} }
std::shared_ptr<Actor> Actor::createActor(const QString& type) std::shared_ptr<Actor> Actor::createActor(const QString& type)
@ -112,9 +120,7 @@ std::shared_ptr<Actor> Actor::loadActor(const QJsonObject &json)
return actor; return actor;
} }
void Actor::setValue(uint8_t value) void Actor::enactValue(uint8_t value)
{ {
Item::setValue(value);
setActive(value); setActive(value);
} }

View file

@ -19,18 +19,19 @@ protected:
bool exausted = false; bool exausted = false;
void performAction(); void performAction();
void performValueAction(uint8_t value);
virtual void enactValue(uint8_t value) override;
signals: signals:
void sigValue(uint8_t value); void sigItemUpdate(ItemUpdateRequest update);
public slots: public slots:
virtual void makeActive(); virtual void makeActive();
virtual void makeInactive(); virtual void makeInactive();
virtual void setActive(uint8_t state); virtual void setActive(uint8_t state);
virtual void onValueChanged(uint8_t state); virtual void onItemUpdated(ItemUpdateRequest update);
virtual void setValue(uint8_t value);
public: public:
Actor(QObject* parent = nullptr); Actor(QObject* parent = nullptr);
@ -46,8 +47,8 @@ public:
static std::shared_ptr<Actor> createActor(const QString& type); static std::shared_ptr<Actor> createActor(const QString& type);
virtual void store(QJsonObject& json); virtual void store(QJsonObject& json) override;
virtual void load(const QJsonObject& json, const bool preserve = false); virtual void load(const QJsonObject& json, const bool preserve = false) override;
static std::shared_ptr<Actor> loadActor(const QJsonObject& json); static std::shared_ptr<Actor> loadActor(const QJsonObject& json);
}; };

View file

@ -6,18 +6,19 @@ MultiFactorActor::MultiFactorActor(Actor* factorActor, const uint preCancleMin,
preCancleMin_(preCancleMin) preCancleMin_(preCancleMin)
{ {
activationTime.setMSecsSinceEpoch(0); 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(); activationTime = QDateTime::currentDateTime();
} }
} }
void MultiFactorActor::setValue(uint8_t value) void MultiFactorActor::enactValue(uint8_t value)
{ {
if(value) if(value)
{ {
@ -46,7 +47,7 @@ QString MultiFactorActor::getName() const
void MultiFactorActor::setFactorActor(std::shared_ptr<Actor> factorActor) void MultiFactorActor::setFactorActor(std::shared_ptr<Actor> factorActor)
{ {
factorActor_=factorActor; factorActor_=factorActor;
connect(factorActor_.get(), &Actor::sigValue, this, &MultiFactorActor::factorActorSlot); connect(factorActor_.get(), &Actor::sigItemUpdate, this, &MultiFactorActor::factorActorSlot);
} }
void MultiFactorActor::store(QJsonObject &json) void MultiFactorActor::store(QJsonObject &json)
@ -73,7 +74,7 @@ void MultiFactorActor::load(const QJsonObject &json, bool preserve)
} }
if(factorActor_) if(factorActor_)
{ {
connect(factorActor_.get(), &Actor::sigValue, this, &MultiFactorActor::factorActorSlot); connect(factorActor_.get(), &Actor::sigItemUpdate, this, &MultiFactorActor::factorActorSlot);
} }
} }

View file

@ -16,17 +16,17 @@ private:
private slots: private slots:
void factorActorSlot(uint8_t value); void factorActorSlot(ItemUpdateRequest update);
public slots: public slots:
virtual void setValue(uint8_t value); virtual void enactValue(uint8_t value) override;
public: public:
MultiFactorActor(Actor* FactorActor = nullptr, const uint preCancleMin = 10, QObject *parent = nullptr); 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<Actor> factorActor); void setFactorActor(std::shared_ptr<Actor> factorActor);
std::shared_ptr<Actor> getFactorActor() std::shared_ptr<Actor> getFactorActor()
@ -52,8 +52,8 @@ public:
virtual ~MultiFactorActor() {} virtual ~MultiFactorActor() {}
virtual void store(QJsonObject& json); virtual void store(QJsonObject& json) override;
virtual void load(const QJsonObject& json, bool preserve); virtual void load(const QJsonObject& json, bool preserve) override;
}; };
#endif // REMINDERACTOR_H #endif // REMINDERACTOR_H

View file

@ -39,7 +39,8 @@ void PolynomalActor::sensorEvent(Sensor sensor)
+pow0_; +pow0_;
if(result < 0) result = 0; if(result < 0) result = 0;
else if(result > 254) result = 255; else if(result > 254) result = 255;
if(result != prevValue)sigValue(static_cast<uint8_t>(result)); if(result != prevValue)
performValueAction(static_cast<uint8_t>(result));
prevValue = result; prevValue = result;
} }
} }
@ -65,7 +66,7 @@ void PolynomalActor::load(const QJsonObject& json, bool preserve)
pow2_ = json["Pow2"].toDouble(0); pow2_ = json["Pow2"].toDouble(0);
pow1_ = json["Pow1"].toDouble(1); pow1_ = json["Pow1"].toDouble(1);
pow0_ = json["Pow0"].toDouble(0); 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_.id = json["SensorId"].toInt(0);
sensor_.field = json["SensorField"].toInt(0); sensor_.field = json["SensorField"].toInt(0);
sensor_.name = json["SensorName"].toString("Sensor"); sensor_.name = json["SensorName"].toString("Sensor");

View file

@ -28,11 +28,11 @@ void Regulator::sensorEvent(Sensor sensor)
timer.start(timeout_*1000); timer.start(timeout_*1000);
if( sensor.field < setPoint_-band_ && (sensor.field < sensor_.field || sensor_.field > setPoint_-band_ || first) ) 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) ) else if( sensor.field > setPoint_+band_ && (sensor.field > sensor_.field || sensor_.field < setPoint_+band_ || first) )
{ {
sigValue(!triggerValue); performValueAction(!triggerValue);
} }
first = false; first = false;
sensor_ = sensor; sensor_ = sensor;
@ -42,15 +42,14 @@ void Regulator::sensorEvent(Sensor sensor)
void Regulator::makeInactive() void Regulator::makeInactive()
{ {
first = true; first = true;
if(active) performValueAction(!triggerValue);
sigValue(!triggerValue);
timer.stop(); timer.stop();
Actor::makeInactive(); Actor::makeInactive();
} }
void Regulator::timeout() void Regulator::timeout()
{ {
sigValue(safeValue_); performValueAction(safeValue_);
} }
void Regulator::setPoint(float setPoint) void Regulator::setPoint(float setPoint)
@ -100,7 +99,7 @@ void Regulator::load(const QJsonObject& json, bool preserve)
setPoint_ = json["SetPoint"].toDouble(22); setPoint_ = json["SetPoint"].toDouble(22);
safeValue_ = json["SafeValue"].toDouble(0); safeValue_ = json["SafeValue"].toDouble(0);
timeout_ = json["Timeout"].toDouble(1800); 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_.id = json["SensorId"].toInt(0);
sensor_.field = json["SensorField"].toInt(0); sensor_.field = json["SensorField"].toInt(0);
sensor_.name = json["SensorName"].toString("Sensor"); sensor_.name = json["SensorName"].toString("Sensor");

View file

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

View file

@ -10,5 +10,5 @@ FixedItemSource::FixedItemSource(Microcontroller* micro, QObject *parent):
void FixedItemSource::refresh() void FixedItemSource::refresh()
{ {
gotItems({powerItem, rgbItem, auxItem}); gotItems({powerItem, rgbItem, auxItem}, ITEM_UPDATE_BACKEND);
} }

View file

@ -34,6 +34,11 @@ uint8_t ItemData::getValue() const
return value_; return value_;
} }
void ItemData::setValueData(uint8_t value)
{
value_ = value;
}
uint32_t ItemData::id() const uint32_t ItemData::id() const
{ {
return itemId_; return itemId_;
@ -79,7 +84,7 @@ bool ItemData::hasChanged(const ItemData& other)
return false; return false;
} }
bool ItemData::isHidden() bool ItemData::isHidden() const
{ {
return hidden_; return hidden_;
} }
@ -126,6 +131,7 @@ void Item::store(QJsonObject &json)
} }
} }
json["Actors"] = actorsArray; json["Actors"] = actorsArray;
json["ValueType"] = type_;
} }
void Item::load(const QJsonObject &json, const bool preserve) 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)) name_ = other.getName();
setValue(value); value_ = other.getValue();
itemId_ = other.id();
hidden_ = other.isHidden();
return *this;
} }
void Item::setValue(uint8_t value) void Item::requestUpdate(ItemUpdateRequest update)
{ {
qDebug()<<__func__; assert(update.type != ITEM_UPDATE_INVALID);
informValue(value); if(update.type != ITEM_UPDATE_LOADED && value_ == update.payload.getValue())
if(programMode == PROGRAM_MODE_PRIMARY || programMode == PROGRAM_MODE_HEADLESS_PRIMARY) return;
enactValue(value);
}
void Item::informValue(uint8_t value) if(update.type == ITEM_UPDATE_ACTOR && override_)
return;
qDebug()<<"Item Update Request for"<<getName()<<" type "<<update.type<<" value "<<update.payload.getValue();
if(update.type != ITEM_UPDATE_LOADED &&
update.type != ITEM_UPDATE_BACKEND &&
(programMode == PROGRAM_MODE_PRIMARY || programMode == PROGRAM_MODE_HEADLESS_PRIMARY))
enactValue(update.payload.getValue());
if(update.type != ITEM_UPDATE_LOADED)
{ {
if(value_ != value) value_ = update.payload.getValue();
{
value_ = value;
valueChanged(value_);
updated(*this);
} }
else
{
name_ = update.payload.getName();
//itemId_ = update.payload.id();
hidden_ = update.payload.isHidden();
actors_.clear();
for(std::shared_ptr<Actor>& actor : update.newActors)
addActor(actor);
}
update.payload = *this;
updated(update);
} }
void Item::enactValue(uint8_t value) void Item::enactValue(uint8_t value)
@ -178,8 +202,8 @@ void Item::addActor(std::shared_ptr<Actor> actor)
actor->setParent(this); actor->setParent(this);
actors_.push_back(actor); actors_.push_back(actor);
if(programMode == PROGRAM_MODE_PRIMARY || programMode == PROGRAM_MODE_HEADLESS_PRIMARY) if(programMode == PROGRAM_MODE_PRIMARY || programMode == PROGRAM_MODE_HEADLESS_PRIMARY)
connect(actor.get(), &Actor::sigValue, this, &Item::actorSetValue); connect(actor.get(), &Actor::sigItemUpdate, this, &Item::requestUpdate);
connect(this, &Item::valueChanged, actor.get(), &Actor::onValueChanged); connect(this, &Item::updated, actor.get(), &Actor::onItemUpdated);
std::shared_ptr<SensorActor> sensorActor = std::dynamic_pointer_cast<SensorActor>(actor); std::shared_ptr<SensorActor> sensorActor = std::dynamic_pointer_cast<SensorActor>(actor);
if(sensorActor) if(sensorActor)
@ -238,41 +262,25 @@ void Item::setActorsActive(bool in)
in ? actors_[i]->makeActive() : actors_[i]->makeInactive(); in ? actors_[i]->makeActive() : actors_[i]->makeInactive();
} }
void Item::mergeLoaded(Item& item)
{
name_ = item.name_;
actors_.clear();
for(std::shared_ptr<Actor> actor : item.actors_)
addActor(actor);
}
std::shared_ptr<Item> Item::loadItem(const QJsonObject& json) std::shared_ptr<Item> Item::loadItem(const QJsonObject& json)
{ {
std::shared_ptr<Item> newItem = nullptr; 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); newItem = std::shared_ptr<Relay>(new Relay);
}
else if(json["Type"].toString("") == "Message") else if(json["Type"].toString("") == "Message")
{
newItem = std::shared_ptr<MessageItem>(new MessageItem); newItem = std::shared_ptr<MessageItem>(new MessageItem);
}
else if(json["Type"].toString("") == "System") else if(json["Type"].toString("") == "System")
{
newItem = std::shared_ptr<SystemItem>(new SystemItem); newItem = std::shared_ptr<SystemItem>(new SystemItem);
}
else if(json["Type"].toString("") == "Aux") else if(json["Type"].toString("") == "Aux")
{
newItem = std::shared_ptr<AuxItem>(new AuxItem); newItem = std::shared_ptr<AuxItem>(new AuxItem);
}
else if(json["Type"].toString("") == "Power") else if(json["Type"].toString("") == "Power")
{
newItem = std::shared_ptr<PowerItem>(new PowerItem); newItem = std::shared_ptr<PowerItem>(new PowerItem);
}
else if(json["Type"].toString("") == "Rgb") else if(json["Type"].toString("") == "Rgb")
{
newItem = std::shared_ptr<RgbItem>(new RgbItem); newItem = std::shared_ptr<RgbItem>(new RgbItem);
} else
qWarning()<<"Unable to load unkown item type: "<<json["Type"].toString();
if(newItem) if(newItem)
{ {
newItem->load(json); newItem->load(json);
@ -281,3 +289,15 @@ std::shared_ptr<Item> Item::loadItem(const QJsonObject& json)
return newItem; 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;
}

View file

@ -14,6 +14,15 @@ typedef enum {
ITEM_VALUE_NO_VALUE ITEM_VALUE_NO_VALUE
} item_value_type_t; } 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 class ItemData
{ {
protected: protected:
@ -46,9 +55,10 @@ public:
bool hasChanged(const ItemData& other); bool hasChanged(const ItemData& other);
void setName(QString name); void setName(QString name);
uint8_t getValue() const; uint8_t getValue() const;
void setValueData(uint8_t value);
bool getLoaded() const; bool getLoaded() const;
void setLoaded(bool loaded); void setLoaded(bool loaded);
bool isHidden(); bool isHidden() const;
void setHidden(bool hidden); void setHidden(bool hidden);
item_value_type_t getValueType(); item_value_type_t getValueType();
virtual QString getName() const; virtual QString getName() const;
@ -56,24 +66,26 @@ public:
virtual void load(const QJsonObject& json, const bool preserve = false); virtual void load(const QJsonObject& json, const bool preserve = false);
}; };
struct ItemUpdateRequest
{
item_update_type_t type = ITEM_UPDATE_INVALID;
ItemData payload;
std::vector<std::shared_ptr<Actor> > newActors;
};
class Item: public QObject, public ItemData class Item: public QObject, public ItemData
{ {
Q_OBJECT Q_OBJECT
private: private:
std::vector< std::shared_ptr<Actor> > actors_; std::vector< std::shared_ptr<Actor> > actors_;
bool override_ = false; bool override_ = false;
signals: signals:
void valueChanged(uint8_t value); void updated(ItemUpdateRequest update);
void updated(ItemData data);
private slots:
virtual void actorSetValue(uint8_t value);
public slots: public slots:
void setValue(uint8_t value); virtual void requestUpdate(ItemUpdateRequest update);
public: public:
@ -83,6 +95,7 @@ public:
virtual ~Item(); virtual ~Item();
Item& operator=(const ItemData& other);
std::vector< std::shared_ptr<Actor> >& getActors(); std::vector< std::shared_ptr<Actor> >& getActors();
bool hasActors(); bool hasActors();
void addActor(std::shared_ptr<Actor> actor); void addActor(std::shared_ptr<Actor> actor);
@ -91,8 +104,9 @@ public:
void setActorsActive(bool in); void setActorsActive(bool in);
void setOverride(const bool in); void setOverride(const bool in);
bool getOverride(); bool getOverride();
void informValue(uint8_t value); ItemUpdateRequest createValueUpdateRequest(uint8_t value,
void mergeLoaded(Item& item); item_update_type_t type,
bool withActors = false);
virtual void store(QJsonObject& json); virtual void store(QJsonObject& json);
virtual void load(const QJsonObject& json, const bool preserve = false); virtual void load(const QJsonObject& json, const bool preserve = false);

View file

@ -25,7 +25,7 @@ void ItemLoaderSource::refresh()
qDebug()<<"Loaded item"<<newItem->getName(); qDebug()<<"Loaded item"<<newItem->getName();
} }
} }
gotItems(items); gotItems(items, ITEM_UPDATE_LOADED);
} }
void ItemLoaderSource::updateJson(const QJsonObject& json) void ItemLoaderSource::updateJson(const QJsonObject& json)

View file

@ -17,9 +17,9 @@ public slots:
virtual void refresh() = 0; virtual void refresh() = 0;
signals: signals:
void gotItems(std::vector<std::shared_ptr<Item>> items, bool inform = true); void gotItems(std::vector<std::shared_ptr<Item>> items, item_update_type_t updateType);
void requestReplaceItems(std::vector<std::shared_ptr<Item>> items); void requestReplaceItems(std::vector<std::shared_ptr<Item>> items);
void updateItems(std::vector<ItemData> items, bool inform = true); void updateItems(std::vector<ItemUpdateRequest> updates);
}; };
#endif // ITEMSOURCE_H #endif // ITEMSOURCE_H

View file

@ -6,7 +6,7 @@ ItemStore::ItemStore(QObject *parent): QObject(parent)
{ {
} }
void ItemStore::addItem(std::shared_ptr<Item> item, bool inform) void ItemStore::addItem(const std::shared_ptr<Item>& item, item_update_type_t updateType)
{ {
std::shared_ptr<Item> matched = nullptr; std::shared_ptr<Item> matched = nullptr;
for(unsigned i = 0; i < items_.size(); i++ ) for(unsigned i = 0; i < items_.size(); i++ )
@ -26,17 +26,19 @@ void ItemStore::addItem(std::shared_ptr<Item> item, bool inform)
} }
else else
{ {
if(item->getLoaded()) ItemUpdateRequest request = item->createValueUpdateRequest(item->getValue(),
matched->mergeLoaded(*item); updateType,
else if(item->getValue() != matched->getValue()) updateType == ITEM_UPDATE_LOADED);
updateItem(*item, inform); request.newActors = item->getActors();
updateItem(request);
} }
} }
void ItemStore::addItems(const std::vector<std::shared_ptr<Item>>& itemIn, bool inform) void ItemStore::addItems(const std::vector<std::shared_ptr<Item>>& itemIn,
item_update_type_t updateType)
{ {
for(unsigned j = 0; j < itemIn.size(); j++) for(unsigned j = 0; j < itemIn.size(); j++)
addItem(itemIn[j], inform); addItem(itemIn[j], updateType);
} }
void ItemStore::removeItem(const ItemData& item) void ItemStore::removeItem(const ItemData& item)
@ -55,7 +57,8 @@ void ItemStore::removeItem(const ItemData& item)
void ItemStore::replaceItems(const std::vector<std::shared_ptr<Item>>& items) void ItemStore::replaceItems(const std::vector<std::shared_ptr<Item>>& items)
{ {
addItems(items, true); qDebug()<<__func__;
addItems(items, ITEM_UPDATE_LOADED);
std::vector<ItemData> deletedItems; std::vector<ItemData> deletedItems;
for(std::shared_ptr<Item> item : items_) for(std::shared_ptr<Item> item : items_)
{ {
@ -72,32 +75,20 @@ void ItemStore::clear()
items_.clear(); items_.clear();
} }
void ItemStore::updateItems(const std::vector<ItemUpdateRequest>& updates)
void ItemStore::updateItems(std::vector<ItemData> items, bool inform)
{ {
for(const ItemData& item : items) for(const ItemUpdateRequest& update : updates)
updateItem(item, inform); updateItem(update);
} }
void ItemStore::updateItem(const ItemData& item, bool inform) void ItemStore::updateItem(const ItemUpdateRequest& update)
{ {
for(unsigned i = 0; i < items_.size(); i++ ) for(unsigned i = 0; i < items_.size(); i++ )
{ {
if(items_[i]->operator==(item)) if(items_[i]->operator==(update.payload))
{ {
if(items_[i]->hasChanged(item)) items_[i]->requestUpdate(update);
{ itemUpdated(update);
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"<<items_[i]->getName()<<"updated";
itemUpdated(items_[i]);
}
} }
} }
} }
@ -114,13 +105,9 @@ void ItemStore::store(QJsonObject& json)
json["Items"] = itemsArray; json["Items"] = itemsArray;
} }
void ItemStore::itemUpdateSlot(ItemData data) void ItemStore::itemUpdateSlot(ItemUpdateRequest update)
{ {
for(std::shared_ptr<Item>& item: items_) itemUpdated(update);
{
if(*item == data)
itemUpdated(std::weak_ptr<Item>(item));
}
} }
std::shared_ptr<Item> ItemStore::getItem(uint32_t id) std::shared_ptr<Item> ItemStore::getItem(uint32_t id)

View file

@ -33,21 +33,21 @@ signals:
void itemDeleted(ItemData item); void itemDeleted(ItemData item);
void itemAdded(std::weak_ptr<Item> Item); void itemAdded(std::weak_ptr<Item> Item);
void itemUpdated(std::weak_ptr<Item> Item); void itemUpdated(ItemUpdateRequest update);
void sigRefresh(); void sigRefresh();
public slots: public slots:
void removeItem(const ItemData& item); void removeItem(const ItemData& item);
void addItem(std::shared_ptr<Item> item, bool inform = true); void addItem(const std::shared_ptr<Item>& item, item_update_type_t updateType);
void addItems(const std::vector<std::shared_ptr<Item>>& itemsIn, bool inform = true); void addItems(const std::vector<std::shared_ptr<Item>>& itemsIn, item_update_type_t updateType);
void replaceItems(const std::vector<std::shared_ptr<Item>>& items); void replaceItems(const std::vector<std::shared_ptr<Item>>& items);
void updateItems(std::vector<ItemData> items, bool inform = true); void updateItems(const std::vector<ItemUpdateRequest>& updates);
void updateItem(const ItemData& item, bool inform = true); void updateItem(const ItemUpdateRequest& update);
void refresh(); void refresh();
private slots: private slots:
void itemUpdateSlot(ItemData data); void itemUpdateSlot(ItemUpdateRequest update);
}; };
extern ItemStore globalItems; extern ItemStore globalItems;

View file

@ -7,7 +7,7 @@ PowerItem::PowerItem(uint32_t itemIdIn, QString name, uint8_t value, QObject* p
Item(itemIdIn, name, value, parent) Item(itemIdIn, name, value, parent)
{ {
stateChanged(Sensor(Sensor::TYPE_SHUTDOWN_IMMINENT, 0, 0, "Shutdown Imminent", true)); stateChanged(Sensor(Sensor::TYPE_SHUTDOWN_IMMINENT, 0, 0, "Shutdown Imminent", true));
PowerItem::setValue(true); value_ = true;
hidden_ = true; hidden_ = true;
type_ = ITEM_VALUE_NO_VALUE; type_ = ITEM_VALUE_NO_VALUE;
} }

View file

@ -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) void Relay::store(QJsonObject& json)
{ {
json["Type"] = "Relay"; json["Type"] = "Relay";

View file

@ -4,7 +4,6 @@
#include<stdint.h> #include<stdint.h>
#include<QObject> #include<QObject>
#include "sensors/sensor.h"
#include "item.h" #include "item.h"
class Microcontroller; class Microcontroller;
@ -21,11 +20,6 @@ private:
protected: protected:
virtual void enactValue(uint8_t value) override; virtual void enactValue(uint8_t value) override;
public slots:
void on();
void off();
void toggle();
public: public:
Relay(uint8_t id = 0, QString name = "", uint16_t address = 0, bool state = false, QObject* parent = nullptr); Relay(uint8_t id = 0, QString name = "", uint16_t address = 0, bool state = false, QObject* parent = nullptr);

View file

@ -35,7 +35,7 @@ int main(int argc, char *argv[])
parser.addOption(masterOption); parser.addOption(masterOption);
QCommandLineOption hostOption(QStringList() << "H" << "host", QCoreApplication::translate("main", "Set server host ip addres"), "address", "0.0.0.0"); QCommandLineOption hostOption(QStringList() << "H" << "host", QCoreApplication::translate("main", "Set server host ip addres"), "address", "0.0.0.0");
parser.addOption(hostOption); 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); parser.addOption(portOption);
QCommandLineOption settingsPathOption(QStringList()<<"c"<<"config", QCoreApplication::translate("main", "Set config file"), "configFilePath", QCommandLineOption settingsPathOption(QStringList()<<"c"<<"config", QCoreApplication::translate("main", "Set config file"), "configFilePath",
QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) + "/shinterface.json"); 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(&mainObject.micro, SIGNAL(textRecived(QString)), w, SLOT(changeHeaderLableText(QString)));
QObject::connect(w, &MainWindow::sigSetRgb, &mainObject.micro, &Microcontroller::changeRgbColor); QObject::connect(w, &MainWindow::sigSetRgb, &mainObject.micro, &Microcontroller::changeRgbColor);
QObject::connect(w, &MainWindow::sigSave, &mainObject, [&mainObject, settingsPath](){mainObject.storeToDisk(settingsPath);}); QObject::connect(w, &MainWindow::sigSave, &mainObject, [&mainObject, settingsPath](){mainObject.storeToDisk(settingsPath);});
QObject::connect(w, &MainWindow::createdItem, &globalItems, [](std::shared_ptr<Item> item){globalItems.addItem(item, false);}); QObject::connect(w,
&MainWindow::createdItem,
&globalItems,
[](std::shared_ptr<Item> item) {
globalItems.addItem(item, ITEM_UPDATE_USER);
});
w->show(); w->show();
} }
retVal = a.exec(); retVal = a.exec();
@ -127,7 +132,9 @@ int main(int argc, char *argv[])
{ {
SecondaryMainObject mainObject(parser.value(hostOption), parser.value(portOption).toInt()); SecondaryMainObject mainObject(parser.value(hostOption), parser.value(portOption).toInt());
MainWindow w(&mainObject); MainWindow w(&mainObject);
QObject::connect(&w, &MainWindow::createdItem, &globalItems, [](std::shared_ptr<Item> item){globalItems.addItem(item, false);}); QObject::connect(&w, &MainWindow::createdItem, &globalItems, [](std::shared_ptr<Item> item) {
globalItems.addItem(item, ITEM_UPDATE_USER);
});
QObject::connect(&w, &MainWindow::sigSave, mainObject.tcpClient, &TcpClient::sendItems); QObject::connect(&w, &MainWindow::sigSave, mainObject.tcpClient, &TcpClient::sendItems);
w.show(); w.show();

View file

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

View file

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

View file

@ -1,8 +1,5 @@
#include "microcontroller.h" #include "microcontroller.h"
#include <chrono>
#include <thread>
void Microcontroller::relayToggle(int state, int relay) void Microcontroller::relayToggle(int state, int relay)
{ {
char buffer[8]; char buffer[8];
@ -49,22 +46,34 @@ void Microcontroller::setAuxPwm(int duty)
void Microcontroller::write(const QByteArray& buffer) void Microcontroller::write(const QByteArray& buffer)
{ {
if(_port != nullptr) writeQue.enqueue(buffer);
if(!writeTimer.isActive())
{ {
_port->write(buffer); writeTimer.setInterval(0);
_port->waitForBytesWritten(1000); writeTimer.start();
} }
std::this_thread::sleep_for(std::chrono::milliseconds(40));
} }
void Microcontroller::write(char* buffer, const size_t length) void Microcontroller::write(char* buffer, const size_t length)
{ {
if(_port != nullptr) write(QByteArray(buffer, length));
{ }
_port->write(buffer, length);
_port->waitForBytesWritten(1000); void Microcontroller::onWriteTimerTimeout()
{
writeTimer.setInterval(50);
if(connected())
{
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) void Microcontroller::setPattern(int pattern)
@ -82,8 +91,10 @@ void Microcontroller::startSunrise()
bool Microcontroller::connected() bool Microcontroller::connected()
{ {
if(_port != nullptr) return _port->isOpen(); if(_port != nullptr)
else return false; return _port->isOpen();
else
return false;
} }
void Microcontroller::refresh() void Microcontroller::refresh()
@ -93,13 +104,17 @@ void Microcontroller::refresh()
//housekeeping //housekeeping
Microcontroller::Microcontroller(QIODevice* port) Microcontroller::Microcontroller(QIODevice* port): Microcontroller()
{ {
setIODevice(port); setIODevice(port);
} }
Microcontroller::Microcontroller() Microcontroller::Microcontroller()
{ {
writeTimer.setInterval(50);
writeTimer.setSingleShot(false);
connect(&writeTimer, &QTimer::timeout, this, &Microcontroller::onWriteTimerTimeout);
qDebug()<<__func__<<writeTimer.isActive();
} }
Microcontroller::~Microcontroller() Microcontroller::~Microcontroller()
@ -142,7 +157,7 @@ void Microcontroller::processList(const QString& buffer)
else if(buffer.contains("EOL")) else if(buffer.contains("EOL"))
{ {
listMode = false; listMode = false;
gotItems(relayList); gotItems(relayList, ITEM_UPDATE_BACKEND);
relayList.clear(); relayList.clear();
} }
else listMode = false; else listMode = false;
@ -150,7 +165,10 @@ void Microcontroller::processList(const QString& buffer)
void Microcontroller::processRelayState(const QString& buffer) void Microcontroller::processRelayState(const QString& buffer)
{ {
updateItems({static_cast<ItemData>(*processRelayLine(buffer))}); ItemUpdateRequest update;
update.type = ITEM_UPDATE_BACKEND;
update.payload = static_cast<ItemData>(*processRelayLine(buffer));
updateItems({update});
} }
void Microcontroller::processSensorState(const QString& buffer) void Microcontroller::processSensorState(const QString& buffer)

View file

@ -7,9 +7,9 @@
#include <QString> #include <QString>
#include <QRunnable> #include <QRunnable>
#include <QDebug> #include <QDebug>
#include <QEventLoop>
#include <QTimer> #include <QTimer>
#include <QAbstractButton> #include <QByteArray>
#include <QQueue>
#include <vector> #include <vector>
#include <memory> #include <memory>
#include "items/item.h" #include "items/item.h"
@ -32,13 +32,12 @@ private:
bool listMode = false; bool listMode = false;
//uint8_t _auxState = 0;
QIODevice* _port = nullptr; QIODevice* _port = nullptr;
QQueue<QByteArray> writeQue;
QTimer writeTimer;
std::vector< std::shared_ptr<Item> > relayList; std::vector< std::shared_ptr<Item> > relayList;
QScopedPointer<QEventLoop> loop;
QString _buffer; QString _buffer;
void processMicroReturn(); void processMicroReturn();
@ -75,6 +74,7 @@ public slots:
private slots: private slots:
void isReadyRead(); void isReadyRead();
void onWriteTimerTimeout();
signals: signals:
void textRecived(const QString string); void textRecived(const QString string);

View file

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

View file

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

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

@ -0,0 +1,104 @@
#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, ITEM_UPDATE_REMOTE);
}
}
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;
}
}
}
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);
}

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

@ -0,0 +1,52 @@
#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 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

View file

@ -1,7 +1,6 @@
#include <QTcpSocket> #include <QTcpSocket>
#include <QJsonDocument> #include <QJsonDocument>
#include <QJsonArray> #include <QJsonArray>
#include <iostream>
#include "items/item.h" #include "items/item.h"
#include "items/itemstore.h" #include "items/itemstore.h"
@ -30,17 +29,7 @@ void Service::sensorEvent(Sensor sensor)
sendJson(json); sendJson(json);
} }
void Service::itemUpdated(std::weak_ptr<Item> item) void Service::itemUpdated(ItemUpdateRequest update) {}
{
qDebug()<<__func__;
QJsonArray items;
QJsonObject itemjson;
item.lock()->store(itemjson);
items.append(itemjson);
QJsonObject json = createMessage("ItemUpdate", items);
json["FullList"] = false;
sendJson(json);
}
void Service::refresh() void Service::refresh()
{ {
@ -78,7 +67,6 @@ void Service::sendItems()
void Service::processIncomeingJson(const QByteArray& jsonbytes) void Service::processIncomeingJson(const QByteArray& jsonbytes)
{ {
QJsonDocument doc = QJsonDocument::fromJson(jsonbytes); QJsonDocument doc = QJsonDocument::fromJson(jsonbytes);
qDebug()<<"Got Json:"<<QString::fromLatin1(doc.toJson(QJsonDocument::JsonFormat::Indented));
QJsonObject json = doc.object(); QJsonObject json = doc.object();
QString type = json["MessageType"].toString(); QString type = json["MessageType"].toString();
if(type == "GetSensors") if(type == "GetSensors")
@ -94,6 +82,7 @@ void Service::processIncomeingJson(const QByteArray& jsonbytes)
else if(type == "SensorUpdate") else if(type == "SensorUpdate")
{ {
QJsonArray data = json["Data"].toArray(); QJsonArray data = json["Data"].toArray();
qWarning()<<"Got sensor update with no/empty sensors array";
for(QJsonValueRef sensorjson : data) for(QJsonValueRef sensorjson : data)
{ {
QJsonObject jsonobject = sensorjson.toObject(); QJsonObject jsonobject = sensorjson.toObject();

View file

@ -24,7 +24,7 @@ signals:
public slots: public slots:
void sensorEvent(Sensor sensor); void sensorEvent(Sensor sensor);
void itemUpdated(std::weak_ptr<Item> item); virtual void itemUpdated(ItemUpdateRequest update);
virtual void refresh() override; virtual void refresh() override;
public: public:

View file

@ -33,7 +33,6 @@ void TcpClient::processIncomeingJson(const QByteArray& jsonbytes)
QString type = json["MessageType"].toString(); QString type = json["MessageType"].toString();
if(type == "ItemUpdate") if(type == "ItemUpdate")
{ {
std::cout<<"Got item json:\n"<<QString::fromLatin1(jsonbytes).toStdString();
QJsonArray data = json["Data"].toArray(); QJsonArray data = json["Data"].toArray();
std::vector<std::shared_ptr<Item>> items; std::vector<std::shared_ptr<Item>> items;
for(QJsonValueRef itemjson : data) for(QJsonValueRef itemjson : data)
@ -42,12 +41,13 @@ void TcpClient::processIncomeingJson(const QByteArray& jsonbytes)
std::shared_ptr<Item> item = Item::loadItem(jsonobject); std::shared_ptr<Item> item = Item::loadItem(jsonobject);
if(item) if(item)
{ {
qDebug()<<"Client got item"<<item->getName();
item->setLoaded(false); item->setLoaded(false);
items.push_back(item); items.push_back(item);
} }
} }
if(!items.empty()) if(!items.empty())
gotItems(items, true); gotItems(items, ITEM_UPDATE_REMOTE);
} }
else 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() TcpClient::~TcpClient()
{ {
delete socket; delete socket;

View file

@ -16,6 +16,9 @@ class TcpClient : public Service
long long recievebytes = 0; long long recievebytes = 0;
QByteArray buffer; QByteArray buffer;
public slots:
virtual void itemUpdated(ItemUpdateRequest update) override;
public: public:
TcpClient(QObject* parent = nullptr); TcpClient(QObject* parent = nullptr);
~TcpClient(); ~TcpClient();

View file

@ -4,11 +4,11 @@
#include <QJsonArray> #include <QJsonArray>
#include "items/item.h" #include "items/item.h"
#include "service.h" #include "server.h"
#include "tcpserver.h" #include "tcpserver.h"
TcpServer::TcpServer(QObject* parent): TcpServer::TcpServer(QObject* parent):
Service(parent), Server(parent),
server(this) server(this)
{ {
connect(&server, &QTcpServer::newConnection, this, &TcpServer::incomingConnection); connect(&server, &QTcpServer::newConnection, this, &TcpServer::incomingConnection);
@ -16,50 +16,23 @@ TcpServer::TcpServer(QObject* parent):
void TcpServer::sendJson(const QJsonObject& json) void TcpServer::sendJson(const QJsonObject& json)
{ {
for(auto client: clients) for(auto& client: clients)
{ {
if(client.socket.tcpSocket) {
QByteArray jsonData = QJsonDocument(json).toJson(); 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) void TcpServer::processIncomeingJson(const QByteArray& jsonbytes)
{ {
QJsonDocument doc = QJsonDocument::fromJson(jsonbytes); Server::processIncomeingJson(jsonbytes);
QJsonObject json = doc.object();
QString type = json["MessageType"].toString();
if(type == "ItemUpdate")
{
qDebug()<<"Got Items";
QJsonArray data = json["Data"].toArray();
bool FullList = json["FullList"].toBool(false);
std::vector<std::shared_ptr<Item>> items;
for(QJsonValueRef itemjson : data)
{
QJsonObject jsonobject = itemjson.toObject();
std::shared_ptr<Item> item = Item::loadItem(jsonobject);
item->setLoaded(FullList);
if(item)
items.push_back(item);
}
if(FullList && !items.empty())
{
requestReplaceItems(items);
sigRequestSave();
}
else if(!items.empty())
{
gotItems(items, false);
}
}
else
{
Service::processIncomeingJson(jsonbytes);
}
} }
bool TcpServer::launch(const QHostAddress &address, quint16 port) bool TcpServer::launch(const QHostAddress &address, quint16 port)
{ {
qInfo()<<"Tcp server launched on"<<address<<"port"<<port;
return server.listen(address, port); return server.listen(address, port);
} }
@ -71,38 +44,16 @@ void TcpServer::incomingConnection()
qDebug()<<"Got new client from"<<client->peerAddress().toString(); qDebug()<<"Got new client from"<<client->peerAddress().toString();
if(client) if(client)
{ {
clients.push_back({client}); Client c;
connect(client, &QTcpSocket::errorOccurred, this, &TcpServer::socketError); c.socket.tcpSocket = client;
connect(client, &QTcpSocket::disconnected, this, &TcpServer::socketDisconnect); 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); 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) void TcpServer::processComand(const QByteArray& command, Client& client)
{ {
if(command.startsWith("MSG JSON LEN ")) if(command.startsWith("MSG JSON LEN "))
@ -117,15 +68,15 @@ void TcpServer::socketReadyRead()
{ {
for(size_t i = 0; i < clients.size(); i++) 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; clients[i].buffer += newChars;
bool remianing = true; bool remianing = true;
while(remianing) while(remianing)
{ {
qDebug()<<clients[i].buffer;
remianing = false; remianing = false;
while(clients[i].state == STATE_IDLE && clients[i].buffer.contains('\n')) while(clients[i].state == STATE_IDLE && clients[i].buffer.contains('\n'))
{ {

View file

@ -4,21 +4,12 @@
#include <QTcpServer> #include <QTcpServer>
#include <vector> #include <vector>
#include "service.h" #include "server.h"
class TcpServer : public Service class TcpServer : public Server
{ {
Q_OBJECT Q_OBJECT
struct Client
{
QTcpSocket* socket;
QByteArray buffer;
client_state_t state = STATE_IDLE;
long long recievebytes = 0;
};
std::vector<Client> clients;
QTcpServer server; QTcpServer server;
public: public:
@ -31,8 +22,6 @@ signals:
private slots: private slots:
void incomingConnection(); void incomingConnection();
void socketError(QAbstractSocket::SocketError socketError);
void socketDisconnect();
void socketReadyRead(); void socketReadyRead();
protected: 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

@ -38,7 +38,7 @@ ItemWidget::ItemWidget(std::weak_ptr<Item> item, QWidget *parent) :
else else
connect(ui->checkBox, &QCheckBox::toggled, this, &ItemWidget::moveToState); connect(ui->checkBox, &QCheckBox::toggled, this, &ItemWidget::moveToState);
connect(ui->pushButton, &QPushButton::clicked, this, &ItemWidget::showSettingsDialog); 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); connect(ui->pushButton_Remove, &QPushButton::clicked, this, &ItemWidget::deleteItem);
} }
@ -59,18 +59,28 @@ void ItemWidget::deleteItem()
void ItemWidget::moveToValue(int value) void ItemWidget::moveToValue(int value)
{ {
if(auto workingItem = item_.lock()) if(auto workingItem = item_.lock())
workingItem->setValue(value); {
ItemUpdateRequest request = workingItem->createValueUpdateRequest(value, ITEM_UPDATE_USER);
workingItem->requestUpdate(request);
}
else else
{
disable(); disable();
} }
}
void ItemWidget::moveToState(bool state) void ItemWidget::moveToState(bool state)
{ {
if(auto workingItem = item_.lock()) if(auto workingItem = item_.lock())
workingItem->setValue(state); {
ItemUpdateRequest request = workingItem->createValueUpdateRequest(state, ITEM_UPDATE_USER);
workingItem->requestUpdate(request);
}
else else
{
disable(); disable();
} }
}
void ItemWidget::disable() void ItemWidget::disable()
{ {
@ -105,6 +115,11 @@ std::weak_ptr<Item> ItemWidget::getItem()
return item_; return item_;
} }
void ItemWidget::onItemUpdated(ItemUpdateRequest update)
{
stateChanged(update.payload.getValue());
}
void ItemWidget::stateChanged(int state) void ItemWidget::stateChanged(int state)
{ {
ui->slider->blockSignals(true); ui->slider->blockSignals(true);

View file

@ -38,6 +38,7 @@ public:
public slots: public slots:
void stateChanged(int state); void stateChanged(int state);
void onItemUpdated(ItemUpdateRequest update);
private: private:
Ui::ItemWidget *ui; Ui::ItemWidget *ui;

View file

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

View file

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

View file

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