From 5746fb88aa9607c6c3fe2a26297d3d756aa3ca4a Mon Sep 17 00:00:00 2001 From: Reinder Feenstra Date: Thu, 11 Apr 2024 00:03:06 +0200 Subject: [PATCH] implemented support for setting interface sub properties see #107 --- client/data/wizard/add_interface.json | 20 +-- client/src/network/abstractproperty.hpp | 32 +++++ client/src/network/create/createinterface.cpp | 68 +++++----- client/src/network/create/createinterface.hpp | 13 +- client/src/network/create/properties.hpp | 33 +++++ .../network/create/setobjectproperties.cpp | 120 ++++++++++++++++++ .../network/create/setobjectproperties.hpp | 63 +++++++++ client/src/wizard/jsonwizard.cpp | 6 +- 8 files changed, 299 insertions(+), 56 deletions(-) create mode 100644 client/src/network/create/properties.hpp create mode 100644 client/src/network/create/setobjectproperties.cpp create mode 100644 client/src/network/create/setobjectproperties.hpp diff --git a/client/data/wizard/add_interface.json b/client/data/wizard/add_interface.json index f21424e5..cd91d911 100644 --- a/client/data/wizard/add_interface.json +++ b/client/data/wizard/add_interface.json @@ -240,7 +240,7 @@ "type": "serial", "baudrate": 115200, "flow_control": "hardware", - "settings.command_station": "%loconet_command_station%" + "loconet.command_station": "%loconet_command_station%" } } } @@ -310,7 +310,7 @@ "type": "serial", "baudrate": 115200, "flow_control": "none", - "settings.command_station": "digikeijs_dr5000" + "loconet.command_station": "digikeijs_dr5000" } } } @@ -325,7 +325,7 @@ "type": "serial", "baudrate": 115200, "flow_control": "none", - "settings.command_station": "digikeijs_dr5000" + "xpressnet.command_station": "digikeijs_dr5000" } } } @@ -345,7 +345,7 @@ "class_id": "interface.loconet", "properties": { "type": "tcp_binary", - "settings.command_station": "digikeijs_dr5000" + "loconet.command_station": "digikeijs_dr5000" } } } @@ -358,7 +358,7 @@ "class_id": "interface.loconet", "properties": { "type": "lbserver", - "settings.command_station": "digikeijs_dr5000" + "loconet.command_station": "digikeijs_dr5000" } } } @@ -371,7 +371,7 @@ "class_id": "interface.xpressnet", "properties": { "type": "network", - "settings.command_station": "digikeijs_dr5000" + "xpressnet.command_station": "digikeijs_dr5000" } } } @@ -402,7 +402,7 @@ "type": "serial", "baudrate": 19200, "flowcontrol": "hardware", - "settings.command_station": "%loconet_command_station%" + "loconet.command_station": "%loconet_command_station%" } }, "set_variables": { @@ -420,7 +420,7 @@ "type": "serial", "baudrate": 115200, "flowcontrol": "none", - "settings.command_station": "%loconet_command_station%" + "loconet.command_station": "%loconet_command_station%" } }, "set_variables": { @@ -445,7 +445,7 @@ "type": "serial", "baudrate": 9600, "flowcontrol": "hardware", - "settings.command_station": "%xpressnet_command_station%" + "xpressnet.command_station": "%xpressnet_command_station%" } }, "set_variables": { @@ -463,7 +463,7 @@ "type": "serial", "baudrate": 19200, "flowcontrol": "hardware", - "settings.command_station": "%xpressnet_command_station%" + "xpressnet.command_station": "%xpressnet_command_station%" } }, "set_variables": { diff --git a/client/src/network/abstractproperty.hpp b/client/src/network/abstractproperty.hpp index 37453adc..9a3076f6 100644 --- a/client/src/network/abstractproperty.hpp +++ b/client/src/network/abstractproperty.hpp @@ -93,6 +93,38 @@ class AbstractProperty : public BaseProperty virtual void setValueInt64(int64_t value) { Q_ASSERT(value != value); } virtual void setValueDouble(double value) { Q_ASSERT(value != value); } virtual void setValueString(const QString& value) { Q_ASSERT(value != value); } + + void setValueVariant(const QVariant& value) + { + switch(value.type()) + { + case QVariant::Bool: + setValueBool(value.toBool()); + break; + + case QVariant::Int: + setValueInt(value.toInt()); + break; + + case QVariant::UInt: + case QVariant::LongLong: + case QVariant::ULongLong: + setValueInt64(value.toLongLong()); + break; + + case QVariant::Double: + setValueDouble(value.toDouble()); + break; + + case QVariant::String: + setValueString(value.toString()); + break; + + default: /*[[unlikely]]*/ + assert(false); + break; + } + } }; #endif diff --git a/client/src/network/create/createinterface.cpp b/client/src/network/create/createinterface.cpp index ca02fdde..e8f4cb07 100644 --- a/client/src/network/create/createinterface.cpp +++ b/client/src/network/create/createinterface.cpp @@ -21,10 +21,12 @@ */ #include "createinterface.hpp" +#include "setobjectproperties.hpp" #include "../connection.hpp" #include "../error.hpp" #include "../method.hpp" #include "../object.hpp" +#include "../property.hpp" #include "../objectproperty.hpp" CreateInterface::CreateInterface(ObjectPtr world, QString classId, Properties properties) @@ -66,45 +68,33 @@ CreateInterface::CreateInterface(ObjectPtr world, QString classId, Properties pr } else if(interface) { - for(const auto& it : m_properties) + m_interface = interface; + + if(m_properties.empty()) { - // a bit hacky but easier for now #FIXME - auto event = Message::newEvent(Message::Command::ObjectSetProperty); - event->write(interface->handle()); - event->write(it.first); - - switch(it.second.type()) - { - case QVariant::Bool: - event->write(ValueType::Boolean); - event->write(it.second.toBool()); - break; - - case QVariant::Int: - case QVariant::UInt: - case QVariant::LongLong: - case QVariant::ULongLong: - event->write(ValueType::Integer); - event->write(it.second.toLongLong()); - break; - - case QVariant::Double: - event->write(ValueType::Float); - event->write(it.second.toDouble()); - break; - - case QVariant::String: - event->write(ValueType::String); - event->write(it.second.toString().toUtf8()); - break; - - default: /*[[unlikely]]*/ - assert(false); - continue; - } - interface->connection()->send(event); + m_promise.reportFinished(&m_interface); + } + else + { + m_setObjectProperties = std::make_shared(m_interface, m_properties); + if(m_setObjectProperties->future().isFinished()) + { + m_promise.reportFinished(&m_interface); + } + else + { + m_setObjectPropertiesFutureWatcher = std::make_unique>(); + m_setObjectPropertiesFutureWatcher->setFuture(m_setObjectProperties->future()); + QObject::connect(m_setObjectPropertiesFutureWatcher.get(), &QFutureWatcher::finished, + [this]() + { + if(!m_canceled) + { + m_promise.reportFinished(&m_interface); + } + }); + } } - m_promise.reportFinished(&interface); } else { @@ -127,6 +117,10 @@ CreateInterface::~CreateInterface() { cancel(); } + if(m_requestId != Connection::invalidRequestId) + { + m_world->connection()->cancelRequest(m_requestId); + } } void CreateInterface::cancel() diff --git a/client/src/network/create/createinterface.hpp b/client/src/network/create/createinterface.hpp index 40a5bfe2..3f7536fe 100644 --- a/client/src/network/create/createinterface.hpp +++ b/client/src/network/create/createinterface.hpp @@ -24,25 +24,26 @@ #define TRAINTASTIC_CLIENT_NETWORK_CREATE_CREATEINTERFACE_HPP #include -#include -#include -#include #include #include +#include +#include "properties.hpp" #include "../objectptr.hpp" +class SetObjectProperties; + class CreateInterface : public std::enable_shared_from_this { - public: - using Properties = std::vector>; - private: QFutureInterface m_promise; ObjectPtr m_world; ObjectPtr m_interfaceList; + ObjectPtr m_interface; QString m_classId; Properties m_properties; int m_requestId; + std::shared_ptr m_setObjectProperties; + std::unique_ptr> m_setObjectPropertiesFutureWatcher; bool m_canceled = false; public: diff --git a/client/src/network/create/properties.hpp b/client/src/network/create/properties.hpp new file mode 100644 index 00000000..9ebe95bc --- /dev/null +++ b/client/src/network/create/properties.hpp @@ -0,0 +1,33 @@ +/** + * client/src/network/create/properties.hpp + * + * This file is part of the traintastic source code. + * + * Copyright (C) 2024 Reinder Feenstra + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef TRAINTASTIC_CLIENT_NETWORK_CREATE_PROPERTIES_HPP +#define TRAINTASTIC_CLIENT_NETWORK_CREATE_PROPERTIES_HPP + +#include +#include +#include +#include + +using Properties = std::vector>; + +#endif diff --git a/client/src/network/create/setobjectproperties.cpp b/client/src/network/create/setobjectproperties.cpp new file mode 100644 index 00000000..4630c4cb --- /dev/null +++ b/client/src/network/create/setobjectproperties.cpp @@ -0,0 +1,120 @@ +/** + * client/src/network/create/setobjectproperties.cpp + * + * This file is part of the traintastic source code. + * + * Copyright (C) 2024 Reinder Feenstra + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "setobjectproperties.hpp" +#include "../connection.hpp" +#include "../error.hpp" +#include "../object.hpp" +#include "../abstractproperty.hpp" +#include "../objectproperty.hpp" + +SetObjectProperties::SetObjectProperties(ObjectPtr object, Properties properties) + : m_object{std::move(object)} + , m_properties{std::move(properties)} + , m_requestId{Connection::invalidRequestId} +{ + setProperties(); +} + +SetObjectProperties::SetObjectProperties(ObjectProperty& objectProperty, Properties properties) + : m_objectProperty{&objectProperty} + , m_properties{std::move(properties)} +{ + m_requestId = m_objectProperty->getObject( + [this](const ObjectPtr& object, std::optional /*error*/) + { + m_requestId = Connection::invalidRequestId; + if(!m_canceled) + { + m_object = object; + setProperties(); + } + }); +} + +SetObjectProperties::~SetObjectProperties() +{ + if(!m_promise.isFinished() && !m_canceled) + { + cancel(); + } + if(m_requestId != Connection::invalidRequestId) + { + m_objectProperty->object().connection()->cancelRequest(m_requestId); + } +} + +void SetObjectProperties::cancel() +{ + m_canceled = true; + if(!m_promise.isFinished()) + { + m_promise.reportCanceled(); + } +} + +void SetObjectProperties::setProperties() +{ + assert(m_object); + + std::map subObjects; + + for(const auto& it : m_properties) + { + if(int pos = it.first.indexOf(QChar('.')); pos >= 0) // property of sub object + { + qDebug() << it.first << it.first.left(pos); + auto* objectProperty = m_object->getObjectProperty(it.first.left(pos)); + if(objectProperty) + { + subObjects[objectProperty].emplace_back(it.first.mid(pos + 1), it.second); + } + } + else if(auto* property = m_object->getProperty(it.first)) + { + qDebug() << it.first << it.second; + property->setValueVariant(it.second); + } + } + + if(subObjects.empty()) // we're done + { + m_promise.reportFinished(&m_object); + } + else + { + for(const auto& it : subObjects) + { + m_setObjectProperties.emplace_back(std::make_shared(*it.first, it.second)); + m_setObjectPropertiesFutureWatcher.emplace_back(std::make_unique>()); + m_setObjectPropertiesFutureWatcher.back()->setFuture(m_setObjectProperties.back()->future()); + QObject::connect(m_setObjectPropertiesFutureWatcher.back().get(), &QFutureWatcher::finished, + [this]() + { + if(!m_canceled && ++m_setObjectPropertiesFinishedCount == m_setObjectProperties.size()) + { + m_promise.reportFinished(&m_object); + } + }); + } + } +} diff --git a/client/src/network/create/setobjectproperties.hpp b/client/src/network/create/setobjectproperties.hpp new file mode 100644 index 00000000..c2164924 --- /dev/null +++ b/client/src/network/create/setobjectproperties.hpp @@ -0,0 +1,63 @@ +/** + * client/src/network/create/setbjectproperties.hpp + * + * This file is part of the traintastic source code. + * + * Copyright (C) 2024 Reinder Feenstra + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef TRAINTASTIC_CLIENT_NETWORK_CREATE_SETOBJECTPROPERTIES_HPP +#define TRAINTASTIC_CLIENT_NETWORK_CREATE_SETOBJECTPROPERTIES_HPP + +#include +#include +#include +#include +#include "properties.hpp" +#include "../objectptr.hpp" + +class ObjectProperty; + +class SetObjectProperties : public std::enable_shared_from_this +{ + private: + QFutureInterface m_promise; + ObjectPtr m_object; + ObjectProperty* m_objectProperty = nullptr; + Properties m_properties; + int m_requestId; + std::vector> m_setObjectProperties; + std::vector>> m_setObjectPropertiesFutureWatcher; + size_t m_setObjectPropertiesFinishedCount = 0; + bool m_canceled = false; + + void setProperties(); + + public: + SetObjectProperties(ObjectPtr object, Properties properties); + SetObjectProperties(ObjectProperty& objectProperty, Properties properties); + ~SetObjectProperties(); + + void cancel(); + + QFuture future() + { + return m_promise.future(); + } +}; + +#endif diff --git a/client/src/wizard/jsonwizard.cpp b/client/src/wizard/jsonwizard.cpp index eb316334..2fcc880c 100644 --- a/client/src/wizard/jsonwizard.cpp +++ b/client/src/wizard/jsonwizard.cpp @@ -53,15 +53,15 @@ static void setTitleAndText(JSONWizard& wizard, TextPage* page, const QJsonObjec page->setText(wizard.translateAndReplaceVariables(object["text"].toString())); } -static CreateInterface::Properties toProperties(const QJsonObject& object) +static Properties toProperties(const QJsonObject& object) { - CreateInterface::Properties properties; + Properties properties; for(const auto& key : object.keys()) { auto value = object[key]; if(value.isBool() || value.isDouble() || value.isString()) { - properties.emplace_back(key.toStdString(), value.toVariant()); + properties.emplace_back(key, value.toVariant()); } else {