diff --git a/CMakeLists.txt b/CMakeLists.txt index 89858132..8fb0735d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -233,6 +233,8 @@ set(PROJECT_HEADERS ${PROJECT_SOURCE_DIR}/include/${PROJECT_NAME}/model/statistics/EntityItem.h ${PROJECT_SOURCE_DIR}/include/${PROJECT_NAME}/model/SubListedListItem.h ${PROJECT_SOURCE_DIR}/include/${PROJECT_NAME}/model/SubListedListModel.h + ${PROJECT_SOURCE_DIR}/include/${PROJECT_NAME}/model/tree/StatusTreeItem.h + ${PROJECT_SOURCE_DIR}/include/${PROJECT_NAME}/model/tree/StatusTreeModel.h ${PROJECT_SOURCE_DIR}/include/${PROJECT_NAME}/model/tree/TreeItem.h ${PROJECT_SOURCE_DIR}/include/${PROJECT_NAME}/model/tree/TreeModel.h ${PROJECT_SOURCE_DIR}/include/${PROJECT_NAME}/statistics/DataChartBox.h @@ -261,6 +263,8 @@ set(PROJECT_SOURCES_NO_MAIN ${PROJECT_SOURCE_DIR}/src/model/physical/ProcessModelItem.cpp ${PROJECT_SOURCE_DIR}/src/model/statistics/EntityItem.cpp ${PROJECT_SOURCE_DIR}/src/model/SubListedListModel.cpp + ${PROJECT_SOURCE_DIR}/src/model/tree/StatusTreeItem.cpp + ${PROJECT_SOURCE_DIR}/src/model/tree/StatusTreeModel.cpp ${PROJECT_SOURCE_DIR}/src/model/tree/TreeItem.cpp ${PROJECT_SOURCE_DIR}/src/model/tree/TreeModel.cpp ${PROJECT_SOURCE_DIR}/src/statistics/DataChartBox.cpp diff --git a/fastdds_monitor.pro b/fastdds_monitor.pro index f96cf855..869a54d4 100644 --- a/fastdds_monitor.pro +++ b/fastdds_monitor.pro @@ -24,6 +24,8 @@ SOURCES += \ src/model/physical/ProcessModelItem.cpp \ src/model/statistics/EntityItem.cpp \ src/model/SubListedListModel.cpp \ + src/model/tree/StatusTreeItem.cpp \ + src/model/tree/StatusTreeModel.cpp \ src/model/tree/TreeItem.cpp \ src/model/tree/TreeModel.cpp \ src/statistics/DataChartBox.cpp \ @@ -75,6 +77,8 @@ HEADERS += \ include/fastdds_monitor/model/statistics/EntityItem.h \ include/fastdds_monitor/model/SubListedListItem.h \ include/fastdds_monitor/model/SubListedListModel.h \ + include/fastdds_monitor/model/tree/StatusTreeModel.h \ + include/fastdds_monitor/model/tree/StatusTreeItem.h \ include/fastdds_monitor/model/tree/TreeItem.h \ include/fastdds_monitor/model/tree/TreeModel.h \ include/fastdds_monitor/statistics/DataChartBox.h \ diff --git a/include/fastdds_monitor/Controller.h b/include/fastdds_monitor/Controller.h index 17f61ace..220b3bb7 100644 --- a/include/fastdds_monitor/Controller.h +++ b/include/fastdds_monitor/Controller.h @@ -26,6 +26,8 @@ #include <QObject> #include <QtCharts/QVXYModelMapper> +#include <fastdds_monitor/backend/backend_types.h> + class Engine; enum class ErrorType : int @@ -59,6 +61,15 @@ class Controller : public QObject QString error_msg, ErrorType error_type = ErrorType::GENERIC); + //! Status counters displayed in the QML + struct StatusCounters + { + std::map<backend::EntityId,uint32_t> errors; + std::map<backend::EntityId,uint32_t> warnings; + int32_t total_errors = 0; + int32_t total_warnings = 0; + } status_counters; + public slots: // Methods to be called from QML @@ -275,6 +286,9 @@ public slots: //! Signal to inform qml that a new monitor has been initialized void monitorInitialized(); + //! Signal to notify status counters have been updated + void update_status_counters(QString errors, QString warnings); + protected: //! Reference to \c Engine object diff --git a/include/fastdds_monitor/Engine.h b/include/fastdds_monitor/Engine.h index 129b6d95..d55cf86e 100644 --- a/include/fastdds_monitor/Engine.h +++ b/include/fastdds_monitor/Engine.h @@ -32,7 +32,7 @@ #include <QWaitCondition> #include <fastdds_monitor/backend/Callback.h> -#include <fastdds_monitor/backend/Listener.h> +#include <fastdds_monitor/backend/StatusCallback.h> #include <fastdds_monitor/backend/Listener.h> #include <fastdds_monitor/backend/SyncBackendConnection.h> #include <fastdds_monitor/Controller.h> @@ -40,6 +40,7 @@ #include <fastdds_monitor/model/info/InfoModel.h> #include <fastdds_monitor/statistics/dynamic/DynamicStatisticsData.h> #include <fastdds_monitor/statistics/historic/HistoricStatisticsData.h> +#include <fastdds_monitor/model/tree/StatusTreeModel.h> struct EntityClicked { @@ -267,6 +268,26 @@ class Engine : public QQmlApplicationEngine bool new_entity = true, bool last_clicked = false); + /** + * @brief Update the entity status model with the status kind received + * + * @param id entity id + * @param kind StatusKind reported + * @return true if any change in model has been done + */ + bool update_entity_status( + const backend::EntityId& id, + backend::StatusKind kind); + + /** + * @brief Update the entity status counters and populate the model with empty message if empty + * + * @param id entity id + * @return false + */ + bool remove_inactive_entities_from_status_model( + const backend::EntityId& id); + /** * @brief Update the internal dds model with entities related with Entity referenced by \c id * @@ -348,6 +369,19 @@ class Engine : public QQmlApplicationEngine bool add_callback( backend::Callback callback); + /** + * @brief add a status callback arrived from the backend to the status callback queue + * + * Add a status callback to the status callback queue in order to process it afterwards by the main thread. + * Emit a signal that communicate the main thread that there are info to process in the status callback queue. + * Add a status callback issue. + * + * @param callback new status callback to add + * @return true + */ + bool add_callback( + backend::StatusCallback callback); + /** * @brief Refresh the view * @@ -390,6 +424,14 @@ class Engine : public QQmlApplicationEngine */ void process_callback_queue(); + /** + * @brief Pop status callbacks from the status callback queues while non empty and update the models + * + * @warning This method must be executed from the main Thread (or at least a QThread) so the models are + * updated in the view when modified. + */ + void process_status_callback_queue(); + //! Refresh summary panel void refresh_summary(); @@ -507,14 +549,26 @@ class Engine : public QQmlApplicationEngine */ void new_callback_signal(); + /** + * Internal signal that communicate that there are status callbacks to process by the main Thread. + * Arise from \c add_callback + */ + void new_status_callback_signal(); + public slots: /** - * Receive the internal signal \c new_callback_signal and start the process of - * callback queue by \c process_callback_queue + * Receive the internal signal \c new_callback_signal and start the process of callback + * queue by \c process_callback_queue */ void new_callback_slot(); + /** + * Receive the internal signal \c new_status_callback_signal and start the process of status + * callback queue by \c process_status_callback_queue + */ + void new_status_callback_slot(); + protected: /** @@ -633,13 +687,23 @@ public slots: //! True if there are callbacks in the callback queue bool are_callbacks_to_process_(); + //! True if there are status callbacks in the callback queue + bool are_status_callbacks_to_process_(); + //! Pop a callback from callback queues and call \c read_callback for that callback bool process_callback_(); + //! Pop a status callback from callback queues and call \c read_callback for that status callback + bool process_status_callback_(); + //! Update the model concerned by the entity in the callback bool read_callback_( backend::Callback callback); + //! Update the model concerned by the entity in the status callback + bool read_callback_( + backend::StatusCallback callback); + //! Common method to demultiplex to update functions depending on the entity kind bool update_entity_generic( backend::EntityId entity_id, @@ -695,6 +759,12 @@ public slots: //! Data that is represented in the Status Model when this model is refreshed backend::Info status_info_; + //! Data Model for Fast DDS Monitor status view. Collects all entities statuses detected by the monitor service + models::StatusTreeModel* entity_status_model_; + + //! Display and allow to filter Model for Fast DDS Monitor status view. + models::StatusTreeModel* entity_status_proxy_model_; + //! TODO models::ListModel* source_entity_id_model_; @@ -722,9 +792,15 @@ public slots: //! Mutex to protect \c callback_queue_ std::recursive_mutex callback_queue_mutex_; + //! Mutex to protect \c status_callback_queue_ + std::recursive_mutex status_callback_queue_mutex_; + //! Queue of Callbacks that have arrived by the \c Listener and have not been processed QQueue<backend::Callback> callback_queue_; + //! Queue of status Callbacks that have arrived by the \c Listener and have not been processed + QQueue<backend::StatusCallback> status_callback_queue_; + //! Object that manage all the communications with the QML view Controller* controller_; @@ -746,6 +822,9 @@ public slots: * to happen) there are going to create entities already created. */ std::recursive_mutex initializing_monitor_; + + //! All status log + backend::Info status_status_log_; }; #endif // _EPROSIMA_FASTDDS_MONITOR_ENGINE_H diff --git a/include/fastdds_monitor/backend/Listener.h b/include/fastdds_monitor/backend/Listener.h index 459509c4..be61fdd5 100644 --- a/include/fastdds_monitor/backend/Listener.h +++ b/include/fastdds_monitor/backend/Listener.h @@ -91,6 +91,12 @@ class Listener : public PhysicalListener EntityId datawriter_id, const Status& status) override; + //! Callback when a status is reported + void on_status_reported( + EntityId domain_id, + EntityId entity_id, + StatusKind data_kind) override; + protected: //! Engine reference diff --git a/include/fastdds_monitor/backend/StatusCallback.h b/include/fastdds_monitor/backend/StatusCallback.h new file mode 100644 index 00000000..cffdc273 --- /dev/null +++ b/include/fastdds_monitor/backend/StatusCallback.h @@ -0,0 +1,60 @@ +// Copyright 2023 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// This file is part of eProsima Fast DDS Monitor. +// +// eProsima Fast DDS Monitor 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 3 of the License, or +// (at your option) any later version. +// +// eProsima Fast DDS Monitor 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 eProsima Fast DDS Monitor. If not, see <https://www.gnu.org/licenses/>. + +/** + * @file StatusCallback.h + */ + +#ifndef _EPROSIMA_FASTDDS_MONITOR_BACKEND_STATUS_CALLBACK_H +#define _EPROSIMA_FASTDDS_MONITOR_BACKEND_STATUS_CALLBACK_H + +#include <fastdds_monitor/backend/backend_types.h> + +namespace backend { + +/* + * Struct that store the status callback information required by the GUI. + * It encapsulates the domain id, entity id and the kind of the new status reported. + */ +struct StatusCallback +{ + //! Void constructor to use copy constructor afterwards + StatusCallback() + { + } + + //! Standard constructor with the two fields required + StatusCallback( + backend::EntityId domain_entity_id, + backend::EntityId entity_id, + backend::StatusKind status_kind) + : domain_entity_id(domain_entity_id) + , entity_id(entity_id) + , status_kind(status_kind) + { + } + //! Information of the domain \c EntityId the callback refers + backend::EntityId domain_entity_id; + //! Information of the \c EntityId the callback refers + backend::EntityId entity_id; + //! Information of the \c StatusKind the callback refers + backend::StatusKind status_kind; +}; + +} // namespace backend + +#endif // _EPROSIMA_FASTDDS_MONITOR_BACKEND_STATUS_CALLBACK_H diff --git a/include/fastdds_monitor/backend/SyncBackendConnection.h b/include/fastdds_monitor/backend/SyncBackendConnection.h index ee4ab907..4b2e479a 100644 --- a/include/fastdds_monitor/backend/SyncBackendConnection.h +++ b/include/fastdds_monitor/backend/SyncBackendConnection.h @@ -23,6 +23,7 @@ #define _EPROSIMA_FASTDDS_MONITOR_BACKEND_SYNCBACKENDCONNECTION_H #include <fastdds_statistics_backend/StatisticsBackend.hpp> +#include <fastdds_statistics_backend/types/types.hpp> #include <fastdds_monitor/backend/backend_types.h> #include <fastdds_monitor/backend/Listener.h> @@ -116,6 +117,14 @@ class SyncBackendConnection std::string get_name( backend::EntityId id); + //! Get the alias of an entity from the Backend by calling \c get_info + std::string get_alias( + backend::EntityId id); + + //! Get the status level of an entity from the Backend by calling \c get_status + StatusLevel get_status( + backend::EntityId id); + //! Get the alive status of an entity from the Backend by calling \c is_active bool get_alive( backend::EntityId id); @@ -147,6 +156,42 @@ class SyncBackendConnection Timestamp start_time = Timestamp(), Timestamp end_time = std::chrono::system_clock::now()); + bool get_status_data( + EntityId source_entity_id, + ConnectionListSample& sample); + + bool get_status_data( + EntityId source_entity_id, + DeadlineMissedSample& sample); + + bool get_status_data( + EntityId source_entity_id, + IncompatibleQosSample& sample); + + bool get_status_data( + EntityId source_entity_id, + InconsistentTopicSample& sample); + + bool get_status_data( + EntityId source_entity_id, + LivelinessChangedSample& sample); + + bool get_status_data( + EntityId source_entity_id, + LivelinessLostSample& sample); + + bool get_status_data( + EntityId source_entity_id, + ProxySample& sample); + + bool get_status_data( + EntityId source_entity_id, + SampleLostSample& sample); + + /*bool get_status_data( + EntityId source_entity_id, + StatusesSizeSample& sample);*/ + //! Get info from an entity from the Backend std::vector<EntityId> get_entities( EntityKind entity_type, diff --git a/include/fastdds_monitor/backend/backend_types.h b/include/fastdds_monitor/backend/backend_types.h index ec5753d8..72b3bb32 100644 --- a/include/fastdds_monitor/backend/backend_types.h +++ b/include/fastdds_monitor/backend/backend_types.h @@ -26,6 +26,7 @@ #include <QString> #include <fastdds_statistics_backend/types/types.hpp> +#include <fastdds_statistics_backend/types/JSONTags.h> namespace backend { @@ -33,10 +34,23 @@ namespace backend { using EntityId = eprosima::statistics_backend::EntityId; using EntityKind = eprosima::statistics_backend::EntityKind; using DataKind = eprosima::statistics_backend::DataKind; +using StatusKind = eprosima::statistics_backend::StatusKind; +using StatusLevel = eprosima::statistics_backend::StatusLevel; using StatisticKind = eprosima::statistics_backend::StatisticKind; using EntityInfo = eprosima::statistics_backend::Info; using Timestamp = eprosima::statistics_backend::Timestamp; +// Status types from backend +using ConnectionListSample = eprosima::statistics_backend::ConnectionListSample; +using DeadlineMissedSample = eprosima::statistics_backend::DeadlineMissedSample; +using IncompatibleQosSample = eprosima::statistics_backend::IncompatibleQosSample; +using InconsistentTopicSample = eprosima::statistics_backend::InconsistentTopicSample; +using LivelinessChangedSample = eprosima::statistics_backend::LivelinessChangedSample; +using LivelinessLostSample = eprosima::statistics_backend::LivelinessLostSample; +using ProxySample = eprosima::statistics_backend::ProxySample; +using SampleLostSample = eprosima::statistics_backend::SampleLostSample; +//using StatusesSizeSample = eprosima::statistics_backend::StatusesSizeSample; + //! Reference the ID_ALL in the project extern const EntityId ID_ALL; //! Reference the ID_NONE in the project diff --git a/include/fastdds_monitor/backend/backend_utils.h b/include/fastdds_monitor/backend/backend_utils.h index 009e6c03..5418208d 100644 --- a/include/fastdds_monitor/backend/backend_utils.h +++ b/include/fastdds_monitor/backend/backend_utils.h @@ -87,6 +87,14 @@ std::string data_kind_to_string( std::string statistic_kind_to_string( const StatisticKind& statistic_kind); +//! Converts the \c StatusKind to string +std::string status_kind_to_string( + const StatusKind& status_kind); + +//! Converts the \c StatusLevel to string +std::string status_level_to_string( + const StatusLevel& status_level); + //! Retrieves the \c EntityKind related with its name in QString backend::EntityKind string_to_entity_kind( const QString& entity_kind); @@ -99,7 +107,6 @@ backend::DataKind string_to_data_kind( backend::StatisticKind string_to_statistic_kind( const QString& statistic_kind); - //! recursive function to convert array json subelements to dictionaries indexed by numbers backend::EntityInfo refactor_json( backend::EntityInfo json_data); @@ -108,6 +115,15 @@ backend::EntityInfo refactor_json( std::string timestamp_to_string( const backend::Timestamp timestamp); +std::string policy_id_to_string( + const uint32_t& id); + +std::string entity_status_description( + const backend::StatusKind kind); + +std::string policy_documentation_description( + const uint32_t& id); + } //namespace backend #endif // _EPROSIMA_FASTDDS_MONITOR_BACKEND_BACKENDUTILS_H diff --git a/include/fastdds_monitor/model/tree/StatusTreeItem.h b/include/fastdds_monitor/model/tree/StatusTreeItem.h new file mode 100644 index 00000000..89caa2de --- /dev/null +++ b/include/fastdds_monitor/model/tree/StatusTreeItem.h @@ -0,0 +1,172 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2021 Maurizio Ingrassia + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +// Copyright 2023 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// This file is part of eProsima Fast DDS Monitor. +// +// eProsima Fast DDS Monitor 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 3 of the License, or +// (at your option) any later version. +// +// eProsima Fast DDS Monitor 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 eProsima Fast DDS Monitor. If not, see <https://www.gnu.org/licenses/>. + +#ifndef _EPROSIMA_FASTDDS_MONITOR_MODEL_TREE_StatusTreeItem_H +#define _EPROSIMA_FASTDDS_MONITOR_MODEL_TREE_StatusTreeItem_H + +#include <QVariant> +#include <fastdds_monitor/backend/backend_types.h> + +namespace models { + +/*! + * This class represents a node of the StatusTreeModel. + * The items are meant to be managed from the StatusTreeModel, thus is only allowed + * to modify the stored data. + * Parenting and deletion are dealt from the StatusTreeModel. Deleting a StatusTreeItem + * will call the delete for each child node. + */ +class StatusTreeItem +{ + friend class StatusTreeModel; + +public: + + //! Create an empty item. + StatusTreeItem(); + + //! Create an item with the given data. + explicit StatusTreeItem( + const QVariant& data); + + //! Create an Entity item / top level item + explicit StatusTreeItem( + const backend::EntityId& id, + const std::string& name, + const backend::StatusLevel& status_level, + const std::string& description); + + //! Create an item with the status parameters + explicit StatusTreeItem( + const backend::EntityId& id, + const backend::StatusKind& kind, + const std::string& name, + const backend::StatusLevel& status_level, + const std::string& value, + const std::string& description); + + //! Destroy the item. It will destroy every child. + ~StatusTreeItem(); + + const QVariant& entity_id() const; + + const QVariant& status_kind() const; + + const QVariant& name() const; + + const QVariant& status() const; + + const QVariant& value() const; + + const QVariant& description() const; + + const QVariant& alive() const; + + //! Set the internal data of the node. + void setData( + const QVariant& data); + + //! Return the number of children nodes. + int childCount() const; + + int row() const; + + //! Return true if the node is a leaf node (no children). + bool isLeaf() const; + + //! Return the depth of this node inside the tree. + int depth() const; + + StatusTreeItem* child( + int row); + + backend::EntityId id(); + + backend::StatusKind kind(); + + backend::StatusLevel status_level(); + + void status_level( + backend::StatusLevel val); + + std::string name_str(); + + std::string value_str(); + + std::string description_str(); + + //! Increases the issues counter of a top level entity item + int recalculate_entity_counter(); + +private: + StatusTreeItem* parentItem(); + + void setParentItem( + StatusTreeItem* parentItem); + + void appendChild( + StatusTreeItem* item); + + void removeChild( + StatusTreeItem* item); + +private: + StatusTreeItem* parent_item_; + QVector<StatusTreeItem*> child_items_; + backend::EntityId id_; + backend::StatusKind kind_; + std::string name_; + backend::StatusLevel status_level_; + std::string value_; + std::string description_; + bool is_active_; + QVariant id_variant_; + QVariant kind_variant_; + QVariant name_variant_; + QVariant status_level_variant_; + QVariant value_variant_; + QVariant description_variant_; + QVariant is_active_variant_; +}; + +} // namespace models + +#endif // _EPROSIMA_FASTDDS_MONITOR_MODEL_TREE_StatusTreeItem_H diff --git a/include/fastdds_monitor/model/tree/StatusTreeModel.h b/include/fastdds_monitor/model/tree/StatusTreeModel.h new file mode 100644 index 00000000..1305b240 --- /dev/null +++ b/include/fastdds_monitor/model/tree/StatusTreeModel.h @@ -0,0 +1,207 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2021 Maurizio Ingrassia + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +// Copyright 2023 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// This file is part of eProsima Fast DDS Monitor. +// +// eProsima Fast DDS Monitor 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 3 of the License, or +// (at your option) any later version. +// +// eProsima Fast DDS Monitor 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 eProsima Fast DDS Monitor. If not, see <https://www.gnu.org/licenses/>. + +#ifndef _EPROSIMA_FASTDDS_MONITOR_MODEL_TREE_StatusTreeModel_H +#define _EPROSIMA_FASTDDS_MONITOR_MODEL_TREE_StatusTreeModel_H + +#include <fastdds_monitor/model/tree/StatusTreeItem.h> + +#include <QAbstractItemModel> + +namespace models { + +/*! + * The Tree Model works as List Model, using one column and using the row information + * referred to the parent node. + */ +class StatusTreeModel : public QAbstractItemModel +{ + Q_OBJECT + +public: + explicit StatusTreeModel( + QObject* parent = nullptr); + + ~StatusTreeModel() override; + + // Overriden method from QAbstractItemModel + +public: + int rowCount( + const QModelIndex& index) const override; + int columnCount( + const QModelIndex& index) const override; + + QModelIndex index( + int row, + int column, + const QModelIndex& parent) const override; + + QModelIndex parent( + const QModelIndex& childIndex) const override; + + QVariant data( + const QModelIndex& index, + int role = 0) const override; + + Q_INVOKABLE QVariant name( + const QModelIndex& index, + int role = 0) const; + + Q_INVOKABLE QVariant id( + const QModelIndex& index, + int role = 0) const; + + Q_INVOKABLE QVariant status( + const QModelIndex& index, + int role = 0) const; + + Q_INVOKABLE QVariant kind( + const QModelIndex& index, + int role = 0) const; + + Q_INVOKABLE QVariant value( + const QModelIndex& index, + int role = 0) const; + + Q_INVOKABLE QVariant description( + const QModelIndex& index, + int role = 0) const; + + Q_INVOKABLE QVariant alive( + const QModelIndex& index, + int role = 0) const; + + bool setData( + const QModelIndex& index, + const QVariant& value, + int role = Qt::EditRole) override; + + bool removeRow( + int row, + const QModelIndex &index = QModelIndex()); + +public: + //! Add an item to the top level. + void addTopLevelItem( + StatusTreeItem* child); + + //! Add a child to the parent item. + void addItem( + StatusTreeItem* parent, + StatusTreeItem* child); + + //! Remove the item from the model. + void removeItem( + StatusTreeItem* item); + + //! Return the root item of the model. + StatusTreeItem* rootItem() const; + + //! Return the depth for the given index + Q_INVOKABLE int depth( + const QModelIndex& index) const; + + //! Clear the model. + Q_INVOKABLE void clear(); + + /*! + * Return the root item to the QML Side. + * This method is not meant to be used in client code. + */ + Q_INVOKABLE QModelIndex rootIndex(); + + // Check if top level item is defined + bool containsTopLevelItem( + StatusTreeItem* child); + + // Check if item is defined in the parent item + bool contains( + StatusTreeItem* parent, + StatusTreeItem* child); + + // Returns the child in the given position + StatusTreeItem* child( + int row); + + // Check if default empty value is the only element + bool is_empty(); + + void removeEmptyItem(); + + //! Looks for a TopLevelItem that matches that id. If not existing, creates new one and returns it + StatusTreeItem* getTopLevelItem( + const backend::EntityId& id, + const std::string& data, + const backend::StatusLevel& status, + const std::string& description); + + void set_source_model( + StatusTreeModel* source_model); + + /*! + * Filters the model if it is defined as proxy + */ + Q_INVOKABLE void filter_proxy( + const QVariant& entity_id); + +private: + StatusTreeItem* internalPointer( + const QModelIndex& index) const; + + StatusTreeItem* filtered_copy( + StatusTreeItem* source, + const backend::EntityId entity_id); + + void filter( + const backend::EntityId entity_id); + +private: + StatusTreeModel* source_model_; + StatusTreeItem* root_item_; + bool is_empty_; + + backend::EntityId current_filter_; +}; + +} // namespace models + +#endif // _EPROSIMA_FASTDDS_MONITOR_MODEL_TREE_StatusTreeModel_H diff --git a/mock/complex_mock/database/Database.cpp b/mock/complex_mock/database/Database.cpp index d0afb7f6..1dffb6ee 100644 --- a/mock/complex_mock/database/Database.cpp +++ b/mock/complex_mock/database/Database.cpp @@ -360,8 +360,12 @@ void Database::callback_listener_thread_() default: break; } + + // add status callback + listener_->on_status_reported(std::get<2>(entity), std::get<0>(entity), StatusKind::PROXY); } } + } std::cout << "DATABASE Callback Listener Thread stopping" << std::endl; diff --git a/qml.qrc b/qml.qrc index 22715c9e..8eb72d0b 100644 --- a/qml.qrc +++ b/qml.qrc @@ -53,6 +53,8 @@ <file>qml/MonitorToolBarButton.qml</file> <file>qml/Panels.qml</file> <file>qml/PhysicalView.qml</file> + <file>qml/StatusTreeView.qml</file> + <file>qml/StatusTreeViewItem.qml</file> <file>qml/QosView.qml</file> <file>qml/ScheduleClearDialog.qml</file> <file>qml/SeriesSetMaxPointsDialog.qml</file> @@ -60,9 +62,11 @@ <file>qml/StatisticsChartBox.qml</file> <file>qml/StatisticsChartView.qml</file> <file>qml/StatusPanel.qml</file> + <file>qml/StatusLayout.qml</file> <file>qml/StatusView.qml</file> <file>qml/SummaryView.qml</file> <file>qml/TabLayout.qml</file> + <file>qml/TopicMenu.qml</file> <file>qtquickcontrols2.conf</file> </qresource> <qresource prefix="/resources/images"> @@ -70,6 +74,8 @@ <file alias="eprosima_logo.ico">resources/images/eprosima_logo.ico</file> <file alias="fastdds-monitor-logo.png">resources/images/fastdds-monitor-logo.png</file> <file alias="graphs.svg">resources/images/graphs.svg</file> + <file alias="domain_graph.svg">resources/images/domain_graph.svg</file> + <file alias="topic_graph.svg">resources/images/topic_graph.svg</file> </qresource> <qresource prefix="/resources/images/icons"> <file alias="clearissues_black.svg">resources/images/icons/clearissues/clearissues_black.svg</file> @@ -80,10 +86,15 @@ <file alias="clearlog_eProsimaLightBlue.svg">resources/images/icons/clearlog/clearlog_eProsimaLightBlue.svg</file> <file alias="clearlog_grey.svg">resources/images/icons/clearlog/clearlog_grey.svg</file> <file alias="clearlog_white.svg">resources/images/icons/clearlog/clearlog_white.svg</file> + <file alias="collapse_black.svg">resources/images/icons/collapse/collapse_black.svg</file> + <file alias="collapse_eProsimaLightBlue.svg">resources/images/icons/collapse/collapse_eProsimaLightBlue.svg</file> + <file alias="collapse_grey.svg">resources/images/icons/collapse/collapse_grey.svg</file> + <file alias="collapse_white.svg">resources/images/icons/collapse/collapse_white.svg</file> <file alias="cross_black.svg">resources/images/icons/cross/cross_black.svg</file> <file alias="cross_eProsimaLightBlue.svg">resources/images/icons/cross/cross_eProsimaLightBlue.svg</file> <file alias="cross_grey.svg">resources/images/icons/cross/cross_grey.svg</file> <file alias="cross_white.svg">resources/images/icons/cross/cross_white.svg</file> + <file alias="cross_red.svg">resources/images/icons/cross/cross_red.svg</file> <file alias="datareader_black.svg">resources/images/icons/datareader/datareader_black.svg</file> <file alias="datareader_eProsimaLightBlue.svg">resources/images/icons/datareader/datareader_eProsimaLightBlue.svg</file> <file alias="datareader_grey.svg">resources/images/icons/datareader/datareader_grey.svg</file> @@ -104,10 +115,27 @@ <file alias="editaxis_eProsimaLightBlue.svg">resources/images/icons/editaxis/editaxis_eProsimaLightBlue.svg</file> <file alias="editaxis_grey.svg">resources/images/icons/editaxis/editaxis_grey.svg</file> <file alias="editaxis_white.svg">resources/images/icons/editaxis/editaxis_white.svg</file> + <file alias="error_black.svg">resources/images/icons/error/error_black.svg</file> + <file alias="error_eProsimaLightBlue.svg">resources/images/icons/error/error_eProsimaLightBlue.svg</file> + <file alias="error_grey.svg">resources/images/icons/error/error_grey.svg</file> + <file alias="error_white.svg">resources/images/icons/error/error_white.svg</file> + <file alias="error_red.svg">resources/images/icons/error/error_red.svg</file> + <file alias="expand_black.svg">resources/images/icons/expand/expand_black.svg</file> + <file alias="expand_eProsimaLightBlue.svg">resources/images/icons/expand/expand_eProsimaLightBlue.svg</file> + <file alias="expand_grey.svg">resources/images/icons/expand/expand_grey.svg</file> + <file alias="expand_white.svg">resources/images/icons/expand/expand_white.svg</file> <file alias="explorer_black.svg">resources/images/icons/explorer/explorer_black.svg</file> <file alias="explorer_eProsimaLightBlue.svg">resources/images/icons/explorer/explorer_eProsimaLightBlue.svg</file> <file alias="explorer_grey.svg">resources/images/icons/explorer/explorer_grey.svg</file> <file alias="explorer_white.svg">resources/images/icons/explorer/explorer_white.svg</file> + <file alias="filter_empty_black.svg">resources/images/icons/filter_empty/filter_empty_black.svg</file> + <file alias="filter_empty_eProsimaLightBlue.svg">resources/images/icons/filter_empty/filter_empty_eProsimaLightBlue.svg</file> + <file alias="filter_empty_grey.svg">resources/images/icons/filter_empty/filter_empty_grey.svg</file> + <file alias="filter_empty_white.svg">resources/images/icons/filter_empty/filter_empty_white.svg</file> + <file alias="filter_full_black.svg">resources/images/icons/filter_full/filter_full_black.svg</file> + <file alias="filter_full_eProsimaLightBlue.svg">resources/images/icons/filter_full/filter_full_eProsimaLightBlue.svg</file> + <file alias="filter_full_grey.svg">resources/images/icons/filter_full/filter_full_grey.svg</file> + <file alias="filter_full_white.svg">resources/images/icons/filter_full/filter_full_white.svg</file> <file alias="grid1_black.svg">resources/images/icons/grid1/grid1_black.svg</file> <file alias="grid1_eProsimaLightBlue.svg">resources/images/icons/grid1/grid1_eProsimaLightBlue.svg</file> <file alias="grid1_grey.svg">resources/images/icons/grid1/grid1_grey.svg</file> @@ -195,5 +223,21 @@ <file alias="help_eProsimaLightBlue.svg">resources/images/icons/help/help_eProsimaLightBlue.svg</file> <file alias="help_grey.svg">resources/images/icons/help/help_grey.svg</file> <file alias="help_white.svg">resources/images/icons/help/help_white.svg</file> + <file alias="left_arrow_black.svg">resources/images/icons/left_arrow/left_arrow_black.svg</file> + <file alias="left_arrow_eProsimaLightBlue.svg">resources/images/icons/left_arrow/left_arrow_eProsimaLightBlue.svg</file> + <file alias="left_arrow_grey.svg">resources/images/icons/left_arrow/left_arrow_grey.svg</file> + <file alias="left_arrow_white.svg">resources/images/icons/left_arrow/left_arrow_white.svg</file> + <file alias="right_arrow_black.svg">resources/images/icons/right_arrow/right_arrow_black.svg</file> + <file alias="right_arrow_eProsimaLightBlue.svg">resources/images/icons/right_arrow/right_arrow_eProsimaLightBlue.svg</file> + <file alias="right_arrow_grey.svg">resources/images/icons/right_arrow/right_arrow_grey.svg</file> + <file alias="right_arrow_white.svg">resources/images/icons/right_arrow/right_arrow_white.svg</file> + <file alias="rounded_left_arrow_black.svg">resources/images/icons/rounded_left_arrow/rounded_left_arrow_black.svg</file> + <file alias="rounded_left_arrow_eProsimaLightBlue.svg">resources/images/icons/rounded_left_arrow/rounded_left_arrow_eProsimaLightBlue.svg</file> + <file alias="rounded_left_arrow_grey.svg">resources/images/icons/rounded_left_arrow/rounded_left_arrow_grey.svg</file> + <file alias="rounded_left_arrow_white.svg">resources/images/icons/rounded_left_arrow/rounded_left_arrow_white.svg</file> + <file alias="rounded_right_arrow_black.svg">resources/images/icons/rounded_right_arrow/rounded_right_arrow_black.svg</file> + <file alias="rounded_right_arrow_eProsimaLightBlue.svg">resources/images/icons/rounded_right_arrow/rounded_right_arrow_eProsimaLightBlue.svg</file> + <file alias="rounded_right_arrow_grey.svg">resources/images/icons/rounded_right_arrow/rounded_right_arrow_grey.svg</file> + <file alias="rounded_right_arrow_white.svg">resources/images/icons/rounded_right_arrow/rounded_right_arrow_white.svg</file> </qresource> </RCC> diff --git a/qml/ChangeAliasDialog.qml b/qml/ChangeAliasDialog.qml index 3c0507c0..fc16d65e 100644 --- a/qml/ChangeAliasDialog.qml +++ b/qml/ChangeAliasDialog.qml @@ -32,6 +32,7 @@ Dialog { standardButtons: Dialog.Ok | Dialog.Cancel anchors.centerIn: Overlay.overlay + property var domainEntityId: "" property var entityId: "" property var currentAlias: "" property var entityKind: "" @@ -79,6 +80,7 @@ Dialog { } else { controller.set_alias(entityId, newSeriesNameTextField.text, entityKind) } + refreshDomainGraphView(domainEntityId, entityId) } } } diff --git a/qml/DomainGraphLayout.qml b/qml/DomainGraphLayout.qml index 5aadb1fa..0797f116 100644 --- a/qml/DomainGraphLayout.qml +++ b/qml/DomainGraphLayout.qml @@ -27,12 +27,14 @@ Item // Public properties property var model: {} // domain view graph JSON model - property int entity_id // entity id associated to the domain id + property int domain_entity_id // entity id associated to the domain id property int domain_id // domain id required property string component_id // mandatory to be included when object created // Public signals - signal update_tab_name(string new_name) // Update tab name based on selected domain id + signal update_tab_name(string new_name, string stack_id) // Update tab name based on selected domain id + signal openEntitiesMenu(string domainEntityId, string entityId, string currentAlias, string entityKind) + signal openTopicMenu(string domainEntityId, string domainId, string entityId, string currentAlias, string entityKind) // Private properties property var topic_locations_: {} // topic information needed for connection representation @@ -41,7 +43,8 @@ Item property var endpoint_painted_: [] // already painted endpoint connection references property var pending_endpoints_: [] // pending endpoints references that have not been resized yet property var pending_connections_: [] // pending connections references that have not been generated yet - property int entity_box_width_: 0 // entities box width management + property var filtered_topics_: [] // flitered topic entity id in the graph + property int entity_box_width_: 0 // entity box width management // Private (resize) signals The signal resize_elements_ will trigger all entities resize methods in // HOST TOPIC ─┐ the order displayed in the left figure. All entities width value are @@ -54,18 +57,20 @@ Item // Read only design properties (sizes and colors) readonly property int radius_: 10 - readonly property int connection_thickness_: 5 - readonly property int elements_spacing_: 12 + readonly property int connection_thickness_: 6 + readonly property int elements_spacing_: 5 readonly property int containers_spacing_: 100 - readonly property int endpoint_height_: 40 + readonly property int endpoint_height_: 30 readonly property int first_indentation_: 5 readonly property int icon_size_: 18 - readonly property int label_height_: 35 + readonly property int label_height_: 25 readonly property int spacing_icon_label_: 8 + readonly property int spacing_icon_: 4 readonly property int scrollbar_min_size_: 8 readonly property int scrollbar_max_size_: 12 readonly property int topic_thickness_: 10 readonly property int wheel_displacement_: 30 + readonly property int timer_initial_ms_interval_: 200 readonly property string topic_color_: Theme.grey readonly property string host_color_: Theme.darkGrey readonly property string user_color_: Theme.eProsimaLightBlue @@ -229,9 +234,14 @@ Item MouseArea { anchors.fill: parent + acceptedButtons: Qt.LeftButton | Qt.RightButton onClicked: { - controller.topic_click(modelData["id"]) + if(mouse.button & Qt.RightButton) { + openTopicMenu(domain_entity_id, domain_id, modelData["id"], modelData["alias"], modelData["kind"]) + } else { + controller.topic_click(modelData["id"]) + } } } } @@ -531,18 +541,20 @@ Item Rectangle { color: "transparent" - width: modelData["status"] != "OK" - ? first_indentation_ : 0 - } - IconSVG { - visible: modelData["status"] != "OK" - name: "issues" - color: "white" - size: modelData["status"] != "OK"? icon_size_ : 0 + width: first_indentation_ } Rectangle { - color: "transparent" - width: first_indentation_ /2 + visible: modelData["status"] != "OK" + color: modelData["status"] == "WARNING" ? "transparent" : "white" + width: modelData["status"] != "OK"? icon_size_ + spacing_icon_: 0 + height: modelData["status"] != "OK"? icon_size_ + spacing_icon_: 0 + radius: modelData["status"] != "OK"? icon_size_ + spacing_icon_: 0 + IconSVG { + anchors.centerIn: parent + name: modelData["status"] == "WARNING" ? "issues" : "error" + color: modelData["status"] == "WARNING" ? "black" : "red" + size: modelData["status"] != "OK"? icon_size_ : 0 + } } IconSVG { name: "host" @@ -558,9 +570,14 @@ Item MouseArea { anchors.fill: parent + acceptedButtons: Qt.LeftButton | Qt.RightButton onClicked: { - controller.host_click(modelData["id"]) + if(mouse.button & Qt.RightButton) { + openEntitiesMenu(domain_entity_id, modelData["id"], modelData["alias"], modelData["kind"]) + } else { + controller.host_click(modelData["id"]) + } } } } @@ -668,18 +685,20 @@ Item Rectangle { color: "transparent" - width: modelData["status"] != "OK" - ? first_indentation_ : 0 - } - IconSVG { - visible: modelData["status"] != "OK" - name: "issues" - color: "white" - size: modelData["status"] != "OK"? icon_size_ : 0 + width: first_indentation_ } Rectangle { - color: "transparent" - width: first_indentation_ /2 + visible: modelData["status"] != "OK" + color: modelData["status"] == "WARNING" ? "transparent" : "white" + width: modelData["status"] != "OK"? icon_size_ + spacing_icon_: 0 + height: modelData["status"] != "OK"? icon_size_ + spacing_icon_: 0 + radius: modelData["status"] != "OK"? icon_size_ + spacing_icon_: 0 + IconSVG { + anchors.centerIn: parent + name: modelData["status"] == "WARNING" ? "issues" : "error" + color: modelData["status"] == "WARNING" ? "black" : "red" + size: modelData["status"] != "OK"? icon_size_ : 0 + } } IconSVG { name: "user" @@ -695,9 +714,14 @@ Item MouseArea { anchors.fill: parent + acceptedButtons: Qt.LeftButton | Qt.RightButton onClicked: { - controller.user_click(modelData["id"]) + if(mouse.button & Qt.RightButton) { + openEntitiesMenu(domain_entity_id, modelData["id"], modelData["alias"], modelData["kind"]) + } else { + controller.user_click(modelData["id"]) + } } } } @@ -804,18 +828,20 @@ Item Rectangle { color: "transparent" - width: modelData["status"] != "OK" - ? first_indentation_ : 0 - } - IconSVG { - visible: modelData["status"] != "OK" - name: "issues" - color: "white" - size: modelData["status"] != "OK"? icon_size_ : 0 + width: first_indentation_ } Rectangle { - color: "transparent" - width: first_indentation_ /2 + visible: modelData["status"] != "OK" + color: modelData["status"] == "WARNING" ? "transparent" : "white" + width: modelData["status"] != "OK"? icon_size_ + spacing_icon_: 0 + height: modelData["status"] != "OK"? icon_size_ + spacing_icon_: 0 + radius: modelData["status"] != "OK"? icon_size_ + spacing_icon_: 0 + IconSVG { + anchors.centerIn: parent + name: modelData["status"] == "WARNING" ? "issues" : "error" + color: modelData["status"] == "WARNING" ? "black" : "red" + size: modelData["status"] != "OK"? icon_size_ : 0 + } } IconSVG { name: "process" @@ -831,9 +857,14 @@ Item MouseArea { anchors.fill: parent + acceptedButtons: Qt.LeftButton | Qt.RightButton onClicked: { - controller.process_click(modelData["id"]) + if(mouse.button & Qt.RightButton) { + openEntitiesMenu(domain_entity_id, modelData["id"], modelData["alias"], modelData["kind"]) + } else { + controller.process_click(modelData["id"]) + } } } } @@ -940,20 +971,23 @@ Item Rectangle { color: "transparent" - width: modelData["status"] != "OK" - ? first_indentation_ : 0 - } - IconSVG { - visible: modelData["status"] != "OK" - name: "issues" - size: modelData["status"] != "OK"? icon_size_ : 0 + width: first_indentation_ } Rectangle { - color: "transparent" - width: first_indentation_ /2 + visible: modelData["status"] != "OK" + color: modelData["status"] == "WARNING" ? "transparent" : "white" + width: modelData["status"] != "OK"? icon_size_ + spacing_icon_: 0 + height: modelData["status"] != "OK"? icon_size_ + spacing_icon_: 0 + radius: modelData["status"] != "OK"? icon_size_ + spacing_icon_: 0 + IconSVG { + anchors.centerIn: parent + name: modelData["status"] == "WARNING" ? "issues" : "error" + color: modelData["status"] == "WARNING" ? "black" : "red" + size: modelData["status"] != "OK"? icon_size_ : 0 + } } IconSVG { - name: modelData["kind"] + name: "participant" size: icon_size_ } Label { @@ -964,9 +998,14 @@ Item MouseArea { anchors.fill: parent + acceptedButtons: Qt.LeftButton | Qt.RightButton onClicked: { - controller.participant_click(modelData["id"]) + if(mouse.button & Qt.RightButton) { + openEntitiesMenu(domain_entity_id, modelData["id"], modelData["alias"], modelData["kind"]) + } else { + controller.participant_click(modelData["id"]) + } } } } @@ -1083,8 +1122,8 @@ Item var globalCoordinates = endpointComponent.mapToItem(mainSpace, 0, 0) var src_x = globalCoordinates.x + entity_box_width_-(8*elements_spacing_) var src_y = modelData["accum_y"] + (endpointComponent.height / 2) - var left_direction = modelData["kind"] == "datareader" - var right_direction = modelData["kind"] == "datawriter" + var left_direction = modelData["kind"] == "DataReader" + var right_direction = modelData["kind"] == "DataWriter" endpoint_topic_connections_[modelData["id"]] = { "id": modelData["id"], "left_direction": left_direction, @@ -1104,7 +1143,7 @@ Item id: endpoint_background width: parent.width height: endpoint_height_ - color: modelData["kind"] == "datareader" ? reader_color_ : writer_color_ + color: modelData["kind"] == "DataReader" ? reader_color_ : writer_color_ radius: radius_ } @@ -1128,20 +1167,24 @@ Item Rectangle { color: "transparent" - width: modelData["status"] != "OK" - ? first_indentation_ : 0 - } - IconSVG { - visible: modelData["status"] != "OK" - name: "issues" - size: modelData["status"] != "OK"? icon_size_ : 0 + width: first_indentation_ } Rectangle { - color: "transparent" - width: first_indentation_ /2 + visible: modelData["status"] != "OK" + color: modelData["status"] == "WARNING" ? "transparent" : "white" + width: modelData["status"] != "OK"? icon_size_ + spacing_icon_: 0 + height: modelData["status"] != "OK"? icon_size_ + spacing_icon_: 0 + radius: modelData["status"] != "OK"? icon_size_ + spacing_icon_: 0 + IconSVG { + anchors.centerIn: parent + name: modelData["status"] == "WARNING" ? "issues" : "error" + color: modelData["status"] == "WARNING" ? "black" : "red" + size: modelData["status"] != "OK"? icon_size_ : 0 + } } IconSVG { - name: modelData["kind"] + name: modelData["kind"] == "DataReader" + ? "datareader" : "datawriter" size: icon_size_ } Label { @@ -1152,9 +1195,14 @@ Item MouseArea { anchors.fill: parent + acceptedButtons: Qt.LeftButton | Qt.RightButton onClicked: { - controller.endpoint_click(modelData["id"]) + if(mouse.button & Qt.RightButton) { + openEntitiesMenu(domain_entity_id, modelData["id"], modelData["alias"], modelData["kind"]) + } else { + controller.endpoint_click(modelData["id"]) + } } } } @@ -1230,16 +1278,6 @@ Item } } - // footer section to cut entities layout - Rectangle { - anchors.bottom: parent.bottom - anchors.left: parent.left - height: elements_spacing_ - width: entity_box_width_ + 2*elements_spacing_ - color: "white" - z: 14 - } - // Empty screen message Rectangle { anchors.fill: parent @@ -1265,18 +1303,48 @@ Item Timer { id: safety_timer - interval: 200; running: false - onTriggered: { interval += interval; load_model() } - } function stop_timer() { safety_timer.stop() } + interval: timer_initial_ms_interval_; running: false + onTriggered: { + interval += interval + load_model() + } + } + function stop_timer() { + if (safety_timer.running) + { + safety_timer.stop() + safety_timer.interval = timer_initial_ms_interval_ + } + } // Obtain given domain id graph JSON model function load_model() { + if (filtered_topics_.length > 0) + { + filter_model_by_topic (filtered_topics_[filtered_topics_.length-1]) + } + else + { + filter_model_by_topic ("") + } + } + + // Filter model by topic + function filter_model_by_topic (topic_id) + { + // topic id management + var topic_names = [] + if (topic_id != "" && !filtered_topics_.includes(topic_id)) + { + filtered_topics_[filtered_topics_.length] = topic_id; + } + // clear internal models clear_graph() // Obtain model from backend - var model_string = controller.get_domain_view_graph(entity_id) + var model_string = controller.get_domain_view_graph(domain_entity_id) // declare obtained hosts and topics variables var new_topics = [] @@ -1299,37 +1367,63 @@ Item var metatraffic_ = new_model["topics"][topic]["metatraffic"] if (metatraffic_ != true || is_metatraffic_visible_) { - new_topics[new_topics.length] = { - "id":topic, - "kind":new_model["topics"][topic]["kind"], - "alias":new_model["topics"][topic]["alias"] + if (filtered_topics_.length > 0) + { + for (var i = 0; i < filtered_topics_.length; i++) + { + if (filtered_topics_[i] == topic) + { + topic_names[i] = new_model["topics"][topic]["alias"] + new_topics[new_topics.length] = { + "id":topic, + "kind":"Topic", + "alias":new_model["topics"][topic]["alias"] + } + } + } + } + else + { + new_topics[new_topics.length] = { + "id":topic, + "kind":"Topic", + "alias":new_model["topics"][topic]["alias"] + } } } } var accum_y = 0 + var host_temp_y = 0 for (var host in new_model["hosts"]) { + var discard_host = true var metatraffic_ = new_model["hosts"][host]["metatraffic"] if (metatraffic_ != true || is_metatraffic_visible_) { accum_y += label_height_ + elements_spacing_ var new_users = [] + var user_temp_y = accum_y for (var user in new_model["hosts"][host]["users"]) { + var discard_user = true var metatraffic_ = new_model["hosts"][host]["users"][user]["metatraffic"] if (metatraffic_ != true || is_metatraffic_visible_) { accum_y += label_height_ + elements_spacing_ var new_processes = [] + var process_temp_y = accum_y for (var process in new_model["hosts"][host]["users"][user]["processes"]) { + var discard_process = true var metatraffic_ = new_model["hosts"][host]["users"][user]["processes"][process]["metatraffic"] if (metatraffic_ != true || is_metatraffic_visible_) { accum_y += label_height_ + elements_spacing_ var new_participants = [] + var participant_temp_y = accum_y for (var participant in new_model["hosts"][host]["users"][user]["processes"][process]["participants"]) { + var discard_participant = true var metatraffic_ = new_model["hosts"][host]["users"][user]["processes"][process]["participants"][participant]["metatraffic"] if (metatraffic_ != true || is_metatraffic_visible_) { @@ -1340,60 +1434,103 @@ Item var metatraffic_ = new_model["hosts"][host]["users"][user]["processes"][process]["participants"][participant]["endpoints"][endpoint]["metatraffic"] if (metatraffic_ != true || is_metatraffic_visible_) { - new_endpoints[new_endpoints.length] = { - "id":endpoint, - "kind":new_model["hosts"][host]["users"][user]["processes"][process]["participants"][participant]["endpoints"][endpoint]["kind"], - "alias":new_model["hosts"][host]["users"][user]["processes"][process]["participants"][participant]["endpoints"][endpoint]["alias"], - "status":new_model["hosts"][host]["users"][user]["processes"][process]["participants"][participant]["endpoints"][endpoint]["status"], - "topic":new_model["hosts"][host]["users"][user]["processes"][process]["participants"][participant]["endpoints"][endpoint]["topic"], - "accum_y":accum_y + if ((!filtered_topics_.length) || (filtered_topics_.length > 0 + && filtered_topics_.includes(new_model["hosts"][host]["users"][user]["processes"][process]["participants"][participant]["endpoints"][endpoint]["topic"]))) + { + discard_participant = false; discard_process = false; discard_user = false; discard_host = false + var kind = "DataWriter" + if (new_model["hosts"][host]["users"][user]["processes"][process]["participants"][participant]["endpoints"][endpoint]["kind"] == "datareader") + { + kind = "DataReader" + } + new_endpoints[new_endpoints.length] = { + "id":endpoint, + "kind":kind, + "alias":new_model["hosts"][host]["users"][user]["processes"][process]["participants"][participant]["endpoints"][endpoint]["alias"], + "status":new_model["hosts"][host]["users"][user]["processes"][process]["participants"][participant]["endpoints"][endpoint]["status"], + "topic":new_model["hosts"][host]["users"][user]["processes"][process]["participants"][participant]["endpoints"][endpoint]["topic"], + "accum_y":accum_y + } + accum_y += endpoint_height_ + elements_spacing_ + pending_endpoints_[pending_endpoints_.length] = endpoint } - accum_y += endpoint_height_ + elements_spacing_ - pending_endpoints_[pending_endpoints_.length] = endpoint } } - new_participants[new_participants.length] = { - "id":participant, - "kind":new_model["hosts"][host]["users"][user]["processes"][process]["participants"][participant]["kind"], - "alias":new_model["hosts"][host]["users"][user]["processes"][process]["participants"][participant]["alias"], - "status":new_model["hosts"][host]["users"][user]["processes"][process]["participants"][participant]["status"], - "app_id":new_model["hosts"][host]["users"][user]["processes"][process]["participants"][participant]["app_id"], - "app_metadata":new_model["hosts"][host]["users"][user]["processes"][process]["participants"][participant]["app_metadata"], - "endpoints":new_endpoints + if (!discard_participant) + { + new_participants[new_participants.length] = { + "id":participant, + "kind": "DomainParticipant", + "alias":new_model["hosts"][host]["users"][user]["processes"][process]["participants"][participant]["alias"], + "status":new_model["hosts"][host]["users"][user]["processes"][process]["participants"][participant]["status"], + "app_id":new_model["hosts"][host]["users"][user]["processes"][process]["participants"][participant]["app_id"], + "app_metadata":new_model["hosts"][host]["users"][user]["processes"][process]["participants"][participant]["app_metadata"], + "endpoints":new_endpoints + } + accum_y += elements_spacing_ + participant_temp_y = accum_y + } + else + { + accum_y = participant_temp_y } - accum_y += elements_spacing_ } } - new_processes[new_processes.length] = { - "id":process, - "kind":new_model["hosts"][host]["users"][user]["processes"][process]["kind"], - "alias":new_model["hosts"][host]["users"][user]["processes"][process]["alias"], - "pid": new_model["hosts"][host]["users"][user]["processes"][process]["pid"], - "status":new_model["hosts"][host]["users"][user]["processes"][process]["status"], - "participants":new_participants + if (!discard_process) + { + new_processes[new_processes.length] = { + "id":process, + "kind":"Process", + "alias":new_model["hosts"][host]["users"][user]["processes"][process]["alias"], + "pid": new_model["hosts"][host]["users"][user]["processes"][process]["pid"], + "status":new_model["hosts"][host]["users"][user]["processes"][process]["status"], + "participants":new_participants + } + accum_y += elements_spacing_ + process_temp_y = accum_y + } + else + { + accum_y = process_temp_y } - accum_y += elements_spacing_ } } - new_users[new_users.length] = { - "id":user, - "kind":new_model["hosts"][host]["users"][user]["kind"], - "alias":new_model["hosts"][host]["users"][user]["alias"], - "status":new_model["hosts"][host]["users"][user]["status"], - "processes":new_processes + if (!discard_user) + { + new_users[new_users.length] = { + "id":user, + "kind": "User", + "alias":new_model["hosts"][host]["users"][user]["alias"], + "status":new_model["hosts"][host]["users"][user]["status"], + "processes":new_processes + } + accum_y += elements_spacing_ + user_temp_y = accum_y + } + else + { + accum_y = user_temp_y } - accum_y += elements_spacing_ } } - new_hosts[new_hosts.length] = { - "id":host, - "kind":new_model["hosts"][host]["kind"], - "alias":new_model["hosts"][host]["alias"], - "status":new_model["hosts"][host]["status"], - "users":new_users + if (!discard_host) + { + new_hosts[new_hosts.length] = { + "id":host, + "kind":"Host", + "alias":new_model["hosts"][host]["alias"], + "status":new_model["hosts"][host]["status"], + "users":new_users + } + accum_y += elements_spacing_ + host_temp_y = accum_y + } + else + { + accum_y = host_temp_y } - accum_y += elements_spacing_ } + } model = { "kind": new_model["kind"], @@ -1423,12 +1560,39 @@ Item "hosts": [], } + // disable recovery timer + stop_timer(); + // display empty screen label emptyScreenLabel.visible = true } // Update tab name with selected domain id - domainGraphLayout.update_tab_name("Domain " + domain_id + " View") + if (filtered_topics_.length > 0) + { + if (filtered_topics_.length == 1) + { + domainGraphLayout.update_tab_name(topic_names[0] + " Topic View", component_id) + } + else + { + var print_topic_names = topic_names[0] + for (var i = 1; i < topic_names.length -1; i++) + { + print_topic_names += ", " + topic_names[i] + } + if (print_topic_names.length-1 > 0) + { + print_topic_names += " and " + topic_names[topic_names.length-1] + } + + domainGraphLayout.update_tab_name(print_topic_names + " Topics View", component_id) + } + } + else + { + domainGraphLayout.update_tab_name("Domain " + domain_id + " View", component_id) + } } // remove drawn connections @@ -1458,4 +1622,87 @@ Item } } } + + // check if model contains entity + function contains_entity(domainEntityId, entityId) + { + // check if domainEntityId has content + if (domainEntityId != "") + { + // belongs to the current domain + if (domain_entity_id.toString() != domainEntityId) + { + return false + } + } + // check all entities by entityId + + // check domain + if(domain_entity_id.toString() == entityId) + { + return true + } + + // check topics + for (var topic in model["topics"]) + { + if (model["topics"][topic]["id"] == entityId) + { + return true + } + } + + // check entities + for (var host in model["hosts"]) + { + if (model["hosts"][host]["id"] == entityId) + { + return true + } + else + { + for (var user in model["hosts"][host]["users"]) + { + if (model["hosts"][host]["users"][user]["id"] == entityId) + { + return true + } + else + { + for (var process in model["hosts"][host]["users"][user]["processes"]) + { + if (model["hosts"][host]["users"][user]["processes"][process]["id"] == entityId) + { + return true + } + else + { + for (var participant in model["hosts"][host]["users"][user]["processes"][process]["participants"]) + { + if (model["hosts"][host]["users"][user]["processes"][process]["participants"][participant]["id"] == entityId) + { + return true + } + else + { + for (var endpoint in model["hosts"][host]["users"][user]["processes"][process]["participants"][participant]["endpoints"]) + { + if (model["hosts"][host]["users"][user]["processes"][process]["participants"][participant]["endpoints"][endpoint]["id"] == entityId) + { + return true + } + } + } + } + } + } + } + } + } + } + + + // not found yet + return false + } } diff --git a/qml/EntitiesMenu.qml b/qml/EntitiesMenu.qml index 48b084f6..160b699b 100644 --- a/qml/EntitiesMenu.qml +++ b/qml/EntitiesMenu.qml @@ -7,12 +7,17 @@ import Theme 1.0 */ Menu { id: entitiesMenu + property string domainEntityId: "" property string entityId: "" property string currentAlias: "" property string entityKind: "" MenuItem { text: "Change alias" - onTriggered: changeAlias(menu.entityId, menu.currentAlias, menu.entityKind) + onTriggered: changeAlias(menu.domainEntityId, menu.entityId, menu.currentAlias, menu.entityKind) + } + MenuItem { + text: "View Problems" + onTriggered: filterEntityStatusLog(menu.entityId) } } diff --git a/qml/EntityList.qml b/qml/EntityList.qml index 0746d97b..61c534d2 100644 --- a/qml/EntityList.qml +++ b/qml/EntityList.qml @@ -90,7 +90,7 @@ Rectangle { } onClicked: { if(mouse.button & Qt.RightButton) { - openEntitiesMenu(id, name, kind) + openEntitiesMenu("", id, name, kind) } else { controller.participant_click(id) } @@ -173,7 +173,7 @@ Rectangle { } onClicked: { if(mouse.button & Qt.RightButton) { - openEntitiesMenu(id, name, kind) + openEntitiesMenu("", id, name, kind) } else { controller.endpoint_click(id) } @@ -245,7 +245,7 @@ Rectangle { onClicked: { if(mouse.button & Qt.RightButton) { - openEntitiesMenu(id, name, kind) + openEntitiesMenu("", id, name, kind) } else { controller.locator_click(id) } diff --git a/qml/GraphConnection.qml b/qml/GraphConnection.qml index 5464d3fc..b52b4fe6 100644 --- a/qml/GraphConnection.qml +++ b/qml/GraphConnection.qml @@ -2,15 +2,22 @@ import QtQuick 2.0 Item { // public property - property bool left_direction: false // defines if the represented connection must draw a left arrow - property bool right_direction: false // defines if the represented connection must draw a right arrow - property int left_margin: 0 // left margin to be applied - property string arrow_color: Theme.grey // connection color - property string background_color: "white" // background color + property bool left_direction: false // defines if the represented connection must draw a left arrow + property bool right_direction: false // defines if the represented connection must draw a right arrow + property int left_margin: 0 // left margin to be applied + property string arrow_color: Theme.grey // connection color + property string background_color: "white" // background color // readonly private design properties - readonly property int arrow_margin_: -4 // margins for background - readonly property int arrow_size_: 30 // arrow size + readonly property int arrow_margin_: -3 // margins for background + readonly property int arrow_size_: 18 // arrow size + readonly property int main_left_margin_: 10 // left arrow margin size + readonly property int main_right_margin_: 15 // right arrow margin size + readonly property int left_arrow_background_: 15 // left arrow background size + readonly property int left_arrow_background_icon_size_offset_: 8 + readonly property int left_arrow_background_margin_: -4 + readonly property int left_arrow_margin_: -5 // left arrow margin + readonly property int right_arrow_margin_: -2 // right arrow margin // background to make connection overlap nicely with previous topics (looks like connection goes OVER the topic) Rectangle { @@ -19,18 +26,19 @@ Item { anchors.top: parent.top; anchors.bottom: parent.bottom anchors.topMargin: arrow_margin_; anchors.bottomMargin: arrow_margin_ anchors.left: parent.left; anchors.right: parent.right - anchors.leftMargin: left_margin; anchors.rightMargin: left_margin; - color: background_color + anchors.leftMargin: left_margin; anchors.rightMargin: left_margin + color: "white" } + // background to make connection overlap nicely left panel Rectangle { id: left_background - opacity: 0.70 anchors.top: parent.top; anchors.bottom: parent.bottom - anchors.topMargin: -2; anchors.bottomMargin: -2 + anchors.topMargin: arrow_margin_; anchors.bottomMargin: arrow_margin_ anchors.left: parent.left; anchors.right: parent.right - anchors.leftMargin: parent.height /2; anchors.rightMargin: 5; - color: background_color + anchors.leftMargin: parent.height /2 + anchors.rightMargin: left_direction ? left_margin: left_margin != 0 ? parent.height : parent.height/2 + color: "white" } @@ -39,34 +47,26 @@ Item { Item { id: left_arrow_background visible: left_direction - height: arrow_size_ + 8 - width: arrow_size_ + 2 - opacity: 0.7 + height: arrow_size_ + left_arrow_background_ + width: arrow_size_ + clip: true anchors.verticalCenter: parent.verticalCenter - anchors.left: parent.left - - Canvas { - id: left_canvas_background - - anchors.centerIn: parent - - height: parent.height / 2 - width: parent.width - - antialiasing:true; smooth:true - - onPaint: { - var ctx = left_canvas_background.getContext('2d') + anchors.left: parent.left; anchors.leftMargin: left_arrow_background_margin_ + + IconSVG { + anchors.left: parent.left + anchors.top: parent.top + name: "left_arrow" + color: "white" + size: arrow_size_ + left_arrow_background_icon_size_offset_ + } - ctx.strokeStyle = "white" - ctx.lineWidth = left_canvas_background.width * 0.1 - ctx.beginPath() - ctx.moveTo(left_canvas_background.width, left_canvas_background.height * 0.001) - ctx.lineTo(12, left_canvas_background.height / 2 - 6) - ctx.lineTo(12, left_canvas_background.height / 2 + 6) - ctx.lineTo(left_canvas_background.width, left_canvas_background.height * 0.999) - ctx.stroke() - } + IconSVG { + anchors.left: parent.left + anchors.bottom: parent.bottom + name: "left_arrow" + color: "white" + size: arrow_size_ + left_arrow_background_icon_size_offset_ } } @@ -77,29 +77,12 @@ Item { height: arrow_size_ width: arrow_size_ anchors.verticalCenter: parent.verticalCenter - anchors.left: parent.left - - Canvas { - id: left_canvas - - anchors.centerIn: parent + anchors.left: parent.left; anchors.leftMargin: left_arrow_margin_ - height: parent.height / 2 - width: parent.width - - antialiasing:true; smooth:true - - onPaint: { - var ctx = left_canvas.getContext('2d') - - ctx.strokeStyle = arrow_color - ctx.lineWidth = left_canvas.width * 0.1 - ctx.beginPath() - ctx.moveTo(left_canvas.width, left_canvas.height * 0.05) - ctx.lineTo(0, left_canvas.height / 2) - ctx.lineTo(left_canvas.width, left_canvas.height * 0.95) - ctx.stroke() - } + IconSVG { + name: "left_arrow" + color: "grey" + size: arrow_size_ } } @@ -108,8 +91,8 @@ Item { id: base_arrow anchors.top: parent.top; anchors.bottom: parent.bottom anchors.left: parent.left; anchors.right: parent.right - anchors.leftMargin: left_direction ? 8 : 0 - anchors.rightMargin: right_direction ? 8 : 0 + anchors.leftMargin: left_direction ? main_left_margin_ : 0 + anchors.rightMargin: right_direction ? main_right_margin_ : 0 color: arrow_color } @@ -117,32 +100,15 @@ Item { Item { id: right_arrow visible: right_direction - height: arrow_size_ - width: arrow_size_ + height: arrow_size_ + right_arrow_margin_ + width: arrow_size_ + right_arrow_margin_ anchors.verticalCenter: parent.verticalCenter - anchors.right: parent.right; anchors.rightMargin: parent.height /2 + 2 - - Canvas { - id: right_canvas - - anchors.centerIn: parent - - height: parent.height / 2 - width: parent.width - - antialiasing:true; smooth:true - - onPaint: { - var ctx = right_canvas.getContext('2d') + anchors.right: parent.right; anchors.rightMargin: parent.height /2 - ctx.strokeStyle = arrow_color - ctx.lineWidth = right_canvas.width * 0.1 - ctx.beginPath() - ctx.moveTo(0, right_canvas.height * 0.05) - ctx.lineTo(right_canvas.width, right_canvas.height / 2 -1) - ctx.lineTo(0, right_canvas.height * 0.95) - ctx.stroke() - } + IconSVG { + name: "right_arrow" + color: "grey" + size: arrow_size_ + right_arrow_margin_ } } } diff --git a/qml/LeftPanel.qml b/qml/LeftPanel.qml index 4f3fe822..35a7435c 100644 --- a/qml/LeftPanel.qml +++ b/qml/LeftPanel.qml @@ -40,6 +40,9 @@ RowLayout { signal explorerPhysicalChanged(bool status) signal explorerLogicalChanged(bool status) signal explorerEntityInfoChanged(bool status) + signal open_topic_view(string domainEntityId, string domainId, string entityId) + signal refresh_domain_graph_view(string domainEntityId, string entityId) + signal filter_entity_status_log(string entityId) MonitoringPanel { id: monitoringPanel @@ -65,26 +68,52 @@ RowLayout { id: entitiesMenu } + TopicMenu { + id: topicMenu + } + IssuesPanel { id: issuesPanel Layout.fillHeight: true visible: (visiblePanel === panelItem[LeftPanel.LeftSubPanel.Issues]) ? true : false } - function changeAlias(entityId, currentAlias, entityKind) { + function changeAlias(domainEntityId, entityId, currentAlias, entityKind) { + aliasDialog.domainEntityId = domainEntityId aliasDialog.entityId = entityId aliasDialog.currentAlias = currentAlias aliasDialog.entityKind = entityKind aliasDialog.open() } - function openEntitiesMenu(entityId, currentAlias, entityKind) { + function openEntitiesMenu(domainEntityId, entityId, currentAlias, entityKind) { + entitiesMenu.domainEntityId = domainEntityId entitiesMenu.entityId = entityId entitiesMenu.currentAlias = currentAlias entitiesMenu.entityKind = entityKind entitiesMenu.popup() } + function openTopicMenu(domainEntityId, domainId, entityId, currentAlias, entityKind) { + topicMenu.domainEntityId = domainEntityId + topicMenu.domainId = domainId + topicMenu.entityId = entityId + topicMenu.currentAlias = currentAlias + topicMenu.entityKind = entityKind + topicMenu.popup() + } + + function openTopicView(domainEntityId, domainId, entityId) { + leftPanel.open_topic_view(domainEntityId, domainId, entityId) + } + + function refreshDomainGraphView(domainEntityId, entityId) { + leftPanel.refresh_domain_graph_view(domainEntityId, entityId) + } + function filterEntityStatusLog(entityId){ + leftPanel.filter_entity_status_log(entityId) + } + function expandAll(view, model) { for(var i=0; i < model.rowCount(); i++) { var index = model.index(i, 0) diff --git a/qml/LogicalView.qml b/qml/LogicalView.qml index cbbfa464..999950e4 100644 --- a/qml/LogicalView.qml +++ b/qml/LogicalView.qml @@ -63,6 +63,7 @@ Rectangle { height: domainListColumn.childrenRect.height property var domainId: id + property var domainName: name property int domainIdx: index property var topicList: topicList @@ -96,7 +97,7 @@ Rectangle { } onClicked: { if(mouse.button & Qt.RightButton) { - openEntitiesMenu(id, name, kind) + openEntitiesMenu(domainId, id, name, kind) } else { controller.domain_click(id) } @@ -166,7 +167,7 @@ Rectangle { onClicked: { if(mouse.button & Qt.RightButton) { - openEntitiesMenu(id, name, kind) + openTopicMenu(domainId, domainName, id, name, kind) } else { controller.topic_click(id) } diff --git a/qml/Panels.qml b/qml/Panels.qml index 84df2717..5e80df17 100644 --- a/qml/Panels.qml +++ b/qml/Panels.qml @@ -30,6 +30,9 @@ RowLayout { // If is set to true, the left sidebar was opened, and false if it was closed. property bool prevFullScreenLeftSidebarState: true + // private properties + property int status_layout_height: status_layout_min_height_ + signal openCloseLeftSideBar signal changeChartboxLayout(int chartsPerRow) signal explorerDDSEntitiesChanged(bool status) @@ -37,6 +40,10 @@ RowLayout { signal explorerLogicalChanged(bool status) signal explorerEntityInfoChanged(bool status) + // Read only design properties + readonly property int status_layout_min_height_: 24 + + onOpenCloseLeftSideBar: { if (panels.showLeftSidebar) { iconsVBar.iconClicked(iconsVBar.selected) @@ -90,24 +97,66 @@ RowLayout { onExplorerPhysicalChanged: panels.explorerPhysicalChanged(status) onExplorerLogicalChanged: panels.explorerLogicalChanged(status) onExplorerEntityInfoChanged: panels.explorerEntityInfoChanged(status) + onOpen_topic_view: tabs.open_topic_view(domainEntityId, domainId, entityId) + onRefresh_domain_graph_view: tabs.refresh_domain_graph_view(domainEntityId, entityId) + onFilter_entity_status_log: statusLayout.filter_entity_status_log(entityId) } - TabLayout { - id: tabs + Rectangle { SplitView.fillWidth: true - clip: true - onFullScreenChanged: { - if (fullScreen) { - if (showLeftSidebar) { - openCloseLeftSideBar() - prevFullScreenLeftSidebarState = true - } else { - prevFullScreenLeftSidebarState = false + SplitView { + id: tabsSplitView + anchors.fill: parent + orientation: Qt.Vertical + + TabLayout { + id: tabs + SplitView.fillWidth: true + SplitView.fillHeight: true + SplitView.preferredHeight: parent.height - parent.height / 5 + clip: true + + onFullScreenChanged: { + if (fullScreen) { + if (showLeftSidebar) { + openCloseLeftSideBar() + prevFullScreenLeftSidebarState = true + } else { + prevFullScreenLeftSidebarState = false + } + } else { + if (!showLeftSidebar && prevFullScreenLeftSidebarState) { + openCloseLeftSideBar() + } + } } - } else { - if (!showLeftSidebar && prevFullScreenLeftSidebarState) { - openCloseLeftSideBar() + onOpenEntitiesMenu: { + panels.openEntitiesMenu(domainEntityId, entityId, currentAlias, entityKind) + } + onOpenTopicMenu: { + panels.openTopicMenu(domainEntityId, domainId, entityId, currentAlias, entityKind) + } + } + StatusLayout { + id: statusLayout + SplitView.preferredHeight: status_layout_height + SplitView.minimumHeight: status_layout_min_height_ + clip: true + current_status: StatusLayout.Status.Collapsed + footer_height: status_layout_min_height_ + + onClose_status_layout: { + status_layout_height = status_layout_min_height_ + statusLayout.current_status = StatusLayout.Status.Closed + } + onCollapse_status_layout: { + status_layout_height = tabsSplitView.height / 5 + statusLayout.current_status = StatusLayout.Status.Collapsed + } + onExpand_status_layout: { + status_layout_height = tabsSplitView.height + statusLayout.current_status = StatusLayout.Status.Expanded } } } @@ -145,4 +194,12 @@ RowLayout { function changeExplorerEntityInfo(status) { leftPanel.changeExplorerEntityInfo(status) } + + function openEntitiesMenu(domainEntityId, entityId, currentAlias, entityKind) { + leftPanel.openEntitiesMenu(domainEntityId, entityId, currentAlias, entityKind) + } + + function openTopicMenu(domainEntityId, domainId, entityId, currentAlias, entityKind) { + leftPanel.openTopicMenu(domainEntityId, domainId, entityId, currentAlias, entityKind) + } } diff --git a/qml/PhysicalView.qml b/qml/PhysicalView.qml index 0fa8cb55..e698b445 100644 --- a/qml/PhysicalView.qml +++ b/qml/PhysicalView.qml @@ -90,7 +90,7 @@ Rectangle { } onClicked: { if(mouse.button & Qt.RightButton) { - openEntitiesMenu(id, name, kind) + openEntitiesMenu("", id, name, kind) } else { controller.host_click(id) } @@ -174,7 +174,7 @@ Rectangle { } onClicked: { if(mouse.button & Qt.RightButton) { - openEntitiesMenu(id, name, kind) + openEntitiesMenu("", id, name, kind) } else { controller.user_click(id) } @@ -246,7 +246,7 @@ Rectangle { onClicked: { if(mouse.button & Qt.RightButton) { - openEntitiesMenu(id, name, kind) + openEntitiesMenu("", id, name, kind) } else { controller.process_click(id) } diff --git a/qml/StatusLayout.qml b/qml/StatusLayout.qml new file mode 100644 index 00000000..beca6d9f --- /dev/null +++ b/qml/StatusLayout.qml @@ -0,0 +1,320 @@ +// Copyright 2023 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// This file is part of eProsima Fast DDS Monitor. +// +// eProsima Fast DDS Monitor 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 3 of the License, or +// (at your option) any later version. +// +// eProsima Fast DDS Monitor 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 eProsima Fast DDS Monitor. If not, see <https://www.gnu.org/licenses/>. + +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 +import QtQuick.Layouts 1.15 + +import Theme 1.0 + +Item +{ + id: statusLayout + + // Public properties + enum Status {Closed, Expanded, Collapsed} + property int current_status: StatusLayout.Status.Closed + required property int footer_height + + // Public signals + signal close_status_layout() + signal expand_status_layout() + signal collapse_status_layout() + + // Private properties + property bool filter_visible_: false + + // Private signals + signal focus_entity_(int entityId) + signal clean_filter_() + + // Read only design properties (sizes and colors) + readonly property int tabs_height_: 30 + readonly property int tabs_width_: 100 + readonly property int elements_spacing_: 8 + readonly property int separator_line_: 2 + readonly property string grey_background_: "#eaeaea" + + property int spacingIconLabel: 8 + property int iconSize: 18 + property int firstIndentation: 5 + property int secondIndentation: firstIndentation + iconSize + spacingIconLabel + + // Main tab view with possibility of multiple tabs + TabView { + id: tab_view + anchors.top: parent.top + anchors.bottom: separator_line.top + width: parent.width + + // Main Problems tab + Tab { + title: "Problems" + Rectangle { + + color: "white" + + // Main content of problems tab: problem tree view with problems per entity + StatusTreeView { + id: status_tree_view + anchors.fill: parent + anchors.margins: 1 + + model: entityStatusModel // problems model: entity status proxy model + + // display if hidden when problems filtered (from right-click dialog) + onEntity_status_filtered:{ + collapse_status_layout() + } + + // filter and clean filter signal-slots management + Connections { + target: statusLayout + + function onClean_filter_() { + status_tree_view.clean_filter() + } + + function onFocus_entity_(entityId) { + status_tree_view.filter_model_by_id(entityId) + } + } + } + } + } + + // Tab main stlye + style: TabViewStyle { + frameOverlap: 1 + // Each tab style: simple rounded rect header with text + tab: Rectangle { + color: styleData.selected ? "white" : Theme.lightGrey + implicitWidth: Math.max(text.width + 10, tabs_width_) + implicitHeight: tabs_height_ + radius: 4 + + Rectangle { + width: parent.width + height: parent.height/2 + anchors.bottom: parent.bottom + anchors.left: parent.left + color: parent.color + } + Text { + id: text + anchors.centerIn: parent + text: styleData.title + } + } + // Tab bar style: would contain all tabs, and the custom menu on the right section + // (close, expand/collapse, and filter buttons, from right to left) + tabBar: Rectangle { + anchors.top: parent.top + width: parent.width + height: tabs_height_ + color: grey_background_ + + // Close button + IconSVG { + id: close_icon + anchors.right: parent.right + anchors.rightMargin: elements_spacing_ *2 + anchors.verticalCenter: parent.verticalCenter + name: "cross" + size: parent.height - elements_spacing_ *3/2 + + MouseArea { + anchors.centerIn: parent + width: parent.width + 2*elements_spacing_ + height: parent.height + 2*elements_spacing_ + + onClicked: { + close_status_layout() + } + } + } + + // Container with expand and collapse buttons + Rectangle { + id: rect + anchors.right: close_icon.left + anchors.rightMargin: elements_spacing_ *2 + anchors.verticalCenter: parent.verticalCenter + anchors.verticalCenterOffset: elements_spacing_/2 + width: parent.height - elements_spacing_ + height: parent.height - elements_spacing_ + color: "transparent" + + IconSVG { + id: expand_icon + + name: statusLayout.current_status === StatusLayout.Status.Expanded + ? "collapse" : "expand" + size: parent.width + } + + MouseArea { + anchors.centerIn: parent + width: parent.width + 2*elements_spacing_ + height: parent.height + 2*elements_spacing_ + + onClicked: { + if (statusLayout.current_status === StatusLayout.Status.Expanded) + { + collapse_status_layout() + + } + else if (statusLayout.current_status === StatusLayout.Status.Collapsed) + { + expand_status_layout() + } + } + } + } + + // Container with filter buttons (filtered/not filtered icons) + Rectangle { + id: filter_rect + anchors.right: rect.left + anchors.rightMargin: elements_spacing_ *2 + anchors.verticalCenter: parent.verticalCenter + height: parent.height - elements_spacing_ + width: parent.height - elements_spacing_ + color: "transparent" + + IconSVG { + id: filter_empty_icon + visible: !statusLayout.filter_visible_ + anchors.centerIn: parent + name: "filter_empty" + size: parent.width + } + + IconSVG { + id: filter_full_icon + visible: statusLayout.filter_visible_ + anchors.centerIn: parent + name: "filter_full" + size: parent.width + } + + MouseArea { + id: filter_btn + anchors.fill: parent + onClicked: statusLayout.clean_filter_() + } + } + + // connections to update filter icons + Connections { + target: statusLayout + + function onFocus_entity_(entityId) { + statusLayout.filter_visible_ = true + } + + function onClean_filter_() { + statusLayout.filter_visible_ = false + } + } + } + } + } + + Rectangle { + id: separator_line + anchors.bottom: icon_section.top + width: parent.width + height: separator_line_ + + color: Theme.grey + } + + // footer (and ALWAYS displayed) error and warning counters bar section + Rectangle { + id: icon_section + anchors.bottom: parent.bottom + height: footer_height + width: parent.width + color: grey_background_ + + // Close status so only this section is displayed, when component is loaded + Component.onCompleted: { + close_status_layout() + } + + IconSVG { + id: error_icon + anchors.left: parent.left + anchors.leftMargin: elements_spacing_ + anchors.verticalCenter: parent.verticalCenter + name: "error" + size: parent.height - elements_spacing_ + } + Label { + id: error_value + anchors.left: error_icon.right + anchors.leftMargin: elements_spacing_/2 + anchors.verticalCenter: parent.verticalCenter + text: "0" + } + IconSVG { + id: warning_icon + anchors.left: error_value.right + anchors.leftMargin: elements_spacing_ + anchors.verticalCenter: parent.verticalCenter + name: "issues" + size: parent.height - elements_spacing_ + } + Label { + id: warning_value + anchors.left: warning_icon.right + anchors.leftMargin: elements_spacing_/2 + anchors.verticalCenter: parent.verticalCenter + text: "0" + } + + Connections + { + target: controller + function onUpdate_status_counters(errors, warnings) { + error_value.text = errors + warning_value.text = warnings + } + } + + MouseArea { + anchors.fill: parent + onClicked: { + if (current_status === StatusLayout.Status.Collapsed) + { + close_status_layout() + } + else + { + collapse_status_layout() + } + } + } + } + + function filter_entity_status_log(entityId) { + statusLayout.focus_entity_(entityId) + } +} diff --git a/qml/StatusTreeView.qml b/qml/StatusTreeView.qml new file mode 100644 index 00000000..c58f3226 --- /dev/null +++ b/qml/StatusTreeView.qml @@ -0,0 +1,194 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2021 Maurizio Ingrassia + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +// Copyright 2023 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// This file is part of eProsima Fast DDS Monitor. +// +// eProsima Fast DDS Monitor 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 3 of the License, or +// (at your option) any later version. +// +// eProsima Fast DDS Monitor 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 eProsima Fast DDS Monitor. If not, see <https://www.gnu.org/licenses/>. + + +import QtQuick 2.15 +import QtQuick.Window 2.15 +import QtQuick.Controls 2.15 +import QtQml 2.15 + +Flickable { + id: root + + implicitWidth: 400 + implicitHeight: 400 + clip: true + + property var model + readonly property alias currentIndex: tree.selectedIndex + readonly property alias currentItem: tree.currentItem + property var currentData + + property alias handle: tree.handle + property alias contentItem: tree.contentItem + property Component highlight: Rectangle { + color: root.selectedColor + } + + property alias selectionEnabled: tree.selectionEnabled + property alias hoverEnabled: tree.hoverEnabled + + property alias color: tree.color + property alias handleColor: tree.handleColor + property alias hoverColor: tree.hoverColor + property alias selectedColor: tree.selectedColor + property alias selectedItemColor: tree.selectedItemColor + + property alias rowHeight: tree.rowHeight + property alias rowPadding: tree.rowPadding + property alias rowSpacing: tree.rowSpacing + + property alias fontMetrics: tree.fontMetrics + property alias font: tree.font + + enum Handle { + Triangle, + TriangleSmall, + TriangleOutline, + TriangleSmallOutline, + Chevron, + Arrow + } + + readonly property int filter_all_: -2 // backend::ID_ALL + property int current_filter_: filter_all_ + signal entity_status_filtered() + + property int handleStyle: StatusTreeView.Handle.TriangleSmallOutline + + contentHeight: tree.height + contentWidth: width + boundsBehavior: Flickable.StopAtBounds + ScrollBar.vertical: ScrollBar {} + + Component.onCompleted:{ + root.clean_filter() + } + + Connections + { + target: root.model + function onLayoutChanged() { + root.filter_model_by_id(root.current_filter_) + } + } + + Connections { + function onCurrentIndexChanged() { + if(currentIndex) + { + currentData = model.data(currentIndex) + } + } + } + + StatusTreeViewItem { + id: tree + + model: root.model + parentIndex: model.rootIndex() + childCount: model.rowCount(parentIndex) + + itemLeftPadding: 0 + color: root.color + handleColor: root.handleColor + hoverColor: root.hoverColor + selectedColor: root.selectedColor + selectedItemColor: root.selectedItemColor + defaultIndicator: indicatorToString(handleStyle) + z: 1 + + Connections { + target: root.model + ignoreUnknownSignals: true + function onLayoutChanged() { + tree.childCount = root.model ? root.model.rowCount(tree.parentIndex) : 0 + } + } + } + + Loader { + id: highlightLoader + sourceComponent: highlight + + width: root.width + height: root.rowHeight + z: 0 + visible: root.selectionEnabled && tree.currentItem !== null + + Binding { + target: highlightLoader.item + property: "y" + value: tree.currentItem ? tree.currentItem.mapToItem(tree, 0, 0).y + tree.anchors.topMargin : 0 + when: highlightLoader.status === Loader.Ready + } + } + + function indicatorToString(handle){ + switch (handle){ + case StatusTreeView.Handle.Triangle: return "▶"; + case StatusTreeView.Handle.TriangleSmall: return "►"; + case StatusTreeView.Handle.TriangleOutline: return "▷"; + case StatusTreeView.Handle.TriangleSmallOutline: return "⊳"; + case StatusTreeView.Handle.Chevron: return "❱"; + case StatusTreeView.Handle.Arrow: return "➤"; + default: return "▶"; + } + } + + + function clean_filter() + { + root.filter_model_by_id(filter_all_) + tree.unfilter() + } + + function filter_model_by_id(entityId) + { + if (current_filter_ != entityId) + { + current_filter_ = entityId + model.filter_proxy(current_filter_) + root.entity_status_filtered() + tree.filter(current_filter_) + } + } +} diff --git a/qml/StatusTreeViewItem.qml b/qml/StatusTreeViewItem.qml new file mode 100644 index 00000000..244c06fb --- /dev/null +++ b/qml/StatusTreeViewItem.qml @@ -0,0 +1,414 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2021 Maurizio Ingrassia + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +// Copyright 2023 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// This file is part of eProsima Fast DDS Monitor. +// +// eProsima Fast DDS Monitor 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 3 of the License, or +// (at your option) any later version. +// +// eProsima Fast DDS Monitor 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 eProsima Fast DDS Monitor. If not, see <https://www.gnu.org/licenses/>. + +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 1.15 + +import Theme 1.0 + +Item { + id: root + + // model properties + + property var model + property var parentIndex + property var childCount + + property var currentItem: null + property var selectedIndex: null + property var hoveredIndex: null + + // layout properties + + property bool selectionEnabled: false + property bool hoverEnabled: false + + property int itemLeftPadding: 0 + property int rowHeight: 24 + property int rowPadding: 30 + property int rowSpacing: 6 + + property color color: "black" + property color inactive: "#808080" + property color handleColor: color + property color hoverColor: "lightgray" + property color selectedColor: "silver" + property color selectedItemColor: color + + property string defaultIndicator: "▶" + property FontMetrics fontMetrics: FontMetrics { + font.pointSize: Theme.font.pointSize + } + property alias font: root.fontMetrics.font + + // private (internal) signals + signal filter_(int entityId) + signal unfilter_() + + implicitWidth: parent.width + implicitHeight: childrenRect.height + + // Components + + property Component handle: Rectangle { + id: handle + + implicitWidth: 20 + implicitHeight: 20 + Layout.leftMargin: parent.spacing + rotation: currentRow.expanded ? 90 : 0 + opacity: currentRow.hasChildren + color: "transparent" + + Text { + anchors.centerIn: parent + text: defaultIndicator + font: root.font + antialiasing: true + color: currentRow.isSelectedIndex ? root.selectedItemColor : root.handleColor + } + } + + property Component contentItem: Item { + id: contentData + + IconSVG { + id: status_icon + visible: !(currentRow.currentId === "all" && currentRow.currentKind === "INVALID") + && currentRow.currentStatus != "OK" + anchors.left: parent.left; anchors.leftMargin: -5 + anchors.verticalCenter: parent.verticalCenter + name: currentRow.currentStatus == "ERROR" ? "error" + : currentRow.currentStatus == "WARNING" ? "issues" : "" + color: currentRow.currentAlive ? currentRow.currentStatus == "ERROR" ? "red" :"black" : "grey" + size: 15 + } + + Text { + id: entity_name + anchors.left: status_icon.right; anchors.leftMargin: 5 + anchors.verticalCenter: parent.verticalCenter + verticalAlignment: Text.AlignVCenter + + color:currentRow.currentAlive ? currentRow.isSelectedIndex ? root.selectedItemColor : root.color : root.inactive + text: currentRow.currentData + font: root.font + } + + Text { + id: id_value + anchors.left: entity_name.right; anchors.leftMargin: 10 + anchors.verticalCenter: parent.verticalCenter + verticalAlignment: Text.AlignVCenter + + color:currentRow.currentAlive ? currentRow.isSelectedIndex ? root.selectedItemColor : root.color : root.inactive + text: currentRow.currentId == undefined || currentRow.currentValue == undefined ? "" : + currentRow.currentId != "all" && currentRow.currentKind === "INVALID" ? + currentRow.currentId === "all" ? "" : "(" + currentRow.currentValue + ")" : currentRow.currentValue + font: root.font + } + + Text { + id: description + anchors.left: id_value.right; anchors.leftMargin: 20 + anchors.right: parent.right; anchors.rightMargin: 10 + anchors.verticalCenter: parent.verticalCenter + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignRight + elide: Text.ElideRight + height: parent.height + + color:currentRow.currentAlive ? currentRow.isSelectedIndex ? root.selectedItemColor : root.color : root.inactive + text: currentRow.currentId != "all" && currentRow.currentKind === "INVALID" ? + currentRow.currentId === "all" ? "" : + "Entity ID: " + currentRow.currentId : currentRow.currentDescription + font.pointSize: Theme.font.pointSize + font.italic: true + onLinkActivated: Qt.openUrlExternally(link) + + MouseArea { + visible: currentRow.currentDescription.includes("href") + anchors.fill: parent + acceptedButtons: Qt.NoButton + cursorShape: Qt.PointingHandCursor + } + } + } + + property Component hoverComponent: Rectangle { + width: parent.width + height: parent.height + color: currentRow.isHoveredIndex && !currentRow.isSelectedIndex ? root.hoverColor : "transparent" + } + + // Body + + ColumnLayout { + width: parent.width + spacing: 0 + + Repeater { + id: repeater + model: childCount + Layout.fillWidth: true + + delegate: ColumnLayout { + id: itemColumn + + Layout.leftMargin: itemLeftPadding + spacing: 0 + + QtObject { + id: _prop + + property var currentIndex: root.model.index(index, 0, parentIndex) + property var currentData: root.model.data(currentIndex) + property var currentId: root.model.id(currentIndex) + property var currentStatus: root.model.status(currentIndex) + property var currentKind: root.model.kind(currentIndex) + property var currentValue: root.model.value(currentIndex) + property var currentDescription: root.model.description(currentIndex) + property var currentAlive: root.model.alive(currentIndex) + property Item currentItem: repeater.itemAt(index) + property bool expanded: true + property bool selected: false + property int itemChildCount: root.model.rowCount(currentIndex) + readonly property int depth: root.model.depth(currentIndex) + readonly property bool hasChildren: itemChildCount > 0 + readonly property bool isSelectedIndex: root.selectionEnabled && currentIndex === root.selectedIndex + readonly property bool isHoveredIndex: root.hoverEnabled && currentIndex === root.hoveredIndex + readonly property bool isSelectedAndHoveredIndex: hoverEnabled && selectionEnabled && isHoveredIndex && isSelectedIndex + + function toggle(){ + if(_prop.hasChildren) + { + _prop.expanded = !_prop.expanded + } + + _prop.focus() + } + + function focus(){ + controller.endpoint_click(_prop.currentId) + controller.participant_click(_prop.currentId) + } + } + + Connections { + target: root.model + ignoreUnknownSignals: true + function onLayoutChanged() { + const parent = root.model.index(index, 0, parentIndex) + _prop.itemChildCount = root.model.rowCount(parent) + // refresh counter + var new_value = root.model.value(_prop.currentIndex) + if (new_value != undefined) + { + _prop.currentValue = new_value + } + } + } + + Connections { + target: root + function onFilter_(entityId) { + if (_prop.currentId == entityId) + { + _prop.expanded = true + } + } + + function onUnfilter_(){ + _prop.expanded = false + } + } + + Item { + id: column + + Layout.fillWidth: true + + width: row.implicitWidth + height: Math.max(row.implicitHeight, root.rowHeight) + + RowLayout { + id: row + + anchors.fill: parent + Layout.fillHeight: true + + z: 1 + spacing: root.rowSpacing + + // handle + Loader { + id: indicatorLoader + sourceComponent: handle + + Layout.leftMargin: parent.spacing + + property QtObject currentRow: _prop + + TapHandler { onSingleTapped: _prop.toggle() } + } + + // Content + Loader { + id: contentItemLoader + sourceComponent: contentItem + + Layout.fillWidth: true + height: rowHeight + + property QtObject currentRow: _prop + + Connections { + target: root.model + ignoreUnknownSignals: true + function onDataChanged(modelIndex) { + const changedId = modelIndex.internalId + const currentId = _prop.currentIndex.internalId + if(changedId === currentId){ + contentItemLoader.currentRow.currentData = root.model.data(modelIndex); + } + } + } + } + + TapHandler { + onDoubleTapped: _prop.toggle() + onSingleTapped: { + _prop.focus() + root.currentItem = _prop.currentItem + root.selectedIndex = _prop.currentIndex + } + } + } + + Loader { + id: hoverLoader + sourceComponent: hoverComponent + + width: row.width + (1 + _prop.depth * rowPadding) + height: parent.height + + x: -(_prop.depth * rowPadding) + z: 0 + + clip: false + + property QtObject currentRow: _prop + + HoverHandler { + onHoveredChanged: { + if(root.hoverEnabled){ + if(hovered && root.hoveredIndex !== _prop.currentIndex) + root.hoveredIndex = _prop.currentIndex + if(!hovered && root.hoveredIndex === _prop.currentIndex) + root.hoveredIndex = null + } + } + } + } + + } + + // loader to populate the children row for each node + Loader { + id: loader + + Layout.fillWidth: true + + source: "StatusTreeViewItem.qml" + visible: _prop.expanded + + onLoaded: { + item.model = root.model + item.parentIndex = _prop.currentIndex + item.childCount = _prop.itemChildCount + } + + Connections { + target: root.model + ignoreUnknownSignals: true + function onLayoutChanged() { + const parent = root.model.index(index, 0, parentIndex) + loader.item.childCount = root.model.rowCount(parent) + } + } + + Binding { target: loader.item; property: "model"; value: root.model; when: loader.status == Loader.Ready } + Binding { target: loader.item; property: "handle"; value: root.handle; when: loader.status == Loader.Ready } + Binding { target: loader.item; property: "contentItem"; value: root.contentItem; when: loader.status == Loader.Ready } + + Binding { target: loader.item; property: "currentItem"; value: root.currentItem; when: loader.status == Loader.Ready } + Binding { target: loader.item; property: "selectedIndex"; value: root.selectedIndex; when: loader.status == Loader.Ready } + Binding { target: root; property: "currentItem"; value: loader.item.currentItem; when: loader.status == Loader.Ready } + Binding { target: root; property: "selectedIndex"; value: loader.item.selectedIndex; when: loader.status == Loader.Ready } + + Binding { target: loader.item; property: "color"; value: root.color; when: loader.status == Loader.Ready } + Binding { target: loader.item; property: "handleColor"; value: root.handleColor; when: loader.status == Loader.Ready } + Binding { target: loader.item; property: "hoverEnabled"; value: root.hoverEnabled; when: loader.status == Loader.Ready } + Binding { target: loader.item; property: "hoverColor"; value: root.hoverColor; when: loader.status == Loader.Ready } + Binding { target: loader.item; property: "selectionEnabled"; value: root.selectionEnabled; when: loader.status == Loader.Ready } + Binding { target: loader.item; property: "selectedColor"; value: root.selectedColor; when: loader.status == Loader.Ready } + Binding { target: loader.item; property: "selectedItemColor"; value: root.selectedItemColor; when: loader.status == Loader.Ready } + + Binding { target: loader.item; property: "itemLeftPadding"; value: root.rowPadding; when: loader.status == Loader.Ready } + Binding { target: loader.item; property: "rowHeight"; value: root.rowHeight; when: loader.status == Loader.Ready } + Binding { target: loader.item; property: "rowPadding"; value: root.rowPadding; when: loader.status == Loader.Ready } + Binding { target: loader.item; property: "rowSpacing"; value: root.rowSpacing; when: loader.status == Loader.Ready } + Binding { target: loader.item; property: "fontMetrics"; value: root.selectedItemColor; when: loader.status == Loader.Ready } + } + } + } + } + + function filter(entityId) { + root.filter_(entityId) + } + + function unfilter() { + root.unfilter_() + } +} diff --git a/qml/TabLayout.qml b/qml/TabLayout.qml index 8de5cfaf..be26a7d6 100644 --- a/qml/TabLayout.qml +++ b/qml/TabLayout.qml @@ -19,12 +19,18 @@ import QtQuick 2.15 import QtQuick.Controls 2.15 import QtQuick.Layouts 1.15 +import Theme 1.0 + Item { id: tabLayout // Public properties property bool fullScreen: false // ChartsLayout inherited var + // Public signals + signal openEntitiesMenu(string domainEntityId, string entityId, string currentAlias, string entityKind) + signal openTopicMenu(string domainEntityId, string domainId, string entityId, string currentAlias, string entityKind) + // Private properties property int current_: 0 // current tab displayed property int last_index_: 1 // force unique idx on QML components @@ -34,6 +40,7 @@ Item { // private signals signal open_domain_view_(int stack_id, int entity_id, int domain_id) signal initialize_domain_view_(int stack_id, int entity_id, int domain_id) + signal filter_domain_view_by_topic_(int stack_id, int domain_entity_id, string topic_id) // Read only design properties readonly property int max_tabs_: 15 @@ -95,10 +102,38 @@ Item { width: childrenRect.width spacing: 60 Button { + id: chart_button width: 400; height: 400 + background: Rectangle { + color: disable_chart_selection_ ? Theme.lightGrey : Theme.whiteSmoke + border.width: 3 + border.color: disable_chart_selection_ ? Theme.grey : + chart_button.hovered ? Theme.eProsimaLightBlue : Theme.eProsimaDarkBlue + radius: 40 + + Image { + anchors.centerIn: parent + anchors.verticalCenterOffset: -50 + smooth: true + source: "/resources/images/graphs.svg/" + property int size: 25 + sourceSize.width: size * 16 + sourceSize.height: size * 9 + } + + Text { + anchors.horizontalCenter: parent.horizontalCenter + anchors.bottom: parent.bottom; anchors.bottomMargin: 50 + width: parent.width + text: "Chart View" + horizontalAlignment: Text.AlignHCenter + font.pointSize: 20 + color: disable_chart_selection_ ? Theme.grey : + chart_button.hovered ? Theme.eProsimaLightBlue : Theme.eProsimaDarkBlue + } + } anchors.verticalCenter: parent.verticalCenter enabled: !disable_chart_selection_ - text: "Chart View" onClicked: { if (!disable_chart_selection_) { @@ -114,9 +149,35 @@ Item { } } Button { + id: domain_view_button width: 400; height: 400 + background: Rectangle { + color: Theme.whiteSmoke + border.width: 3 + border.color: domain_view_button.hovered ? Theme.eProsimaLightBlue : Theme.eProsimaDarkBlue + radius: 40 + + Image { + anchors.centerIn: parent + anchors.verticalCenterOffset: -50 + smooth: true + source: "/resources/images/domain_graph.svg/" + property int size: 30 + sourceSize.width: size * 16 + sourceSize.height: size * 9 + } + + Text { + anchors.horizontalCenter: parent.horizontalCenter + anchors.bottom: parent.bottom; anchors.bottomMargin: 50 + width: parent.width + text: "Domain View" + horizontalAlignment: Text.AlignHCenter + font.pointSize: 20 + color: domain_view_button.hovered ? Theme.eProsimaLightBlue : Theme.eProsimaDarkBlue + } + } anchors.verticalCenter: parent.verticalCenter - text: "Domain View" onClicked: { if (mainApplicationView.monitors == 0) { @@ -149,34 +210,56 @@ Item { component_id: stack.stack_id onUpdate_tab_name: { - tabLayout.tab_model_[current_]["title"] = new_name - - // update model to set the visual change - tab_list.model = tabLayout.tab_model_ - - // update left panel information - for (var i=0; i<stack_layout.count; i++) + for (var i = 0; i<tabLayout.tab_model_.length; i++) { - if (stack_layout.children[i].stack_id == tabLayout.tab_model_[current_]["stack_id"] && - stack_layout.children[i].currentItem.entity_id > 0) + if (tabLayout.tab_model_[i]["stack_id"] == stack_id) { - controller.domain_click(stack_layout.children[i].currentItem.entity_id) - break; + tabLayout.tab_model_[i]["title"] = new_name + + // update model to set the visual change + tab_list.model = tabLayout.tab_model_ + + // update left panel information + for (var j=0; j<stack_layout.count; j++) + { + if (stack_layout.children[j].stack_id == tabLayout.tab_model_[i]["stack_id"] && + stack_layout.children[j].currentItem.domain_entity_id > 0) + { + controller.domain_click(stack_layout.children[j].currentItem.domain_entity_id) + break; + } + } + break // exit loop } } } + onOpenEntitiesMenu: { + tabLayout.openEntitiesMenu(domainEntityId, entityId, currentAlias, entityKind) + } + onOpenTopicMenu: { + tabLayout.openTopicMenu(domainEntityId, domainId, entityId, currentAlias, entityKind) + } + Connections { target: tabLayout function onInitialize_domain_view_(stack_id, entity_id, domain_id) { if (domainGraphLayout.component_id == stack_id) { - domainGraphLayout.entity_id = entity_id + domainGraphLayout.domain_entity_id = entity_id domainGraphLayout.domain_id = domain_id domainGraphLayout.load_model() } } + + function onFilter_domain_view_by_topic_(stack_id, domain_entity_id, topic_id) { + if (domainGraphLayout.component_id == stack_id && + domainGraphLayout.domain_entity_id == domain_entity_id) + { + domainGraphLayout.filter_model_by_topic(topic_id) + } + } } } @@ -185,7 +268,7 @@ Item { Connections { target: tabLayout - function onOpen_domain_view_(stack_id, entity_id, domain_id) { + function onOpen_domain_view_(stack_id, entity_id, domain_id, topic_id) { if (stack.stack_id == stack_id) { if (stack.deep > 1) @@ -423,9 +506,10 @@ Item { { for (var i=0; i<stack_layout.count; i++) { - if (stack_layout.children[i].stack_id == tabLayout.tab_model_[idx]["stack_id"]) + if (stack_layout.children[i].stack_id == tabLayout.tab_model_[idx]["stack_id"] && + stack_layout.children[i].currentItem.domain_entity_id > 0) { - controller.domain_click(stack_layout.children[i].currentItem.entity_id) + controller.domain_click(stack_layout.children[i].currentItem.domain_entity_id) break; } } @@ -540,4 +624,23 @@ Item { function chartsLayout_saveAllCSV() { chartsLayout.saveAllCSV() } + + function open_topic_view(domainEntityId, domainId, entityId) { + create_new_tab() + open_domain_view_(tabLayout.tab_model_[current_]["stack_id"], domainEntityId, domainId) + filter_domain_view_by_topic_(tabLayout.tab_model_[current_]["stack_id"], domainEntityId, entityId) + } + + function refresh_domain_graph_view(domainEntityId, entityId) { + for (var i=0; i<stack_layout.count; i++) + { + if (stack_layout.children[i].currentItem.domain_entity_id != undefined) + { + if (stack_layout.children[i].currentItem.contains_entity(domainEntityId, entityId)) + { + stack_layout.children[i].currentItem.load_model() + } + } + } + } } diff --git a/qml/TopicMenu.qml b/qml/TopicMenu.qml new file mode 100644 index 00000000..b1fd64c3 --- /dev/null +++ b/qml/TopicMenu.qml @@ -0,0 +1,45 @@ +// Copyright 2023 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// This file is part of eProsima Fast DDS Monitor. +// +// eProsima Fast DDS Monitor 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 3 of the License, or +// (at your option) any later version. +// +// eProsima Fast DDS Monitor 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 eProsima Fast DDS Monitor. If not, see <https://www.gnu.org/licenses/>. + +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import Theme 1.0 + +/* + Menu containing the possible actions that can be performed on topic entities. + */ +Menu { + id: topicMenu + property string domainEntityId: "" + property string domainId: "" + property string entityId: "" + property string currentAlias: "" + property string entityKind: "" + + MenuItem { + text: "Change alias" + onTriggered: changeAlias(menu.domainEntityId, menu.entityId, menu.currentAlias, menu.entityKind) + } + MenuItem { + text: "View problems" + onTriggered: filterEntityStatusLog(menu.entityId) + } + MenuItem { + text: "Filter graph view" + onTriggered: openTopicView(menu.domainEntityId, menu.domainId, menu.entityId) + } +} diff --git a/resources/images/domain_graph.svg b/resources/images/domain_graph.svg new file mode 100644 index 00000000..45bc9b26 --- /dev/null +++ b/resources/images/domain_graph.svg @@ -0,0 +1,76 @@ +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="1920" height="1080" style="shape-rendering: geometricprecision; text-rendering: geometricprecision; fill-rule: evenodd; clip-rule: evenodd;"><rect id="backgroundrect" width="100%" height="100%" x="0" y="0" fill="none" stroke="none"/> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +<g class="currentLayer" style=""><title>Layer 1</title><g id="svg_1" class="selected" fill-opacity="0" fill="#000000"><path style="opacity:1" fill="#000000" d="M1.038461446762085,-2.038461446762085 C641.0384614467621,-2.038461446762085 1281.038461446762,-2.038461446762085 1921.038461446762,-2.038461446762085 C1921.038461446762,357.9615385532379 1921.038461446762,717.9615385532379 1921.038461446762,1077.961538553238 C1281.038461446762,1077.961538553238 641.0384614467621,1077.961538553238 1.038461446762085,1077.961538553238 C1.038461446762085,717.9615385532379 1.038461446762085,357.9615385532379 1.038461446762085,-2.038461446762085 z" id="svg_2"/></g><g id="svg_3"><path style="opacity:1" fill="#0c95cd" d="M 418.5,88.5 C 476.501,88.3333 534.501,88.5 592.5,89C 602.269,91.4579 609.102,97.2912 613,106.5C 613.667,137.167 613.667,167.833 613,198.5C 610.288,207.548 604.454,213.714 595.5,217C 536.167,217.667 476.833,217.667 417.5,217C 407.809,213.311 401.643,206.478 399,196.5C 398.333,167.5 398.333,138.5 399,109.5C 401.95,99.0551 408.45,92.0551 418.5,88.5 Z" id="svg_4"/></g><g id="svg_5"><path style="opacity:1" fill="#bbbbbb" d="M 941.5,88.5 C 1001.25,109.694 1060.91,131.194 1120.5,153C 1062.57,173.975 1004.74,195.142 947,216.5C 946.5,280.166 946.333,343.832 946.5,407.5C 943.833,407.5 941.167,407.5 938.5,407.5C 938.5,343.5 938.5,279.5 938.5,215.5C 880.602,195.367 822.935,174.533 765.5,153C 824.227,131.372 882.893,109.872 941.5,88.5 Z" id="svg_6"/></g><g id="svg_7"><path style="opacity:1" fill="#bbbbbb" d="M 1346.5,544.5 C 1341.68,543.821 1337.01,542.821 1332.5,541.5C 1333.83,541.5 1335.17,541.5 1336.5,541.5C 1336.5,538.833 1336.5,536.167 1336.5,533.5C 1088.17,533.333 839.833,533.5 591.5,534C 594.423,536.045 597.257,538.212 600,540.5C 599.886,549.104 596.386,550.604 589.5,545C 583.312,540.303 577.312,535.469 571.5,530.5C 571.5,529.167 571.5,527.833 571.5,526.5C 575.873,522.457 580.539,518.624 585.5,515C 589.06,512.597 592.727,510.43 596.5,508.5C 600.254,509.852 601.421,512.518 600,516.5C 597.257,518.788 594.423,520.955 591.5,523C 839.833,523.5 1088.17,523.667 1336.5,523.5C 1336.67,489.498 1336.5,455.498 1336,421.5C 1328.51,428.499 1320.34,434.499 1311.5,439.5C 1307.72,438.563 1306.22,436.229 1307,432.5C 1309.44,430.371 1311.94,428.371 1314.5,426.5C 1315.29,426.217 1315.96,425.717 1316.5,425C 1068.17,424.5 819.834,424.333 571.5,424.5C 570.167,421.5 570.167,418.5 571.5,415.5C 819.834,415.667 1068.17,415.5 1316.5,415C 1312.98,412.48 1309.82,409.647 1307,406.5C 1306.56,400.96 1309.06,399.127 1314.5,401C 1322.02,406.495 1329.19,412.329 1336,418.5C 1336.5,350.834 1336.67,283.167 1336.5,215.5C 1279.01,195.17 1221.68,174.336 1164.5,153C 1223.26,131.303 1282.09,109.803 1341,88.5C 1400.22,109.861 1459.39,131.361 1518.5,153C 1461.5,173.833 1404.5,194.667 1347.5,215.5C 1346.5,325.165 1346.17,434.831 1346.5,544.5 Z" id="svg_8"/></g><g id="svg_9"><path style="opacity:1" fill="#fafdfe" d="M 494.5,101.5 C 525.916,99.1205 546.416,112.787 556,142.5C 559.207,169.596 548.707,189.096 524.5,201C 497.11,209.983 475.61,202.483 460,178.5C 445.951,141.271 457.451,115.604 494.5,101.5 Z" id="svg_10"/></g><g id="svg_11"><path style="opacity:1" fill="#1797ce" d="M 498.5,104.5 C 531.431,104.271 549.764,120.437 553.5,153C 553.504,163.32 550.504,172.654 544.5,181C 539.64,177.559 534.307,174.559 528.5,172C 512.833,171.333 497.167,171.333 481.5,172C 475.853,174.148 470.853,177.315 466.5,181.5C 452.688,161.205 453.188,141.205 468,121.5C 476.571,112.802 486.737,107.135 498.5,104.5 Z" id="svg_12"/></g><g id="svg_13"><path style="opacity:1" fill="#fdfdfd" d="M 932.5,110.5 C 935.044,110.104 937.211,110.771 939,112.5C 939.159,119.87 938.659,127.203 937.5,134.5C 943.833,134.5 950.167,134.5 956.5,134.5C 956.818,128.105 957.652,121.772 959,115.5C 961.164,110.338 964.498,109.338 969,112.5C 969.042,119.586 968.209,126.586 966.5,133.5C 971.802,134.329 977.136,134.829 982.5,135C 986.436,138.159 986.436,141.492 982.5,145C 976.472,145.17 970.472,145.67 964.5,146.5C 963.097,151.392 962.43,156.392 962.5,161.5C 969.508,161.334 976.508,161.5 983.5,162C 986.343,165.619 986.01,168.952 982.5,172C 974.841,172.5 967.174,172.666 959.5,172.5C 958.632,180.221 956.965,187.721 954.5,195C 949.542,196.69 946.542,195.023 945.5,190C 946.919,184.231 947.919,178.397 948.5,172.5C 942.167,172.5 935.833,172.5 929.5,172.5C 928.552,179.519 927.386,186.519 926,193.5C 923,196.167 920,196.167 917,193.5C 916.79,186.463 917.29,179.463 918.5,172.5C 913.489,172.666 908.489,172.499 903.5,172C 899.99,168.952 899.657,165.619 902.5,162C 908.825,161.5 915.158,161.334 921.5,161.5C 921.949,156.137 922.616,150.804 923.5,145.5C 916.825,145.666 910.158,145.5 903.5,145C 900.713,142.833 899.88,139.999 901,136.5C 901.75,135.874 902.584,135.374 903.5,135C 911.159,134.5 918.826,134.334 926.5,134.5C 926.789,128.432 927.622,122.432 929,116.5C 929.564,114.113 930.73,112.113 932.5,110.5 Z" id="svg_14"/></g><g id="svg_15" class=""><path style="opacity:1" fill="#fdfdfd" d="M 1330.5,110.5 C 1333.69,109.961 1336.19,110.961 1338,113.5C 1337.61,120.52 1336.78,127.52 1335.5,134.5C 1341.83,134.5 1348.17,134.5 1354.5,134.5C 1355.53,127.821 1356.69,121.154 1358,114.5C 1359.33,110.896 1361.83,109.729 1365.5,111C 1367.12,112.117 1368.12,113.617 1368.5,115.5C 1367.34,121.821 1366.34,128.154 1365.5,134.5C 1370.84,134.334 1376.18,134.501 1381.5,135C 1384.17,138.333 1384.17,141.667 1381.5,145C 1375.18,145.5 1368.84,145.666 1362.5,145.5C 1362.15,150.878 1361.49,156.211 1360.5,161.5C 1367.51,161.334 1374.51,161.5 1381.5,162C 1384.09,165.297 1384.09,168.631 1381.5,172C 1373.83,172.333 1366.17,172.667 1358.5,173C 1356.96,180.393 1355.29,187.726 1353.5,195C 1351.17,195.667 1348.83,195.667 1346.5,195C 1345.87,194.25 1345.37,193.416 1345,192.5C 1344.74,186.043 1345.58,179.709 1347.5,173.5C 1341.17,172.167 1334.83,172.167 1328.5,173.5C 1327.32,180.579 1325.82,187.579 1324,194.5C 1318.43,197.044 1315.26,195.377 1314.5,189.5C 1315.78,183.872 1316.78,178.205 1317.5,172.5C 1312.49,172.666 1307.49,172.499 1302.5,172C 1297.77,169.078 1297.43,165.745 1301.5,162C 1307.5,161.667 1313.5,161.333 1319.5,161C 1320.64,156.22 1321.64,151.386 1322.5,146.5C 1315.52,145.669 1308.52,145.169 1301.5,145C 1297.27,141.157 1297.6,137.823 1302.5,135C 1309.83,134.5 1317.16,134.334 1324.5,134.5C 1325.71,127.856 1326.88,121.189 1328,114.5C 1328.66,113.016 1329.5,111.683 1330.5,110.5 Z" id="svg_16"/></g><g id="svg_17"><path style="opacity:1" fill="#fdfefe" d="M 497.5,120.5 C 511.852,118.091 522.018,123.424 528,136.5C 530.71,156.98 521.877,167.48 501.5,168C 485.541,163.615 479.041,153.115 482,136.5C 485.355,129.313 490.522,123.979 497.5,120.5 Z" id="svg_18"/></g><g id="svg_19"><path style="opacity:1" fill="#bcbcbc" d="M 934.5,145.5 C 940.833,145.5 947.167,145.5 953.5,145.5C 952.554,150.793 951.887,156.126 951.5,161.5C 945.167,161.5 938.833,161.5 932.5,161.5C 932.845,156.122 933.512,150.789 934.5,145.5 Z" id="svg_20"/></g><g id="svg_21"><path style="opacity:1" fill="#bcbcbc" d="M 1333.5,145.5 C 1339.84,145.334 1346.18,145.5 1352.5,146C 1351.02,151.046 1350.02,156.213 1349.5,161.5C 1343.17,161.5 1336.83,161.5 1330.5,161.5C 1331.25,156.087 1332.25,150.754 1333.5,145.5 Z" id="svg_22"/></g><g id="svg_23"><path style="opacity:1" fill="#babbba" d="M 511.5,271.5 C 508.167,271.5 504.833,271.5 501.5,271.5C 501.5,253.833 501.5,236.167 501.5,218.5C 504.833,218.5 508.167,218.5 511.5,218.5C 511.5,236.167 511.5,253.833 511.5,271.5 Z" id="svg_24"/></g><g id="svg_25"><path style="opacity:1" fill="#89ce2b" d="M 501.5,271.5 C 504.833,271.5 508.167,271.5 511.5,271.5C 531.34,271.17 551.007,271.503 570.5,272.5C 570.5,298.5 570.5,324.5 570.5,350.5C 550.833,350.5 531.167,350.5 511.5,350.5C 508.167,350.5 504.833,350.5 501.5,350.5C 481.833,350.5 462.167,350.5 442.5,350.5C 442.5,324.167 442.5,297.833 442.5,271.5C 462.167,271.5 481.833,271.5 501.5,271.5 Z" id="svg_26"/></g><g id="svg_27"><path style="opacity:1" fill="#f9fcf5" d="M 522.5,278.5 C 523.552,278.351 524.552,278.517 525.5,279C 529.667,283.167 533.833,287.333 538,291.5C 538.315,292.908 537.982,294.241 537,295.5C 522.138,310.696 506.971,325.529 491.5,340C 485.189,341.661 478.856,343.161 472.5,344.5C 473.049,337.635 474.549,330.968 477,324.5C 492.368,309.299 507.535,293.965 522.5,278.5 Z" id="svg_28"/></g><g id="svg_29"><path style="opacity:1" fill="#8dd032" d="M 523.5,283.5 C 526.812,286.146 529.812,289.146 532.5,292.5C 531.5,294.167 530.167,295.5 528.5,296.5C 525.349,293.85 522.349,291.016 519.5,288C 521.041,286.629 522.375,285.129 523.5,283.5 Z" id="svg_30"/></g><g id="svg_31"><path style="opacity:1" fill="#89ce2a" d="M 515.5,291.5 C 519.235,293.725 522.568,296.558 525.5,300C 514,311.5 502.5,323 491,334.5C 487.414,331.616 484.748,328.283 483,324.5C 494.035,313.632 504.869,302.632 515.5,291.5 Z" id="svg_32"/></g><g id="svg_33"><path style="opacity:1" fill="#bbbbbb" d="M 916.5,317.5 C 917.289,317.217 917.956,316.717 918.5,316C 802.834,315.5 687.167,315.333 571.5,315.5C 571.5,312.5 571.5,309.5 571.5,306.5C 686.834,306.667 802.167,306.5 917.5,306C 916.956,305.283 916.289,304.783 915.5,304.5C 912.421,302.466 910.088,299.8 908.5,296.5C 909.217,292.475 911.55,290.975 915.5,292C 921.655,296.911 927.989,301.577 934.5,306C 938.035,308.838 938.368,312.005 935.5,315.5C 929.024,320.142 922.691,324.975 916.5,330C 910.507,331.668 908.007,329.502 909,323.5C 911.439,321.371 913.939,319.371 916.5,317.5 Z" id="svg_34"/></g><g id="svg_35"><path style="opacity:1" fill="#eceee8" d="M 570.5,272.5 C 571.495,283.321 571.828,294.321 571.5,305.5C 686.335,305.833 801.001,305.5 915.5,304.5C 916.289,304.783 916.956,305.283 917.5,306C 802.167,306.5 686.834,306.667 571.5,306.5C 571.5,309.5 571.5,312.5 571.5,315.5C 571.5,315.833 571.5,316.167 571.5,316.5C 571.828,328.012 571.495,339.346 570.5,350.5C 570.5,324.5 570.5,298.5 570.5,272.5 Z" id="svg_36"/></g><g id="svg_37"><path style="opacity:1" fill="#cccccc" d="M 571.5,316.5 C 571.5,316.167 571.5,315.833 571.5,315.5C 687.167,315.333 802.834,315.5 918.5,316C 917.956,316.717 917.289,317.217 916.5,317.5C 801.668,316.5 686.668,316.167 571.5,316.5 Z" id="svg_38"/></g><g id="svg_39"><path style="opacity:1" fill="#95d343" d="M 479.5,330.5 C 482.407,331.734 484.741,333.734 486.5,336.5C 483.581,337.314 480.748,338.314 478,339.5C 478.005,336.464 478.505,333.464 479.5,330.5 Z" id="svg_40"/></g><g id="svg_41"><path style="opacity:1" fill="#babbba" d="M 501.5,350.5 C 504.833,350.5 508.167,350.5 511.5,350.5C 511.5,360.5 511.5,370.5 511.5,380.5C 508.167,380.5 504.833,380.5 501.5,380.5C 501.5,370.5 501.5,360.5 501.5,350.5 Z" id="svg_42"/></g><g id="svg_43"><path style="opacity:1" fill="#89ce2a" d="M 501.5,380.5 C 504.833,380.5 508.167,380.5 511.5,380.5C 531.5,380.5 551.5,380.5 571.5,380.5C 571.5,392.167 571.5,403.833 571.5,415.5C 570.167,418.5 570.167,421.5 571.5,424.5C 571.5,424.833 571.5,425.167 571.5,425.5C 571.5,436.833 571.5,448.167 571.5,459.5C 551.5,459.5 531.5,459.5 511.5,459.5C 508.167,459.5 504.833,459.5 501.5,459.5C 481.833,459.5 462.167,459.5 442.5,459.5C 442.5,433.167 442.5,406.833 442.5,380.5C 462.167,380.5 481.833,380.5 501.5,380.5 Z" id="svg_44"/></g><g id="svg_45"><path style="opacity:1" fill="#fbfdf8" d="M 523.5,386.5 C 528.74,390.233 533.574,394.566 538,399.5C 538.667,400.5 538.667,401.5 538,402.5C 522.833,417.667 507.667,432.833 492.5,448C 486.182,450.113 479.848,451.78 473.5,453C 473.108,446.363 474.275,439.863 477,433.5C 492.702,417.965 508.202,402.299 523.5,386.5 Z" id="svg_46"/></g><g id="svg_47"><path style="opacity:1" fill="#8ed034" d="M 523.5,392.5 C 527.235,394.725 530.568,397.558 533.5,401C 532,402.5 530.5,404 529,405.5C 525.984,402.651 523.15,399.651 520.5,396.5C 521.524,395.148 522.524,393.815 523.5,392.5 Z" id="svg_48"/></g><g id="svg_49"><path style="opacity:1" fill="#89ce2c" d="M 516.5,399.5 C 520.373,402.186 523.206,405.519 525,409.5C 514.167,420.333 503.333,431.167 492.5,442C 491.833,442.667 491.167,442.667 490.5,442C 488.093,439.715 485.593,437.549 483,435.5C 482.333,434.833 482.333,434.167 483,433.5C 494.369,422.298 505.535,410.965 516.5,399.5 Z" id="svg_50"/></g><g id="svg_51" class="" fill-opacity="0" fill="#000000"><path style="opacity:1" fill="#000000" d="M 571.5,526.5 C 571.5,513.833 571.5,501.167 571.5,488.5C 551.5,488.5 531.5,488.5 511.5,488.5C 511.5,478.833 511.5,469.167 511.5,459.5C 531.5,459.5 551.5,459.5 571.5,459.5C 571.5,448.167 571.5,436.833 571.5,425.5C 819.334,425.167 1067,425.5 1314.5,426.5C 1311.94,428.371 1309.44,430.371 1307,432.5C 1306.22,436.229 1307.72,438.563 1311.5,439.5C 1320.34,434.499 1328.51,428.499 1336,421.5C 1336.5,455.498 1336.67,489.498 1336.5,523.5C 1088.17,523.667 839.833,523.5 591.5,523C 594.423,520.955 597.257,518.788 600,516.5C 601.421,512.518 600.254,509.852 596.5,508.5C 592.727,510.43 589.06,512.597 585.5,515C 580.539,518.624 575.873,522.457 571.5,526.5 Z" id="svg_52"/></g><g id="svg_53"><path style="opacity:1" fill="#ededed" d="M 571.5,425.5 C 571.5,425.167 571.5,424.833 571.5,424.5C 819.834,424.333 1068.17,424.5 1316.5,425C 1315.96,425.717 1315.29,426.217 1314.5,426.5C 1067,425.5 819.334,425.167 571.5,425.5 Z" id="svg_54"/></g><g id="svg_55"><path style="opacity:1" fill="#bbbbbb" d="M 938.5,431.5 C 941.5,431.5 944.5,431.5 947.5,431.5C 947.5,460.167 947.5,488.833 947.5,517.5C 944.5,517.5 941.5,517.5 938.5,517.5C 938.5,488.833 938.5,460.167 938.5,431.5 Z" id="svg_56"/></g><g id="svg_57"><path style="opacity:1" fill="#8dd034" d="M 479.5,439.5 C 482.225,440.713 484.559,442.546 486.5,445C 483.509,446.523 480.509,447.023 477.5,446.5C 478.577,444.271 479.244,441.937 479.5,439.5 Z" id="svg_58"/></g><g id="svg_59"><path style="opacity:1" fill="#babbba" d="M 501.5,459.5 C 504.833,459.5 508.167,459.5 511.5,459.5C 511.5,469.167 511.5,478.833 511.5,488.5C 508.167,488.5 504.833,488.5 501.5,488.5C 501.5,478.833 501.5,469.167 501.5,459.5 Z" id="svg_60"/></g><g id="svg_61"><path style="opacity:1" fill="#ffc007" d="M 442.5,489.5 C 485.167,489.5 527.833,489.5 570.5,489.5C 570.5,515.5 570.5,541.5 570.5,567.5C 527.833,567.5 485.167,567.5 442.5,567.5C 442.5,541.5 442.5,515.5 442.5,489.5 Z" id="svg_62"/></g><g id="svg_63"><path style="opacity:1" fill="#fffcf5" d="M 484.5,498.5 C 489.511,498.334 494.511,498.501 499.5,499C 501.536,499.454 503.37,500.287 505,501.5C 511.525,498.595 518.359,497.762 525.5,499C 532.36,501.233 539.36,502.733 546.5,503.5C 547.666,521.491 547.833,539.491 547,557.5C 546.25,558.126 545.416,558.626 544.5,559C 517.811,559.893 491.144,559.56 464.5,558C 464,557.5 463.5,557 463,556.5C 462.333,539.167 462.333,521.833 463,504.5C 463.5,504 464,503.5 464.5,503C 471.612,502.911 478.279,501.411 484.5,498.5 Z" id="svg_64"/></g><g id="svg_65"><path style="opacity:1" fill="#ffc008" d="M 538.5,505.5 C 538.829,520.01 538.496,534.343 537.5,548.5C 531.062,546.933 524.562,545.599 518,544.5C 514.07,544.754 510.57,546.087 507.5,548.5C 506.334,533.845 506.167,519.178 507,504.5C 509.773,501.781 513.106,500.447 517,500.5C 524.457,501.044 531.624,502.711 538.5,505.5 Z" id="svg_66"/></g><g id="svg_67"><path style="opacity:1" fill="#ffc113" d="M 486.5,500.5 C 492.93,499.604 498.764,500.937 504,504.5C 504.667,519.5 504.667,534.5 504,549.5C 500.325,546.273 495.992,544.606 491,544.5C 484.437,545.6 477.937,546.933 471.5,548.5C 471.167,534.151 471.501,519.818 472.5,505.5C 477.17,503.51 481.837,501.844 486.5,500.5 Z" id="svg_68"/></g><g id="svg_69"><path style="opacity:1" fill="#ffc733" d="M 466.5,504.5 C 467.496,504.414 468.329,504.748 469,505.5C 469.5,521.497 469.667,537.497 469.5,553.5C 493.493,554.703 517.493,554.703 541.5,553.5C 541.5,537.167 541.5,520.833 541.5,504.5C 542.822,504.33 543.989,504.663 545,505.5C 545.667,522.167 545.667,538.833 545,555.5C 518.907,557.95 492.74,558.117 466.5,556C 466,555.5 465.5,555 465,554.5C 464.333,538.5 464.333,522.5 465,506.5C 465.717,505.956 466.217,505.289 466.5,504.5 Z" id="svg_70"/></g><g id="svg_71"><path style="opacity:1" fill="#ffe18a" d="M 538.5,505.5 C 539.826,520.005 539.826,534.672 538.5,549.5C 537.893,549.376 537.56,549.043 537.5,548.5C 538.496,534.343 538.829,520.01 538.5,505.5 Z" id="svg_72"/></g><g id="svg_73"><path style="opacity:1" fill="#feda70" d="M 442.5,489.5 C 461.993,488.503 481.66,488.17 501.5,488.5C 504.833,488.5 508.167,488.5 511.5,488.5C 531.5,488.5 551.5,488.5 571.5,488.5C 571.5,501.167 571.5,513.833 571.5,526.5C 571.5,527.833 571.5,529.167 571.5,530.5C 571.829,543.011 571.495,555.345 570.5,567.5C 570.5,541.5 570.5,515.5 570.5,489.5C 527.833,489.5 485.167,489.5 442.5,489.5 Z" id="svg_74"/></g><g id="svg_75"><path style="opacity:1" fill="#e4e4e4" d="M 939.5,540.5 C 939.5,690.167 939.5,839.833 939.5,989.5C 938.833,964.833 938.167,940.167 937.5,915.5C 937.5,912.5 937.5,909.5 937.5,906.5C 938.167,784.5 938.833,662.5 939.5,540.5 Z" id="svg_76"/></g><g id="svg_77"><path style="opacity:1" fill="#babbba" d="M 939.5,540.5 C 942.167,540.5 944.833,540.5 947.5,540.5C 947.5,690.167 947.5,839.833 947.5,989.5C 944.833,989.5 942.167,989.5 939.5,989.5C 939.5,839.833 939.5,690.167 939.5,540.5 Z" id="svg_78"/></g><g id="svg_79"><path style="opacity:1" fill="#0e497f" d="M 1332.5,541.5 C 1337.01,542.821 1341.68,543.821 1346.5,544.5C 1421.59,565.103 1474.43,611.103 1505,682.5C 1517.95,716.376 1522.28,751.376 1518,787.5C 1509.66,858.368 1476.16,913.868 1417.5,954C 1364.96,986.034 1308.29,996.367 1247.5,985C 1173.29,966.458 1119.79,922.625 1087,853.5C 1069.63,808.009 1065.63,761.343 1075,713.5C 1095.03,638.474 1140.53,585.308 1211.5,554C 1250.8,540.649 1291.13,536.482 1332.5,541.5 Z" id="svg_80"/></g><g id="svg_81"><path style="opacity:1" fill="#ffc936" d="M 486.5,547.5 C 491.449,546.959 496.116,547.792 500.5,550C 492.833,550.667 485.167,550.667 477.5,550C 480.742,549.594 483.742,548.761 486.5,547.5 Z" id="svg_82"/></g><g id="svg_83"><path style="opacity:1" fill="#ffc934" d="M 514.5,547.5 C 520.601,547.572 526.601,548.406 532.5,550C 525.167,550.667 517.833,550.667 510.5,550C 511.858,549.066 513.192,548.232 514.5,547.5 Z" id="svg_84"/></g><g id="svg_85" class=""><path style="opacity:1" fill="#fdfefe" d="M 1271.5,559.5 C 1275.77,559.203 1279.93,559.536 1284,560.5C 1284.94,587.198 1284.6,613.865 1283,640.5C 1279.06,642.15 1274.89,642.983 1270.5,643C 1244.17,643.5 1217.84,643.667 1191.5,643.5C 1201.88,615.282 1219.22,592.115 1243.5,574C 1252.49,568.341 1261.82,563.508 1271.5,559.5 Z" id="svg_86"/></g><g id="svg_87"><path style="opacity:1" fill="#fcfdfd" d="M 1305.5,559.5 C 1311.52,558.965 1317.19,560.132 1322.5,563C 1343.24,572.402 1360.4,586.235 1374,604.5C 1379.4,612.234 1384.4,620.234 1389,628.5C 1391.69,632.879 1393.53,637.546 1394.5,642.5C 1367.18,643.541 1339.84,643.708 1312.5,643C 1309.38,642.794 1306.38,642.127 1303.5,641C 1302.21,615.223 1302.38,589.39 1304,563.5C 1304.79,562.255 1305.29,560.922 1305.5,559.5 Z" id="svg_88"/></g><g id="svg_89"><path style="opacity:1" fill="#fcfdfd" d="M 1374.5,574.5 C 1375.25,573.427 1376.25,573.26 1377.5,574C 1409.32,588.811 1436.15,609.978 1458,637.5C 1459.01,639.025 1459.51,640.692 1459.5,642.5C 1445.83,642.667 1432.16,642.5 1418.5,642C 1411.04,630.913 1404.2,619.413 1398,607.5C 1389.65,596.785 1381.82,585.785 1374.5,574.5 Z" id="svg_90"/></g><g id="svg_91" class="" opacity="1"><path style="" fill="#fbfcfc" d="M 1204.5,577.5 C 1208.44,577.178 1209.27,578.511 1207,581.5C 1200.75,589.657 1194.75,597.99 1189,606.5C 1181.96,618.749 1174.79,630.916 1167.5,643C 1161.27,644.127 1154.94,644.627 1148.5,644.5C 1142.45,644.48 1136.45,644.146 1130.5,643.5C 1129.34,641.45 1129.5,639.45 1131,637.5C 1151.53,612.458 1176.03,592.458 1204.5,577.5 Z" id="svg_92" opacity="1"/></g><g id="svg_93"><path style="opacity:1" fill="#fdfdfe" d="M 1439.5,660.5 C 1450.17,660.333 1460.84,660.5 1471.5,661C 1473.29,661.785 1474.79,662.951 1476,664.5C 1490.71,690.961 1498.71,719.294 1500,749.5C 1499.5,750 1499,750.5 1498.5,751C 1489.55,752.09 1480.55,752.59 1471.5,752.5C 1461.09,752.696 1450.76,752.029 1440.5,750.5C 1439.37,747.93 1438.53,745.264 1438,742.5C 1436.85,716.395 1432.35,690.895 1424.5,666C 1424.59,664.893 1424.92,663.893 1425.5,663C 1430.27,662.017 1434.94,661.183 1439.5,660.5 Z" id="svg_94"/></g><g id="svg_95"><path style="opacity:1" fill="#fdfdfe" d="M 1117.5,662.5 C 1132.2,662.216 1146.87,662.55 1161.5,663.5C 1154.11,689.294 1149.61,715.627 1148,742.5C 1147.92,746.709 1146.42,750.209 1143.5,753C 1126.5,753.667 1109.5,753.667 1092.5,753C 1091.3,752.097 1090.47,750.931 1090,749.5C 1088.81,737.961 1089.81,726.628 1093,715.5C 1097.18,698.605 1103.52,682.605 1112,667.5C 1113.73,665.598 1115.56,663.931 1117.5,662.5 Z" id="svg_96"/></g><g id="svg_97"><path style="opacity:1" fill="#fcfdfd" d="M 1183.5,662.5 C 1216.56,662.021 1249.56,662.521 1282.5,664C 1283.29,693.43 1283.29,722.93 1282.5,752.5C 1243.8,752.815 1205.14,752.482 1166.5,751.5C 1166.65,725.289 1170.48,699.622 1178,674.5C 1179.59,670.328 1181.42,666.328 1183.5,662.5 Z" id="svg_98"/></g><g id="svg_99"><path style="opacity:1" fill="#fdfdfe" d="M 1302.5,662.5 C 1336.5,662.5 1370.5,662.5 1404.5,662.5C 1414.12,690.349 1418.95,719.015 1419,748.5C 1418.02,750.887 1416.19,752.054 1413.5,752C 1378.83,752.667 1344.17,752.667 1309.5,752C 1306.91,751.774 1304.41,751.274 1302,750.5C 1301.53,721.164 1301.7,691.83 1302.5,662.5 Z" id="svg_100"/></g><g id="svg_101"><path style="opacity:1" fill="#0d95cd" d="M 419.5,687.5 C 477.501,687.333 535.501,687.5 593.5,688C 603.165,690.333 609.665,696.166 613,705.5C 613.667,736.167 613.667,766.833 613,797.5C 610.288,806.548 604.454,812.714 595.5,816C 536.167,816.667 476.833,816.667 417.5,816C 407.809,812.311 401.643,805.478 399,795.5C 398.333,766.5 398.333,737.5 399,708.5C 402.016,697.652 408.849,690.652 419.5,687.5 Z" id="svg_102"/></g><g id="svg_103"><path style="opacity:1" fill="#fafdfe" d="M 497.5,700.5 C 527.653,698.806 547.487,712.14 557,740.5C 560.901,771.219 548.401,791.719 519.5,802C 497.592,806.809 479.426,800.642 465,783.5C 456.004,770.831 453.004,756.831 456,741.5C 461.756,719.577 475.589,705.91 497.5,700.5 Z" id="svg_104"/></g><g id="svg_105"><path style="opacity:1" fill="#1496cd" d="M 499.5,703.5 C 525.947,702.129 543.78,713.796 553,738.5C 557.11,754.186 554.443,768.52 545,781.5C 540.751,776.627 535.584,773.127 529.5,771C 513.833,770.333 498.167,770.333 482.5,771C 476.548,772.952 471.715,776.452 468,781.5C 459.027,769.599 456.027,756.265 459,741.5C 465.299,721.031 478.799,708.365 499.5,703.5 Z" id="svg_106"/></g><g id="svg_107"><path style="opacity:1" fill="#fdfefe" d="M 501.5,719.5 C 518.021,718.404 527.688,725.904 530.5,742C 529.338,757.829 520.838,766.329 505,767.5C 487.126,763.42 480.126,752.42 484,734.5C 487.424,726.558 493.257,721.558 501.5,719.5 Z" id="svg_108"/></g><g id="svg_109"><path style="opacity:1" fill="#fcfdfd" d="M 1456.5,769.5 C 1470.87,769.177 1485.2,769.677 1499.5,771C 1500,771.5 1500.5,772 1501,772.5C 1500.21,799.999 1494.21,826.332 1483,851.5C 1480.46,856.761 1477.29,861.594 1473.5,866C 1471.91,866.862 1470.25,867.529 1468.5,868C 1454.17,868.5 1439.84,868.667 1425.5,868.5C 1425.33,864.486 1425.5,860.486 1426,856.5C 1430.61,841.457 1433.94,826.124 1436,810.5C 1436.75,798.423 1438.09,786.423 1440,774.5C 1440.5,772.667 1441.67,771.5 1443.5,771C 1448.02,770.825 1452.35,770.325 1456.5,769.5 Z" id="svg_110"/></g><g id="svg_111"><path style="opacity:1" fill="#fdfdfe" d="M 1092.5,771.5 C 1108.5,771.333 1124.5,771.5 1140.5,772C 1142.68,772.163 1144.51,772.996 1146,774.5C 1148.05,791.86 1150.38,809.193 1153,826.5C 1155.23,840.746 1158.4,854.746 1162.5,868.5C 1148.52,869.533 1134.52,869.699 1120.5,869C 1117.9,868.117 1115.73,866.617 1114,864.5C 1098.09,836.967 1089.76,807.3 1089,775.5C 1089.69,773.65 1090.86,772.316 1092.5,771.5 Z" id="svg_112"/></g><g id="svg_113"><path style="opacity:1" fill="#fdfefe" d="M 1304.5,771.5 C 1342.5,771.333 1380.5,771.5 1418.5,772C 1419,772.5 1419.5,773 1420,773.5C 1419.65,803.947 1414.65,833.614 1405,862.5C 1404.21,864.587 1403.04,866.421 1401.5,868C 1368.17,868.5 1334.83,868.667 1301.5,868.5C 1300.24,838.674 1300.57,808.841 1302.5,779C 1302.63,776.223 1303.3,773.723 1304.5,771.5 Z" id="svg_114"/></g><g id="svg_115"><path style="opacity:1" fill="#fdfdfe" d="M 1172.5,771.5 C 1209.17,771.5 1245.83,771.5 1282.5,771.5C 1282.67,802.502 1282.5,833.502 1282,864.5C 1280.99,866.025 1280.49,867.692 1280.5,869.5C 1248.83,869.667 1217.16,869.5 1185.5,869C 1183.38,867.269 1181.88,865.102 1181,862.5C 1171.68,833.569 1167.01,803.903 1167,773.5C 1168.95,772.891 1170.78,772.224 1172.5,771.5 Z" id="svg_116"/></g><g id="svg_117"><path style="opacity:1" fill="#babbba" d="M 511.5,870.5 C 508.167,870.5 504.833,870.5 501.5,870.5C 501.5,852.833 501.5,835.167 501.5,817.5C 504.833,817.5 508.167,817.5 511.5,817.5C 511.5,835.167 511.5,852.833 511.5,870.5 Z" id="svg_118"/></g><g id="svg_119"><path style="opacity:1" fill="#ffc111" d="M 501.5,870.5 C 504.833,870.5 508.167,870.5 511.5,870.5C 531.167,870.5 550.833,870.5 570.5,870.5C 570.5,896.833 570.5,923.167 570.5,949.5C 550.833,949.5 531.167,949.5 511.5,949.5C 508.167,949.5 504.833,949.5 501.5,949.5C 481.833,949.5 462.167,949.5 442.5,949.5C 442.5,923.167 442.5,896.833 442.5,870.5C 462.167,870.5 481.833,870.5 501.5,870.5 Z" id="svg_120"/></g><g id="svg_121"><path style="opacity:1" fill="#fffcf4" d="M 484.5,880.5 C 491.648,879.761 498.481,880.761 505,883.5C 515.171,879.531 525.338,879.697 535.5,884C 539.167,884.333 542.833,884.667 546.5,885C 547.658,902.76 547.825,920.593 547,938.5C 546.5,939 546,939.5 545.5,940C 518.502,941.989 491.502,941.989 464.5,940C 464,939.5 463.5,939 463,938.5C 462.333,921.167 462.333,903.833 463,886.5C 463.5,886 464,885.5 464.5,885C 471.612,884.911 478.279,883.411 484.5,880.5 Z" id="svg_122"/></g><g id="svg_123"><path style="opacity:1" fill="#ffc004" d="M 503.5,886.5 C 503.829,901.343 503.496,916.01 502.5,930.5C 499.202,927.899 495.368,926.566 491,926.5C 484.389,927.236 478.055,928.902 472,931.5C 471.167,916.822 471.334,902.155 472.5,887.5C 480.438,883.746 488.771,882.246 497.5,883C 499.727,883.941 501.727,885.108 503.5,886.5 Z" id="svg_124"/></g><g id="svg_125"><path style="opacity:1" fill="#ffc004" d="M 538.5,887.5 C 538.829,902.01 538.496,916.343 537.5,930.5C 529.657,928.097 521.657,926.93 513.5,927C 511.336,927.914 509.336,929.081 507.5,930.5C 506.334,915.845 506.167,901.178 507,886.5C 509.773,883.781 513.106,882.447 517,882.5C 524.446,883.039 531.613,884.706 538.5,887.5 Z" id="svg_126"/></g><g id="svg_127"><path style="opacity:1" fill="#ffc420" d="M 466.5,886.5 C 468.173,902.376 469.173,918.543 469.5,935C 481.193,935.208 492.86,935.708 504.5,936.5C 510.071,935.071 515.738,934.404 521.5,934.5C 528.171,934.834 534.837,935.167 541.5,935.5C 541.167,919.153 541.501,902.82 542.5,886.5C 543.167,886.833 543.833,887.167 544.5,887.5C 545.666,903.823 545.833,920.156 545,936.5C 544.5,937 544,937.5 543.5,938C 517.831,939.932 492.164,939.932 466.5,938C 466,937.5 465.5,937 465,936.5C 464.333,920.5 464.333,904.5 465,888.5C 465.717,887.956 466.217,887.289 466.5,886.5 Z" id="svg_128"/></g><g id="svg_129"><path style="opacity:1" fill="#fff2c9" d="M 503.5,886.5 C 504.826,901.339 504.826,916.339 503.5,931.5C 502.893,931.376 502.56,931.043 502.5,930.5C 503.496,916.01 503.829,901.343 503.5,886.5 Z" id="svg_130"/></g><g id="svg_131"><path style="opacity:1" fill="#fdfdfe" d="M 1257.5,887.5 C 1264.84,887.334 1272.17,887.5 1279.5,888C 1280,888.5 1280.5,889 1281,889.5C 1281.63,915.506 1281.47,941.506 1280.5,967.5C 1274.42,967.804 1268.42,967.304 1262.5,966C 1230.9,949.067 1207.56,924.401 1192.5,892C 1192.75,890.674 1193.42,889.674 1194.5,889C 1215.67,888.832 1236.67,888.332 1257.5,887.5 Z" id="svg_132"/></g><g id="svg_133"><path style="opacity:1" fill="#fcfdfd" d="M 1314.5,886.5 C 1340.88,886.017 1367.22,886.683 1393.5,888.5C 1390.26,898.97 1385.43,908.637 1379,917.5C 1366.04,935.465 1350.2,950.298 1331.5,962C 1323.37,965.815 1314.87,968.315 1306,969.5C 1304.24,969.456 1302.57,969.122 1301,968.5C 1300.2,941.81 1300.37,915.143 1301.5,888.5C 1306.05,888.376 1310.38,887.709 1314.5,886.5 Z" id="svg_134"/></g><g id="svg_135"><path style="opacity:1" fill="#fafbfc" d="M 1422.5,886.5 C 1434.52,886.39 1446.52,886.723 1458.5,887.5C 1457.76,891.643 1455.93,895.31 1453,898.5C 1431.19,923.161 1405.36,942.161 1375.5,955.5C 1374.83,955.333 1374.17,955.167 1373.5,955C 1389.95,935.284 1403.78,913.784 1415,890.5C 1417.11,888.267 1419.61,886.933 1422.5,886.5 Z" id="svg_136"/></g><g id="svg_137"><path style="opacity:1" fill="#fbfcfd" d="M 1133.5,888.5 C 1143.51,888.334 1153.51,888.5 1163.5,889C 1165.95,889.29 1168.29,889.956 1170.5,891C 1181.55,911.608 1194.21,931.108 1208.5,949.5C 1206.93,950.631 1205.26,950.798 1203.5,950C 1189.19,943.181 1175.86,934.848 1163.5,925C 1153.33,916.167 1143.83,906.667 1135,896.5C 1133.82,894.656 1132.65,892.823 1131.5,891C 1132.06,889.989 1132.72,889.156 1133.5,888.5 Z" id="svg_138"/></g><g id="svg_139"><path style="opacity:1" fill="#fff0c7" d="M 538.5,887.5 C 539.826,902.005 539.826,916.672 538.5,931.5C 537.893,931.376 537.56,931.043 537.5,930.5C 538.496,916.343 538.829,902.01 538.5,887.5 Z" id="svg_140"/></g><g id="svg_141"><path style="opacity:1" fill="#bbbbbb" d="M 937.5,906.5 C 937.5,909.5 937.5,912.5 937.5,915.5C 822.166,915.333 706.833,915.5 591.5,916C 594.333,918.167 597.167,920.333 600,922.5C 600.993,928.502 598.493,930.668 592.5,929C 585.653,924.075 578.986,918.908 572.5,913.5C 570.96,910.709 571.294,908.209 573.5,906C 580.719,900.605 588.053,895.438 595.5,890.5C 599.767,891.602 601.267,894.269 600,898.5C 597.167,900.667 594.333,902.833 591.5,905C 706.833,905.734 822.166,906.234 937.5,906.5 Z" id="svg_142"/></g><g id="svg_143"><path style="opacity:1" fill="#ffc72d" d="M 486.5,929.5 C 491.449,928.959 496.116,929.792 500.5,932C 492.833,932.667 485.167,932.667 477.5,932C 480.628,931.159 483.628,930.325 486.5,929.5 Z" id="svg_144"/></g><g id="svg_145"><path style="opacity:1" fill="#ffc729" d="M 513.5,929.5 C 519.678,929.107 525.678,929.94 531.5,932C 524.167,932.667 516.833,932.667 509.5,932C 511.066,931.392 512.4,930.558 513.5,929.5 Z" id="svg_146"/></g><g id="svg_147"><path style="opacity:1" fill="#bbbbba" d="M 501.5,949.5 C 504.833,949.5 508.167,949.5 511.5,949.5C 511.5,962.5 511.5,975.5 511.5,988.5C 508.167,988.5 504.833,988.5 501.5,988.5C 501.5,975.5 501.5,962.5 501.5,949.5 Z" id="svg_148"/></g></g></svg> \ No newline at end of file diff --git a/resources/images/icons/collapse/collapse_black.svg b/resources/images/icons/collapse/collapse_black.svg new file mode 100644 index 00000000..9d2bb38b --- /dev/null +++ b/resources/images/icons/collapse/collapse_black.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="18.163265228271484" height="18.163265228271484" fill="none" style=""><rect id="backgroundrect" width="100%" height="100%" x="0" y="0" fill="none" stroke="none" style="" class=""/><g class="currentLayer" style=""><title>Layer 1</title><path fill-rule="evenodd" clip-rule="evenodd" d="M8.354224460601806,0.5174896888732912 a1,1 0 0 1 1.4140000000000001,0 l8,8 a1,1 0 0 1 -1.4140000000000001,1.4140000000000001 L9.061224460601807,2.6384896888732907 l-7.293,7.293 a1,1 0 0 1 -1.4140000000000001,-1.4140000000000001 l8,-8 z" fill="#000000" id="svg_1" class="selected" fill-opacity="1" transform="rotate(-180 9.06122875213623,5.21845245361328) "/></g></svg> diff --git a/resources/images/icons/collapse/collapse_eProsimaLightBlue.svg b/resources/images/icons/collapse/collapse_eProsimaLightBlue.svg new file mode 100644 index 00000000..32e7586b --- /dev/null +++ b/resources/images/icons/collapse/collapse_eProsimaLightBlue.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="18.163265228271484" height="18.163265228271484" fill="none" style=""><rect id="backgroundrect" width="100%" height="100%" x="0" y="0" fill="none" stroke="none" style="" class=""/><g class="currentLayer" style=""><title>Layer 1</title><path fill-rule="evenodd" clip-rule="evenodd" d="M8.354224460601806,0.5174896888732912 a1,1 0 0 1 1.4140000000000001,0 l8,8 a1,1 0 0 1 -1.4140000000000001,1.4140000000000001 L9.061224460601807,2.6384896888732907 l-7.293,7.293 a1,1 0 0 1 -1.4140000000000001,-1.4140000000000001 l8,-8 z" fill="#0895cd" id="svg_1" class="" fill-opacity="1" transform="rotate(-180 9.06122875213623,5.21845245361328) "/></g></svg> diff --git a/resources/images/icons/collapse/collapse_grey.svg b/resources/images/icons/collapse/collapse_grey.svg new file mode 100644 index 00000000..1f9eaba8 --- /dev/null +++ b/resources/images/icons/collapse/collapse_grey.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="18.163265228271484" height="18.163265228271484" fill="none" style=""><rect id="backgroundrect" width="100%" height="100%" x="0" y="0" fill="none" stroke="none" style="" class=""/><g class="currentLayer" style=""><title>Layer 1</title><path fill-rule="evenodd" clip-rule="evenodd" d="M8.354224460601806,0.5174896888732912 a1,1 0 0 1 1.4140000000000001,0 l8,8 a1,1 0 0 1 -1.4140000000000001,1.4140000000000001 L9.061224460601807,2.6384896888732907 l-7.293,7.293 a1,1 0 0 1 -1.4140000000000001,-1.4140000000000001 l8,-8 z" fill="#808080" id="svg_1" class="selected" fill-opacity="1" transform="rotate(-180 9.06122875213623,5.21845245361328) "/></g></svg> diff --git a/resources/images/icons/collapse/collapse_white.svg b/resources/images/icons/collapse/collapse_white.svg new file mode 100644 index 00000000..1be200e0 --- /dev/null +++ b/resources/images/icons/collapse/collapse_white.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="18.163265228271484" height="18.163265228271484" fill="none" style=""><rect id="backgroundrect" width="100%" height="100%" x="0" y="0" fill="none" stroke="none" style="" class=""/><g class="currentLayer" style=""><title>Layer 1</title><path fill-rule="evenodd" clip-rule="evenodd" d="M8.354224460601806,0.5174896888732912 a1,1 0 0 1 1.4140000000000001,0 l8,8 a1,1 0 0 1 -1.4140000000000001,1.4140000000000001 L9.061224460601807,2.6384896888732907 l-7.293,7.293 a1,1 0 0 1 -1.4140000000000001,-1.4140000000000001 l8,-8 z" fill="#ffffff" id="svg_1" class="selected" fill-opacity="1" transform="rotate(-180 9.06122875213623,5.21845245361328) "/></g></svg> diff --git a/resources/images/icons/cross/cross_red.svg b/resources/images/icons/cross/cross_red.svg new file mode 100644 index 00000000..ea6821b4 --- /dev/null +++ b/resources/images/icons/cross/cross_red.svg @@ -0,0 +1,5 @@ +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="375" height="374.999991" version="1.2" style=""><rect id="backgroundrect" width="100%" height="100%" x="0" y="0" fill="none" stroke="none"/> + +<g class="currentLayer" style=""><title>Layer 1</title><g id="surface1" class="selected" fill-opacity="1" fill="#ff0d0c"> +<path style="stroke: none; fill-rule: nonzero; fill-opacity: 1;" d="M 365.121094 9.878906 C 355.332031 0.0585938 339.402344 0.0585938 329.617188 9.878906 L 187.5 151.960938 L 45.417969 9.878906 C 35.597656 0.0585938 19.699219 0.0585938 9.878906 9.878906 C 0.0585938 19.699219 0.0585938 35.597656 9.878906 45.417969 L 151.960938 187.5 L 9.878906 329.582031 C 0.0585938 339.402344 0.0585938 355.300781 9.878906 365.121094 C 19.699219 374.941406 35.597656 374.941406 45.417969 365.121094 L 187.5 223.003906 L 329.617188 365.121094 C 339.402344 374.941406 355.332031 374.941406 365.121094 365.121094 C 374.90625 355.300781 374.90625 339.402344 365.121094 329.582031 L 223.003906 187.5 L 365.121094 45.417969 C 374.90625 35.597656 374.90625 19.699219 365.121094 9.878906 " id="svg_1" fill="#ff0d0c"/> +</g></g></svg> \ No newline at end of file diff --git a/resources/images/icons/error/error_black.svg b/resources/images/icons/error/error_black.svg new file mode 100644 index 00000000..f5c62a0d --- /dev/null +++ b/resources/images/icons/error/error_black.svg @@ -0,0 +1,10 @@ +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="#000000" height="512" width="512" version="1.1" xml:space="preserve"><rect id="backgroundrect" width="100%" height="100%" x="0" y="0" fill="none" stroke="none"/> + +<g class="currentLayer" style=""><title>Layer 1</title><g id="svg_1" class="selected"> + <g id="svg_2"> + <g id="svg_3"> + <path d="M437.016,74.984c-99.979-99.979-262.075-99.979-362.033,0.002c-99.978,99.978-99.978,262.073,0.004,362.031 c99.954,99.978,262.05,99.978,362.029-0.002C536.995,337.059,536.995,174.964,437.016,74.984z M406.848,406.844 c-83.318,83.318-218.396,83.318-301.691,0.004c-83.318-83.299-83.318-218.377-0.002-301.693 c83.297-83.317,218.375-83.317,301.691,0S490.162,323.549,406.848,406.844z" id="svg_4"/> + <path d="M361.592,150.408c-8.331-8.331-21.839-8.331-30.17,0l-75.425,75.425l-75.425-75.425c-8.331-8.331-21.839-8.331-30.17,0 s-8.331,21.839,0,30.17l75.425,75.425L150.43,331.4c-8.331,8.331-8.331,21.839,0,30.17c8.331,8.331,21.839,8.331,30.17,0 l75.397-75.397l75.419,75.419c8.331,8.331,21.839,8.331,30.17,0c8.331-8.331,8.331-21.839,0-30.17l-75.419-75.419l75.425-75.425 C369.923,172.247,369.923,158.74,361.592,150.408z" id="svg_5"/> + </g> + </g> +</g></g></svg> \ No newline at end of file diff --git a/resources/images/icons/error/error_eProsimaLightBlue.svg b/resources/images/icons/error/error_eProsimaLightBlue.svg new file mode 100644 index 00000000..277fd56f --- /dev/null +++ b/resources/images/icons/error/error_eProsimaLightBlue.svg @@ -0,0 +1,10 @@ +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="#000000" height="512" width="512" version="1.1" xml:space="preserve"><rect id="backgroundrect" width="100%" height="100%" x="0" y="0" fill="none" stroke="none"/> + +<g class="currentLayer" style=""><title>Layer 1</title><g id="svg_1" class="selected" fill-opacity="1" fill="#0895cd"> + <g id="svg_2" fill="#0895cd"> + <g id="svg_3" fill="#0895cd"> + <path d="M437.016,74.984c-99.979-99.979-262.075-99.979-362.033,0.002c-99.978,99.978-99.978,262.073,0.004,362.031 c99.954,99.978,262.05,99.978,362.029-0.002C536.995,337.059,536.995,174.964,437.016,74.984z M406.848,406.844 c-83.318,83.318-218.396,83.318-301.691,0.004c-83.318-83.299-83.318-218.377-0.002-301.693 c83.297-83.317,218.375-83.317,301.691,0S490.162,323.549,406.848,406.844z" id="svg_4" fill="#0895cd"/> + <path d="M361.592,150.408c-8.331-8.331-21.839-8.331-30.17,0l-75.425,75.425l-75.425-75.425c-8.331-8.331-21.839-8.331-30.17,0 s-8.331,21.839,0,30.17l75.425,75.425L150.43,331.4c-8.331,8.331-8.331,21.839,0,30.17c8.331,8.331,21.839,8.331,30.17,0 l75.397-75.397l75.419,75.419c8.331,8.331,21.839,8.331,30.17,0c8.331-8.331,8.331-21.839,0-30.17l-75.419-75.419l75.425-75.425 C369.923,172.247,369.923,158.74,361.592,150.408z" id="svg_5" fill="#0895cd"/> + </g> + </g> +</g></g></svg> \ No newline at end of file diff --git a/resources/images/icons/error/error_grey.svg b/resources/images/icons/error/error_grey.svg new file mode 100644 index 00000000..43e12705 --- /dev/null +++ b/resources/images/icons/error/error_grey.svg @@ -0,0 +1,10 @@ +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="#000000" height="512" width="512" version="1.1" xml:space="preserve"><rect id="backgroundrect" width="100%" height="100%" x="0" y="0" fill="none" stroke="none"/> + +<g class="currentLayer" style=""><title>Layer 1</title><g id="svg_1" class="selected" fill-opacity="1" fill="#808080"> + <g id="svg_2" fill="#808080"> + <g id="svg_3" fill="#808080"> + <path d="M437.016,74.984c-99.979-99.979-262.075-99.979-362.033,0.002c-99.978,99.978-99.978,262.073,0.004,362.031 c99.954,99.978,262.05,99.978,362.029-0.002C536.995,337.059,536.995,174.964,437.016,74.984z M406.848,406.844 c-83.318,83.318-218.396,83.318-301.691,0.004c-83.318-83.299-83.318-218.377-0.002-301.693 c83.297-83.317,218.375-83.317,301.691,0S490.162,323.549,406.848,406.844z" id="svg_4" fill="#808080"/> + <path d="M361.592,150.408c-8.331-8.331-21.839-8.331-30.17,0l-75.425,75.425l-75.425-75.425c-8.331-8.331-21.839-8.331-30.17,0 s-8.331,21.839,0,30.17l75.425,75.425L150.43,331.4c-8.331,8.331-8.331,21.839,0,30.17c8.331,8.331,21.839,8.331,30.17,0 l75.397-75.397l75.419,75.419c8.331,8.331,21.839,8.331,30.17,0c8.331-8.331,8.331-21.839,0-30.17l-75.419-75.419l75.425-75.425 C369.923,172.247,369.923,158.74,361.592,150.408z" id="svg_5" fill="#808080"/> + </g> + </g> +</g></g></svg> \ No newline at end of file diff --git a/resources/images/icons/error/error_red.svg b/resources/images/icons/error/error_red.svg new file mode 100644 index 00000000..33c5370a --- /dev/null +++ b/resources/images/icons/error/error_red.svg @@ -0,0 +1,10 @@ +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="#000000" height="512" width="512" version="1.1" xml:space="preserve" style=""><rect id="backgroundrect" width="100%" height="100%" x="0" y="0" fill="none" stroke="none"/> + +<g class="currentLayer" style=""><title>Layer 1</title><g id="svg_1" class="selected" fill-opacity="1" fill="#ff0d0c"> + <g id="svg_2" fill="#ff0d0c"> + <g id="svg_3" fill="#ff0d0c"> + <path d="M437.016,74.984c-99.979-99.979-262.075-99.979-362.033,0.002c-99.978,99.978-99.978,262.073,0.004,362.031 c99.954,99.978,262.05,99.978,362.029-0.002C536.995,337.059,536.995,174.964,437.016,74.984z M406.848,406.844 c-83.318,83.318-218.396,83.318-301.691,0.004c-83.318-83.299-83.318-218.377-0.002-301.693 c83.297-83.317,218.375-83.317,301.691,0S490.162,323.549,406.848,406.844z" id="svg_4" fill="#ff0d0c"/> + <path d="M361.592,150.408c-8.331-8.331-21.839-8.331-30.17,0l-75.425,75.425l-75.425-75.425c-8.331-8.331-21.839-8.331-30.17,0 s-8.331,21.839,0,30.17l75.425,75.425L150.43,331.4c-8.331,8.331-8.331,21.839,0,30.17c8.331,8.331,21.839,8.331,30.17,0 l75.397-75.397l75.419,75.419c8.331,8.331,21.839,8.331,30.17,0c8.331-8.331,8.331-21.839,0-30.17l-75.419-75.419l75.425-75.425 C369.923,172.247,369.923,158.74,361.592,150.408z" id="svg_5" fill="#ff0d0c"/> + </g> + </g> +</g></g></svg> \ No newline at end of file diff --git a/resources/images/icons/error/error_white.svg b/resources/images/icons/error/error_white.svg new file mode 100644 index 00000000..6ecb6905 --- /dev/null +++ b/resources/images/icons/error/error_white.svg @@ -0,0 +1,10 @@ +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="#000000" height="512" width="512" version="1.1" xml:space="preserve"><rect id="backgroundrect" width="100%" height="100%" x="0" y="0" fill="none" stroke="none"/> + +<g class="currentLayer" style=""><title>Layer 1</title><g id="svg_1" class="selected" fill-opacity="1" fill="#ffffff"> + <g id="svg_2" fill="#ffffff"> + <g id="svg_3" fill="#ffffff"> + <path d="M437.016,74.984c-99.979-99.979-262.075-99.979-362.033,0.002c-99.978,99.978-99.978,262.073,0.004,362.031 c99.954,99.978,262.05,99.978,362.029-0.002C536.995,337.059,536.995,174.964,437.016,74.984z M406.848,406.844 c-83.318,83.318-218.396,83.318-301.691,0.004c-83.318-83.299-83.318-218.377-0.002-301.693 c83.297-83.317,218.375-83.317,301.691,0S490.162,323.549,406.848,406.844z" id="svg_4" fill="#ffffff"/> + <path d="M361.592,150.408c-8.331-8.331-21.839-8.331-30.17,0l-75.425,75.425l-75.425-75.425c-8.331-8.331-21.839-8.331-30.17,0 s-8.331,21.839,0,30.17l75.425,75.425L150.43,331.4c-8.331,8.331-8.331,21.839,0,30.17c8.331,8.331,21.839,8.331,30.17,0 l75.397-75.397l75.419,75.419c8.331,8.331,21.839,8.331,30.17,0c8.331-8.331,8.331-21.839,0-30.17l-75.419-75.419l75.425-75.425 C369.923,172.247,369.923,158.74,361.592,150.408z" id="svg_5" fill="#ffffff"/> + </g> + </g> +</g></g></svg> \ No newline at end of file diff --git a/resources/images/icons/expand/expand_black.svg b/resources/images/icons/expand/expand_black.svg new file mode 100644 index 00000000..650b4682 --- /dev/null +++ b/resources/images/icons/expand/expand_black.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="18.163265228271484" height="18.163265228271484" fill="none"><rect id="backgroundrect" width="100%" height="100%" x="0" y="0" fill="none" stroke="none" style="" class=""/><g class="currentLayer" style=""><title>Layer 1</title><path fill-rule="evenodd" clip-rule="evenodd" d="M8.354224460601806,0.5174896888732912 a1,1 0 0 1 1.4140000000000001,0 l8,8 a1,1 0 0 1 -1.4140000000000001,1.4140000000000001 L9.061224460601807,2.6384896888732907 l-7.293,7.293 a1,1 0 0 1 -1.4140000000000001,-1.4140000000000001 l8,-8 z" fill="#000000" id="svg_1" class="selected" fill-opacity="1"/></g></svg> diff --git a/resources/images/icons/expand/expand_eProsimaLightBlue.svg b/resources/images/icons/expand/expand_eProsimaLightBlue.svg new file mode 100644 index 00000000..1d1eab90 --- /dev/null +++ b/resources/images/icons/expand/expand_eProsimaLightBlue.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="18.163265228271484" height="18.163265228271484" fill="none" style=""><rect id="backgroundrect" width="100%" height="100%" x="0" y="0" fill="none" stroke="none" style="" class=""/><g class="currentLayer" style=""><title>Layer 1</title><path fill-rule="evenodd" clip-rule="evenodd" d="M8.354224460601806,0.5174896888732912 a1,1 0 0 1 1.4140000000000001,0 l8,8 a1,1 0 0 1 -1.4140000000000001,1.4140000000000001 L9.061224460601807,2.6384896888732907 l-7.293,7.293 a1,1 0 0 1 -1.4140000000000001,-1.4140000000000001 l8,-8 z" fill="#0895cd" id="svg_1" class="selected" fill-opacity="1"/></g></svg> diff --git a/resources/images/icons/expand/expand_grey.svg b/resources/images/icons/expand/expand_grey.svg new file mode 100644 index 00000000..d268e18b --- /dev/null +++ b/resources/images/icons/expand/expand_grey.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="18.163265228271484" height="18.163265228271484" fill="none" style=""><rect id="backgroundrect" width="100%" height="100%" x="0" y="0" fill="none" stroke="none" style="" class=""/><g class="currentLayer" style=""><title>Layer 1</title><path fill-rule="evenodd" clip-rule="evenodd" d="M8.354224460601806,0.5174896888732912 a1,1 0 0 1 1.4140000000000001,0 l8,8 a1,1 0 0 1 -1.4140000000000001,1.4140000000000001 L9.061224460601807,2.6384896888732907 l-7.293,7.293 a1,1 0 0 1 -1.4140000000000001,-1.4140000000000001 l8,-8 z" fill="#808080" id="svg_1" class="selected" fill-opacity="1"/></g></svg> diff --git a/resources/images/icons/expand/expand_white.svg b/resources/images/icons/expand/expand_white.svg new file mode 100644 index 00000000..09cc7c34 --- /dev/null +++ b/resources/images/icons/expand/expand_white.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="18.163265228271484" height="18.163265228271484" fill="none" style=""><rect id="backgroundrect" width="100%" height="100%" x="0" y="0" fill="none" stroke="none" style="" class=""/><g class="currentLayer" style=""><title>Layer 1</title><path fill-rule="evenodd" clip-rule="evenodd" d="M8.354224460601806,0.5174896888732912 a1,1 0 0 1 1.4140000000000001,0 l8,8 a1,1 0 0 1 -1.4140000000000001,1.4140000000000001 L9.061224460601807,2.6384896888732907 l-7.293,7.293 a1,1 0 0 1 -1.4140000000000001,-1.4140000000000001 l8,-8 z" fill="#ffffff" id="svg_1" class="selected" fill-opacity="1"/></g></svg> diff --git a/resources/images/icons/filter_empty/filter_empty_black.svg b/resources/images/icons/filter_empty/filter_empty_black.svg new file mode 100644 index 00000000..9a0211e5 --- /dev/null +++ b/resources/images/icons/filter_empty/filter_empty_black.svg @@ -0,0 +1,3 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none"><rect id="backgroundrect" width="100%" height="100%" x="0" y="0" fill="none" stroke="none"/> + +<g class="currentLayer" style=""><title>Layer 1</title><path d="M19 3H5C3.89543 3 3 3.89543 3 5V6.17157C3 6.70201 3.21071 7.21071 3.58579 7.58579L9.41421 13.4142C9.78929 13.7893 10 14.298 10 14.8284V20V20.2857C10 20.9183 10.7649 21.2351 11.2122 20.7878L12 20L13.4142 18.5858C13.7893 18.2107 14 17.702 14 17.1716V14.8284C14 14.298 14.2107 13.7893 14.5858 13.4142L20.4142 7.58579C20.7893 7.21071 21 6.70201 21 6.17157V5C21 3.89543 20.1046 3 19 3Z" stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" id="svg_1" class="selected" stroke-opacity="1"/></g></svg> \ No newline at end of file diff --git a/resources/images/icons/filter_empty/filter_empty_eProsimaLightBlue.svg b/resources/images/icons/filter_empty/filter_empty_eProsimaLightBlue.svg new file mode 100644 index 00000000..9003c643 --- /dev/null +++ b/resources/images/icons/filter_empty/filter_empty_eProsimaLightBlue.svg @@ -0,0 +1,3 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none"><rect id="backgroundrect" width="100%" height="100%" x="0" y="0" fill="none" stroke="none"/> + +<g class="currentLayer" style=""><title>Layer 1</title><path d="M19 3H5C3.89543 3 3 3.89543 3 5V6.17157C3 6.70201 3.21071 7.21071 3.58579 7.58579L9.41421 13.4142C9.78929 13.7893 10 14.298 10 14.8284V20V20.2857C10 20.9183 10.7649 21.2351 11.2122 20.7878L12 20L13.4142 18.5858C13.7893 18.2107 14 17.702 14 17.1716V14.8284C14 14.298 14.2107 13.7893 14.5858 13.4142L20.4142 7.58579C20.7893 7.21071 21 6.70201 21 6.17157V5C21 3.89543 20.1046 3 19 3Z" stroke="#0895cd" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" id="svg_1" class="selected" stroke-opacity="1"/></g></svg> \ No newline at end of file diff --git a/resources/images/icons/filter_empty/filter_empty_grey.svg b/resources/images/icons/filter_empty/filter_empty_grey.svg new file mode 100644 index 00000000..36c8ba78 --- /dev/null +++ b/resources/images/icons/filter_empty/filter_empty_grey.svg @@ -0,0 +1,3 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none"><rect id="backgroundrect" width="100%" height="100%" x="0" y="0" fill="none" stroke="none"/> + +<g class="currentLayer" style=""><title>Layer 1</title><path d="M19 3H5C3.89543 3 3 3.89543 3 5V6.17157C3 6.70201 3.21071 7.21071 3.58579 7.58579L9.41421 13.4142C9.78929 13.7893 10 14.298 10 14.8284V20V20.2857C10 20.9183 10.7649 21.2351 11.2122 20.7878L12 20L13.4142 18.5858C13.7893 18.2107 14 17.702 14 17.1716V14.8284C14 14.298 14.2107 13.7893 14.5858 13.4142L20.4142 7.58579C20.7893 7.21071 21 6.70201 21 6.17157V5C21 3.89543 20.1046 3 19 3Z" stroke="#808080" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" id="svg_1" class="selected" stroke-opacity="1"/></g></svg> \ No newline at end of file diff --git a/resources/images/icons/filter_empty/filter_empty_white.svg b/resources/images/icons/filter_empty/filter_empty_white.svg new file mode 100644 index 00000000..f57f44bc --- /dev/null +++ b/resources/images/icons/filter_empty/filter_empty_white.svg @@ -0,0 +1,3 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none"><rect id="backgroundrect" width="100%" height="100%" x="0" y="0" fill="none" stroke="none"/> + +<g class="currentLayer" style=""><title>Layer 1</title><path d="M19 3H5C3.89543 3 3 3.89543 3 5V6.17157C3 6.70201 3.21071 7.21071 3.58579 7.58579L9.41421 13.4142C9.78929 13.7893 10 14.298 10 14.8284V20V20.2857C10 20.9183 10.7649 21.2351 11.2122 20.7878L12 20L13.4142 18.5858C13.7893 18.2107 14 17.702 14 17.1716V14.8284C14 14.298 14.2107 13.7893 14.5858 13.4142L20.4142 7.58579C20.7893 7.21071 21 6.70201 21 6.17157V5C21 3.89543 20.1046 3 19 3Z" stroke="#ffffff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" id="svg_1" class="selected" stroke-opacity="1"/></g></svg> \ No newline at end of file diff --git a/resources/images/icons/filter_full/filter_full_black.svg b/resources/images/icons/filter_full/filter_full_black.svg new file mode 100644 index 00000000..27967f66 --- /dev/null +++ b/resources/images/icons/filter_full/filter_full_black.svg @@ -0,0 +1,3 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" style=""><rect id="backgroundrect" width="100%" height="100%" x="0" y="0" fill="none" stroke="none"/> + +<g class="currentLayer" style=""><title>Layer 1</title><path d="M19 3H5C3.89543 3 3 3.89543 3 5V6.17157C3 6.70201 3.21071 7.21071 3.58579 7.58579L9.41421 13.4142C9.78929 13.7893 10 14.298 10 14.8284V20V20.2857C10 20.9183 10.7649 21.2351 11.2122 20.7878L12 20L13.4142 18.5858C13.7893 18.2107 14 17.702 14 17.1716V14.8284C14 14.298 14.2107 13.7893 14.5858 13.4142L20.4142 7.58579C20.7893 7.21071 21 6.70201 21 6.17157V5C21 3.89543 20.1046 3 19 3Z" stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" id="svg_1" class="selected" stroke-opacity="1" fill-opacity="1" fill="#000000"/></g></svg> \ No newline at end of file diff --git a/resources/images/icons/filter_full/filter_full_eProsimaLightBlue.svg b/resources/images/icons/filter_full/filter_full_eProsimaLightBlue.svg new file mode 100644 index 00000000..f5e231a6 --- /dev/null +++ b/resources/images/icons/filter_full/filter_full_eProsimaLightBlue.svg @@ -0,0 +1,3 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" style=""><rect id="backgroundrect" width="100%" height="100%" x="0" y="0" fill="none" stroke="none"/> + +<g class="currentLayer" style=""><title>Layer 1</title><path d="M19 3H5C3.89543 3 3 3.89543 3 5V6.17157C3 6.70201 3.21071 7.21071 3.58579 7.58579L9.41421 13.4142C9.78929 13.7893 10 14.298 10 14.8284V20V20.2857C10 20.9183 10.7649 21.2351 11.2122 20.7878L12 20L13.4142 18.5858C13.7893 18.2107 14 17.702 14 17.1716V14.8284C14 14.298 14.2107 13.7893 14.5858 13.4142L20.4142 7.58579C20.7893 7.21071 21 6.70201 21 6.17157V5C21 3.89543 20.1046 3 19 3Z" stroke="#0895cd" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" id="svg_1" class="selected" stroke-opacity="1" fill-opacity="1" fill="#0895cd"/></g></svg> \ No newline at end of file diff --git a/resources/images/icons/filter_full/filter_full_grey.svg b/resources/images/icons/filter_full/filter_full_grey.svg new file mode 100644 index 00000000..b5652197 --- /dev/null +++ b/resources/images/icons/filter_full/filter_full_grey.svg @@ -0,0 +1,3 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" style=""><rect id="backgroundrect" width="100%" height="100%" x="0" y="0" fill="none" stroke="none"/> + +<g class="currentLayer" style=""><title>Layer 1</title><path d="M19 3H5C3.89543 3 3 3.89543 3 5V6.17157C3 6.70201 3.21071 7.21071 3.58579 7.58579L9.41421 13.4142C9.78929 13.7893 10 14.298 10 14.8284V20V20.2857C10 20.9183 10.7649 21.2351 11.2122 20.7878L12 20L13.4142 18.5858C13.7893 18.2107 14 17.702 14 17.1716V14.8284C14 14.298 14.2107 13.7893 14.5858 13.4142L20.4142 7.58579C20.7893 7.21071 21 6.70201 21 6.17157V5C21 3.89543 20.1046 3 19 3Z" stroke="#808080" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" id="svg_1" class="selected" stroke-opacity="1" fill-opacity="1" fill="#808080"/></g></svg> \ No newline at end of file diff --git a/resources/images/icons/filter_full/filter_full_white.svg b/resources/images/icons/filter_full/filter_full_white.svg new file mode 100644 index 00000000..21bbf614 --- /dev/null +++ b/resources/images/icons/filter_full/filter_full_white.svg @@ -0,0 +1,3 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" style=""><rect id="backgroundrect" width="100%" height="100%" x="0" y="0" fill="none" stroke="none"/> + +<g class="currentLayer" style=""><title>Layer 1</title><path d="M19 3H5C3.89543 3 3 3.89543 3 5V6.17157C3 6.70201 3.21071 7.21071 3.58579 7.58579L9.41421 13.4142C9.78929 13.7893 10 14.298 10 14.8284V20V20.2857C10 20.9183 10.7649 21.2351 11.2122 20.7878L12 20L13.4142 18.5858C13.7893 18.2107 14 17.702 14 17.1716V14.8284C14 14.298 14.2107 13.7893 14.5858 13.4142L20.4142 7.58579C20.7893 7.21071 21 6.70201 21 6.17157V5C21 3.89543 20.1046 3 19 3Z" stroke="#ffffff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" id="svg_1" class="selected" stroke-opacity="1" fill-opacity="1" fill="#ffffff"/></g></svg> \ No newline at end of file diff --git a/resources/images/icons/left_arrow/left_arrow_black.svg b/resources/images/icons/left_arrow/left_arrow_black.svg new file mode 100644 index 00000000..c914089c --- /dev/null +++ b/resources/images/icons/left_arrow/left_arrow_black.svg @@ -0,0 +1,7 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="#000000" style=""><rect id="backgroundrect" width="100%" height="100%" x="0" y="0" fill="none" stroke="none"/> + + <g style="" class="currentLayer"> + <title>Layer 1</title> + <path transform="rotate(-90 12 12)" id="svg_1" d="m21,21l-18,0l9,-18l9,18z"/> + </g> +</svg> \ No newline at end of file diff --git a/resources/images/icons/left_arrow/left_arrow_eProsimaLightBlue.svg b/resources/images/icons/left_arrow/left_arrow_eProsimaLightBlue.svg new file mode 100644 index 00000000..624805e0 --- /dev/null +++ b/resources/images/icons/left_arrow/left_arrow_eProsimaLightBlue.svg @@ -0,0 +1,7 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="#000000" style=""><rect id="backgroundrect" width="100%" height="100%" x="0" y="0" fill="none" stroke="none"/> + + <g style="" class="currentLayer"> + <title>Layer 1</title> + <path transform="rotate(-90 12 12)" id="svg_1" d="m21,21l-18,0l9,-18l9,18z" class="selected" fill="#0895cd" fill-opacity="1"/> + </g> +</svg> \ No newline at end of file diff --git a/resources/images/icons/left_arrow/left_arrow_grey.svg b/resources/images/icons/left_arrow/left_arrow_grey.svg new file mode 100644 index 00000000..f2b5779e --- /dev/null +++ b/resources/images/icons/left_arrow/left_arrow_grey.svg @@ -0,0 +1,7 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="#000000" style=""><rect id="backgroundrect" width="100%" height="100%" x="0" y="0" fill="none" stroke="none"/> + + <g style="" class="currentLayer"> + <title>Layer 1</title> + <path transform="rotate(-90 12 12)" id="svg_1" d="m21,21l-18,0l9,-18l9,18z" class="selected" fill="#808080" fill-opacity="1"/> + </g> +</svg> \ No newline at end of file diff --git a/resources/images/icons/left_arrow/left_arrow_white.svg b/resources/images/icons/left_arrow/left_arrow_white.svg new file mode 100644 index 00000000..6b9c5105 --- /dev/null +++ b/resources/images/icons/left_arrow/left_arrow_white.svg @@ -0,0 +1,7 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="#000000" style=""><rect id="backgroundrect" width="100%" height="100%" x="0" y="0" fill="none" stroke="none"/> + + <g style="" class="currentLayer"> + <title>Layer 1</title> + <path transform="rotate(-90 12 12)" id="svg_1" d="m21,21l-18,0l9,-18l9,18z" class="selected" fill="#ffffff" fill-opacity="1"/> + </g> +</svg> \ No newline at end of file diff --git a/resources/images/icons/right_arrow/right_arrow_black.svg b/resources/images/icons/right_arrow/right_arrow_black.svg new file mode 100644 index 00000000..00768271 --- /dev/null +++ b/resources/images/icons/right_arrow/right_arrow_black.svg @@ -0,0 +1,7 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" fill="#000000" style=""><rect id="backgroundrect" width="100%" height="100%" x="0" y="0" fill="none" stroke="none"/> + + <g style="" class="currentLayer"> + <title>Layer 1</title> + <path transform="rotate(90 9,9.000000000000002) " id="svg_1" d="m18,18 l-18,0 l9,-18 l9,18 z" class="selected" fill="#000000" fill-opacity="1"/> + </g> +</svg> \ No newline at end of file diff --git a/resources/images/icons/right_arrow/right_arrow_eProsimaLightBlue.svg b/resources/images/icons/right_arrow/right_arrow_eProsimaLightBlue.svg new file mode 100644 index 00000000..ee6300c1 --- /dev/null +++ b/resources/images/icons/right_arrow/right_arrow_eProsimaLightBlue.svg @@ -0,0 +1,7 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" fill="#000000" style=""><rect id="backgroundrect" width="100%" height="100%" x="0" y="0" fill="none" stroke="none"/> + + <g style="" class="currentLayer"> + <title>Layer 1</title> + <path transform="rotate(90 9,9.000000000000002) " id="svg_1" d="m18,18 l-18,0 l9,-18 l9,18 z" class="selected" fill="#0895cd" fill-opacity="1"/> + </g> +</svg> \ No newline at end of file diff --git a/resources/images/icons/right_arrow/right_arrow_grey.svg b/resources/images/icons/right_arrow/right_arrow_grey.svg new file mode 100644 index 00000000..135d1b59 --- /dev/null +++ b/resources/images/icons/right_arrow/right_arrow_grey.svg @@ -0,0 +1,7 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" fill="#000000" style=""><rect id="backgroundrect" width="100%" height="100%" x="0" y="0" fill="none" stroke="none"/> + + <g style="" class="currentLayer"> + <title>Layer 1</title> + <path transform="rotate(90 9,9.000000000000002) " id="svg_1" d="m18,18 l-18,0 l9,-18 l9,18 z" class="selected" fill="#808080" fill-opacity="1"/> + </g> +</svg> \ No newline at end of file diff --git a/resources/images/icons/right_arrow/right_arrow_white.svg b/resources/images/icons/right_arrow/right_arrow_white.svg new file mode 100644 index 00000000..7bf3f71a --- /dev/null +++ b/resources/images/icons/right_arrow/right_arrow_white.svg @@ -0,0 +1,7 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" fill="#000000" style=""><rect id="backgroundrect" width="100%" height="100%" x="0" y="0" fill="none" stroke="none"/> + + <g style="" class="currentLayer"> + <title>Layer 1</title> + <path transform="rotate(90 9,9.000000000000002) " id="svg_1" d="m18,18 l-18,0 l9,-18 l9,18 z" class="selected" fill="#ffffff" fill-opacity="1"/> + </g> +</svg> \ No newline at end of file diff --git a/resources/images/icons/rounded_left_arrow/rounded_left_arrow_black.svg b/resources/images/icons/rounded_left_arrow/rounded_left_arrow_black.svg new file mode 100644 index 00000000..69b4af63 --- /dev/null +++ b/resources/images/icons/rounded_left_arrow/rounded_left_arrow_black.svg @@ -0,0 +1,12 @@ +<svg width="512" height="512" xmlns="http://www.w3.org/2000/svg" version="1.1"> + <title>triangle-filled</title> + + <g> + <title>Layer 1</title> + <g transform="rotate(-90 256.148 245.005)" opacity="0.97" fill-rule="evenodd" fill="none" id="Page-1"> + <g fill="#000000" id="drop"> + <path id="Combined-Shape" d="m277.31293,48.2956c6.61467,3.77981 12.09663,9.26178 15.87644,15.87644l182.47767,319.33592c11.69109,20.45941 4.58297,46.52254 -15.87645,58.21364c-6.44683,3.6839 -13.74344,5.62162 -21.16859,5.62162l-364.95533,0c-23.56415,0 -42.66667,-19.10252 -42.66667,-42.66667c0,-7.42514 1.93772,-14.72176 5.62163,-21.16859l182.47766,-319.33592c11.69109,-20.45941 37.75423,-27.56753 58.21364,-15.87644z"/> + </g> + </g> + </g> +</svg> \ No newline at end of file diff --git a/resources/images/icons/rounded_left_arrow/rounded_left_arrow_eProsimaLightBlue.svg b/resources/images/icons/rounded_left_arrow/rounded_left_arrow_eProsimaLightBlue.svg new file mode 100644 index 00000000..e77aebf4 --- /dev/null +++ b/resources/images/icons/rounded_left_arrow/rounded_left_arrow_eProsimaLightBlue.svg @@ -0,0 +1,12 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" version="1.1" style=""><rect id="backgroundrect" width="100%" height="100%" x="0" y="0" fill="none" stroke="none"/> + <title>triangle-filled</title> + + <g style="" class="currentLayer"> + <title>Layer 1</title> + <g transform="rotate(-90 257.16842651367193,244.49479675292972) " opacity="0.97" fill-rule="evenodd" fill="#0895cd" id="Page-1" class="selected" fill-opacity="1"> + <g fill="#0895cd" id="drop" fill-opacity="1"> + <path id="Combined-Shape" d="m278.33333815353393,47.78539592323303 c6.61467,3.77981 12.09663,9.26178 15.87644,15.87644 l182.47767,319.33592 c11.69109,20.45941 4.58297,46.52254 -15.87645,58.21364 c-6.44683,3.6839000000000004 -13.74344,5.62162 -21.168590000000002,5.62162 l-364.95533,0 c-23.56415,0 -42.66667,-19.10252 -42.66667,-42.66667 c0,-7.42514 1.93772,-14.72176 5.62163,-21.168590000000002 l182.47766,-319.33592 c11.69109,-20.45941 37.75423,-27.56753 58.21364,-15.87644 z" fill="#0895cd" fill-opacity="1"/> + </g> + </g> + </g> +</svg> \ No newline at end of file diff --git a/resources/images/icons/rounded_left_arrow/rounded_left_arrow_grey.svg b/resources/images/icons/rounded_left_arrow/rounded_left_arrow_grey.svg new file mode 100644 index 00000000..8e1c8a0c --- /dev/null +++ b/resources/images/icons/rounded_left_arrow/rounded_left_arrow_grey.svg @@ -0,0 +1,12 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" version="1.1" style=""><rect id="backgroundrect" width="100%" height="100%" x="0" y="0" fill="none" stroke="none"/> + <title>triangle-filled</title> + + <g style="" class="currentLayer"> + <title>Layer 1</title> + <g transform="rotate(-90 257.16842651367193,244.49479675292972) " opacity="0.97" fill-rule="evenodd" fill="#808080" id="Page-1" class="selected" fill-opacity="1"> + <g fill="#808080" id="drop" fill-opacity="1"> + <path id="Combined-Shape" d="m278.33333815353393,47.78539592323303 c6.61467,3.77981 12.09663,9.26178 15.87644,15.87644 l182.47767,319.33592 c11.69109,20.45941 4.58297,46.52254 -15.87645,58.21364 c-6.44683,3.6839000000000004 -13.74344,5.62162 -21.168590000000002,5.62162 l-364.95533,0 c-23.56415,0 -42.66667,-19.10252 -42.66667,-42.66667 c0,-7.42514 1.93772,-14.72176 5.62163,-21.168590000000002 l182.47766,-319.33592 c11.69109,-20.45941 37.75423,-27.56753 58.21364,-15.87644 z" fill="#808080" fill-opacity="1"/> + </g> + </g> + </g> +</svg> \ No newline at end of file diff --git a/resources/images/icons/rounded_left_arrow/rounded_left_arrow_white.svg b/resources/images/icons/rounded_left_arrow/rounded_left_arrow_white.svg new file mode 100644 index 00000000..031e91bb --- /dev/null +++ b/resources/images/icons/rounded_left_arrow/rounded_left_arrow_white.svg @@ -0,0 +1,12 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" version="1.1" style=""><rect id="backgroundrect" width="100%" height="100%" x="0" y="0" fill="none" stroke="none"/> + <title>triangle-filled</title> + + <g style="" class="currentLayer"> + <title>Layer 1</title> + <g transform="rotate(-90 256.1480102539063,245.00500488281253) " opacity="0.97" fill-rule="evenodd" fill="#ffffff" id="Page-1" class="selected" fill-opacity="1"> + <g fill="#ffffff" id="drop" fill-opacity="1"> + <path id="Combined-Shape" d="m277.31293,48.2956c6.61467,3.77981 12.09663,9.26178 15.87644,15.87644l182.47767,319.33592c11.69109,20.45941 4.58297,46.52254 -15.87645,58.21364c-6.44683,3.6839 -13.74344,5.62162 -21.16859,5.62162l-364.95533,0c-23.56415,0 -42.66667,-19.10252 -42.66667,-42.66667c0,-7.42514 1.93772,-14.72176 5.62163,-21.16859l182.47766,-319.33592c11.69109,-20.45941 37.75423,-27.56753 58.21364,-15.87644z" fill="#ffffff" fill-opacity="1"/> + </g> + </g> + </g> +</svg> \ No newline at end of file diff --git a/resources/images/icons/rounded_right_arrow/rounded_right_arrow_black.svg b/resources/images/icons/rounded_right_arrow/rounded_right_arrow_black.svg new file mode 100644 index 00000000..ace9468d --- /dev/null +++ b/resources/images/icons/rounded_right_arrow/rounded_right_arrow_black.svg @@ -0,0 +1,12 @@ +<svg width="512" height="512" xmlns="http://www.w3.org/2000/svg" version="1.1"> + <title>triangle-filled</title> + + <g> + <title>Layer 1</title> + <g transform="rotate(90 256.148 245.005)" fill-rule="evenodd" fill="none" id="Page-1"> + <g fill="#000000" id="drop"> + <path id="Combined-Shape" d="m277.31293,48.2956c6.61467,3.77981 12.09663,9.26178 15.87644,15.87644l182.47767,319.33592c11.69109,20.45941 4.58297,46.52254 -15.87645,58.21364c-6.44683,3.6839 -13.74344,5.62162 -21.16859,5.62162l-364.95533,0c-23.56415,0 -42.66667,-19.10252 -42.66667,-42.66667c0,-7.42514 1.93772,-14.72176 5.62163,-21.16859l182.47766,-319.33592c11.69109,-20.45941 37.75423,-27.56753 58.21364,-15.87644z"/> + </g> + </g> + </g> +</svg> \ No newline at end of file diff --git a/resources/images/icons/rounded_right_arrow/rounded_right_arrow_eProsimaLightBlue.svg b/resources/images/icons/rounded_right_arrow/rounded_right_arrow_eProsimaLightBlue.svg new file mode 100644 index 00000000..613e3233 --- /dev/null +++ b/resources/images/icons/rounded_right_arrow/rounded_right_arrow_eProsimaLightBlue.svg @@ -0,0 +1,12 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" version="1.1" style=""><rect id="backgroundrect" width="100%" height="100%" x="0" y="0" fill="none" stroke="none"/> + <title>triangle-filled</title> + + <g style="" class="currentLayer"> + <title>Layer 1</title> + <g transform="rotate(90 256.1480102539063,245.00500488281253) " fill-rule="evenodd" fill="#0895cd" id="Page-1" class="selected" fill-opacity="1"> + <g fill="#0895cd" id="drop" fill-opacity="1"> + <path id="Combined-Shape" d="m277.31293,48.2956c6.61467,3.77981 12.09663,9.26178 15.87644,15.87644l182.47767,319.33592c11.69109,20.45941 4.58297,46.52254 -15.87645,58.21364c-6.44683,3.6839 -13.74344,5.62162 -21.16859,5.62162l-364.95533,0c-23.56415,0 -42.66667,-19.10252 -42.66667,-42.66667c0,-7.42514 1.93772,-14.72176 5.62163,-21.16859l182.47766,-319.33592c11.69109,-20.45941 37.75423,-27.56753 58.21364,-15.87644z" fill="#0895cd" fill-opacity="1"/> + </g> + </g> + </g> +</svg> \ No newline at end of file diff --git a/resources/images/icons/rounded_right_arrow/rounded_right_arrow_grey.svg b/resources/images/icons/rounded_right_arrow/rounded_right_arrow_grey.svg new file mode 100644 index 00000000..3ef61017 --- /dev/null +++ b/resources/images/icons/rounded_right_arrow/rounded_right_arrow_grey.svg @@ -0,0 +1,12 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" version="1.1" style=""><rect id="backgroundrect" width="100%" height="100%" x="0" y="0" fill="none" stroke="none"/> + <title>triangle-filled</title> + + <g style="" class="currentLayer"> + <title>Layer 1</title> + <g transform="rotate(90 256.1480102539063,245.00500488281253) " fill-rule="evenodd" fill="#808080" id="Page-1" class="selected" fill-opacity="1"> + <g fill="#808080" id="drop" fill-opacity="1"> + <path id="Combined-Shape" d="m277.31293,48.2956c6.61467,3.77981 12.09663,9.26178 15.87644,15.87644l182.47767,319.33592c11.69109,20.45941 4.58297,46.52254 -15.87645,58.21364c-6.44683,3.6839 -13.74344,5.62162 -21.16859,5.62162l-364.95533,0c-23.56415,0 -42.66667,-19.10252 -42.66667,-42.66667c0,-7.42514 1.93772,-14.72176 5.62163,-21.16859l182.47766,-319.33592c11.69109,-20.45941 37.75423,-27.56753 58.21364,-15.87644z" fill="#808080" fill-opacity="1"/> + </g> + </g> + </g> +</svg> \ No newline at end of file diff --git a/resources/images/icons/rounded_right_arrow/rounded_right_arrow_white.svg b/resources/images/icons/rounded_right_arrow/rounded_right_arrow_white.svg new file mode 100644 index 00000000..33cb74e1 --- /dev/null +++ b/resources/images/icons/rounded_right_arrow/rounded_right_arrow_white.svg @@ -0,0 +1,12 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" version="1.1" style=""><rect id="backgroundrect" width="100%" height="100%" x="0" y="0" fill="none" stroke="none"/> + <title>triangle-filled</title> + + <g style="" class="currentLayer"> + <title>Layer 1</title> + <g transform="rotate(90 256.1480102539063,245.00500488281253) " fill-rule="evenodd" fill="#ffffff" id="Page-1" class="selected" fill-opacity="1"> + <g fill="#ffffff" id="drop" fill-opacity="1"> + <path id="Combined-Shape" d="m277.31293,48.2956c6.61467,3.77981 12.09663,9.26178 15.87644,15.87644l182.47767,319.33592c11.69109,20.45941 4.58297,46.52254 -15.87645,58.21364c-6.44683,3.6839 -13.74344,5.62162 -21.16859,5.62162l-364.95533,0c-23.56415,0 -42.66667,-19.10252 -42.66667,-42.66667c0,-7.42514 1.93772,-14.72176 5.62163,-21.16859l182.47766,-319.33592c11.69109,-20.45941 37.75423,-27.56753 58.21364,-15.87644z" fill="#ffffff" fill-opacity="1"/> + </g> + </g> + </g> +</svg> \ No newline at end of file diff --git a/resources/images/topic_graph.svg b/resources/images/topic_graph.svg new file mode 100644 index 00000000..a1c47775 --- /dev/null +++ b/resources/images/topic_graph.svg @@ -0,0 +1,37 @@ +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="1920" height="1080" style="shape-rendering: geometricprecision; text-rendering: geometricprecision; fill-rule: evenodd; clip-rule: evenodd;"><rect id="backgroundrect" width="100%" height="100%" x="0" y="0" fill="none" stroke="none"/> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +<g class="currentLayer" style=""><title>Layer 1</title><g id="svg_1" class="selected" fill-opacity="0" fill="#000000"><path style="opacity:1" fill="#000000" d="M -0.5,-0.5 C 639.5,-0.5 1279.5,-0.5 1919.5,-0.5C 1919.5,359.5 1919.5,719.5 1919.5,1079.5C 1279.5,1079.5 639.5,1079.5 -0.5,1079.5C -0.5,719.5 -0.5,359.5 -0.5,-0.5 Z" id="svg_2"/></g><g id="svg_3"><path style="opacity:1" fill="#0a95cd" d="M 490.5,89.5 C 574.501,89.3333 658.501,89.5 742.5,90C 752.333,93.1667 758.833,99.6667 762,109.5C 762.667,154.167 762.667,198.833 762,243.5C 758.899,252.934 752.732,259.434 743.5,263C 658.833,263.667 574.167,263.667 489.5,263C 480.573,259.74 474.739,253.573 472,244.5C 471.333,199.167 471.333,153.833 472,108.5C 475.352,99.3157 481.519,92.9824 490.5,89.5 Z" id="svg_4"/></g><g id="svg_5"><path style="opacity:1" fill="#bbbbbb" d="M 1204.5,89.5 C 1285.38,117.961 1366.05,147.128 1446.5,177C 1368.91,204.975 1291.41,233.142 1214,261.5C 1213.5,504.5 1213.33,747.5 1213.5,990.5C 1209.5,990.5 1205.5,990.5 1201.5,990.5C 1201.5,747.5 1201.5,504.5 1201.5,261.5C 1123.09,233.64 1044.75,205.473 966.5,177C 1045.82,147.509 1125.15,118.342 1204.5,89.5 Z" id="svg_6"/></g><g id="svg_7"><path style="opacity:1" fill="#fbfdfe" d="M 600.5,107.5 C 644.261,103.603 672.095,122.603 684,164.5C 687.519,198.637 674.352,223.804 644.5,240C 610.117,253 581.284,245.5 558,217.5C 541.807,190.993 541.473,164.326 557,137.5C 567.996,122.419 582.496,112.419 600.5,107.5 Z" id="svg_8"/></g><g id="svg_9"><path style="opacity:1" fill="#1296cd" d="M 603.5,111.5 C 640.174,108.716 665.008,124.382 678,158.5C 683.321,179.604 679.655,198.937 667,216.5C 661.858,209.371 655.025,204.538 646.5,202C 625.5,201.333 604.5,201.333 583.5,202C 574.975,204.538 568.142,209.371 563,216.5C 541.676,182.856 545.51,152.356 574.5,125C 583.469,118.847 593.136,114.347 603.5,111.5 Z" id="svg_10"/></g><g id="svg_11"><path style="opacity:1" fill="#fdfdfd" d="M 1192.5,119.5 C 1196.68,118.704 1199.85,120.037 1202,123.5C 1201.3,132.858 1200.13,142.191 1198.5,151.5C 1207.17,151.5 1215.83,151.5 1224.5,151.5C 1225.24,142.057 1226.74,132.724 1229,123.5C 1232.96,118.564 1237.29,118.231 1242,122.5C 1241.39,132.151 1240.56,141.818 1239.5,151.5C 1246.92,151.109 1254.25,151.609 1261.5,153C 1266.1,158.697 1265.1,163.03 1258.5,166C 1250.84,166.5 1243.17,166.666 1235.5,166.5C 1234.17,173.451 1233.17,180.451 1232.5,187.5C 1242.25,187.08 1251.91,187.58 1261.5,189C 1265.5,193 1265.5,197 1261.5,201C 1250.91,202.263 1240.24,202.93 1229.5,203C 1227.76,212.475 1225.93,221.975 1224,231.5C 1220.34,235.115 1216.34,235.448 1212,232.5C 1211.02,230.634 1210.52,228.634 1210.5,226.5C 1211.84,218.488 1213.17,210.488 1214.5,202.5C 1205.83,202.5 1197.17,202.5 1188.5,202.5C 1187.55,212.671 1185.71,222.671 1183,232.5C 1173.07,236.575 1169.07,233.242 1171,222.5C 1171.8,215.892 1172.96,209.392 1174.5,203C 1167.5,202.667 1160.5,202.333 1153.5,202C 1146.83,197.333 1146.83,192.667 1153.5,188C 1161.49,187.5 1169.49,187.334 1177.5,187.5C 1178.59,180.521 1179.59,173.521 1180.5,166.5C 1171.49,166.666 1162.49,166.5 1153.5,166C 1146.83,161.333 1146.83,156.667 1153.5,152C 1163.52,151.832 1173.52,151.332 1183.5,150.5C 1185,142.183 1186.5,133.849 1188,125.5C 1188.8,122.854 1190.3,120.854 1192.5,119.5 Z" id="svg_12"/></g><g id="svg_13"><path style="opacity:1" fill="#fdfefe" d="M 606.5,132.5 C 626.976,129.81 640.476,138.144 647,157.5C 649.054,178.577 639.888,191.744 619.5,197C 602.246,198.083 590.412,190.583 584,174.5C 579.72,154.054 587.22,140.054 606.5,132.5 Z" id="svg_14"/></g><g id="svg_15"><path style="opacity:1" fill="#bbbbbb" d="M 1195.5,166.5 C 1203.83,166.5 1212.17,166.5 1220.5,166.5C 1219.66,173.519 1218.66,180.519 1217.5,187.5C 1209.17,187.5 1200.83,187.5 1192.5,187.5C 1193.04,180.421 1194.04,173.421 1195.5,166.5 Z" id="svg_16"/></g><g id="svg_17"><path style="opacity:1" fill="#bbbbbb" d="M 610.5,264.5 C 615.167,264.5 619.833,264.5 624.5,264.5C 624.5,288.5 624.5,312.5 624.5,336.5C 619.833,336.5 615.167,336.5 610.5,336.5C 610.5,312.5 610.5,288.5 610.5,264.5 Z" id="svg_18"/></g><g id="svg_19"><path style="opacity:1" fill="#88ce28" d="M 703.5,337.5 C 703.5,372.833 703.5,408.167 703.5,443.5C 677.167,443.5 650.833,443.5 624.5,443.5C 619.833,443.5 615.167,443.5 610.5,443.5C 583.5,443.5 556.5,443.5 529.5,443.5C 529.5,408.167 529.5,372.833 529.5,337.5C 587.5,337.5 645.5,337.5 703.5,337.5 Z" id="svg_20"/></g><g id="svg_21"><path style="opacity:1" fill="#f9fcf4" d="M 639.5,345.5 C 640.552,345.351 641.552,345.517 642.5,346C 648,351.5 653.5,357 659,362.5C 659.667,364.167 659.667,365.833 659,367.5C 638.833,387.667 618.667,407.833 598.5,428C 589.648,431.178 580.648,433.511 571.5,435C 571.657,425.192 573.824,415.692 578,406.5C 598.422,385.911 618.922,365.577 639.5,345.5 Z" id="svg_22"/></g><g id="svg_23"><path style="opacity:1" fill="#8ccf31" d="M 639.5,353.5 C 644.239,356.731 648.572,360.564 652.5,365C 650.65,367.018 648.65,368.851 646.5,370.5C 642.348,366.849 638.348,363.016 634.5,359C 636.373,357.296 638.04,355.463 639.5,353.5 Z" id="svg_24"/></g><g id="svg_25"><path style="opacity:1" fill="#89ce2b" d="M 629.5,363.5 C 634.239,366.731 638.572,370.564 642.5,375C 627.138,390.029 612.138,405.362 597.5,421C 596.833,421.667 596.167,421.667 595.5,421C 592.299,417.464 588.799,414.297 585,411.5C 584.333,410.5 584.333,409.5 585,408.5C 600.035,393.632 614.869,378.632 629.5,363.5 Z" id="svg_26"/></g><g id="svg_27"><path style="opacity:1" fill="#bbbbbb" d="M 1171.5,398.5 C 1172.29,398.217 1172.96,397.717 1173.5,397C 1017.17,396.5 860.834,396.333 704.5,396.5C 704.5,392.5 704.5,388.5 704.5,384.5C 860.834,384.667 1017.17,384.5 1173.5,384C 1172.96,383.283 1172.29,382.783 1171.5,382.5C 1167.88,380.049 1164.38,377.383 1161,374.5C 1158.45,367.388 1160.95,363.888 1168.5,364C 1178.95,371.451 1189.12,379.284 1199,387.5C 1199.67,389.5 1199.67,391.5 1199,393.5C 1189.12,401.716 1178.95,409.549 1168.5,417C 1161.8,417.297 1158.96,414.13 1160,407.5C 1163.71,404.285 1167.54,401.285 1171.5,398.5 Z" id="svg_28"/></g><g id="svg_29"><path style="opacity:1" fill="#cccccc" d="M 1171.5,382.5 C 1172.29,382.783 1172.96,383.283 1173.5,384C 1017.17,384.5 860.834,384.667 704.5,384.5C 704.5,384.167 704.5,383.833 704.5,383.5C 860.334,383.833 1016,383.5 1171.5,382.5 Z" id="svg_30"/></g><g id="svg_31"><path style="opacity:1" fill="#dcdfd6" d="M 703.5,337.5 C 704.496,352.658 704.83,367.991 704.5,383.5C 704.5,383.833 704.5,384.167 704.5,384.5C 704.5,388.5 704.5,392.5 704.5,396.5C 860.834,396.333 1017.17,396.5 1173.5,397C 1172.96,397.717 1172.29,398.217 1171.5,398.5C 1016,397.5 860.334,397.167 704.5,397.5C 704.83,413.009 704.496,428.342 703.5,443.5C 703.5,408.167 703.5,372.833 703.5,337.5 Z" id="svg_32"/></g><g id="svg_33"><path style="opacity:1" fill="#8fd037" d="M 580.5,415.5 C 583.812,418.146 586.812,421.146 589.5,424.5C 585.625,425.759 581.791,427.092 578,428.5C 578.655,424.2 579.488,419.866 580.5,415.5 Z" id="svg_34"/></g><g id="svg_35"><path style="opacity:1" fill="#bbbbba" d="M 610.5,443.5 C 615.167,443.5 619.833,443.5 624.5,443.5C 624.5,470.167 624.5,496.833 624.5,523.5C 619.833,523.5 615.167,523.5 610.5,523.5C 610.5,496.833 610.5,470.167 610.5,443.5 Z" id="svg_36"/></g><g id="svg_37"><path style="opacity:1" fill="#0d95cd" d="M 625.5,757.5 C 620.5,757.5 615.5,757.5 610.5,757.5C 571.165,757.667 531.832,757.5 492.5,757C 482.667,753.833 476.167,747.333 473,737.5C 472.333,692.5 472.333,647.5 473,602.5C 476.254,592.281 483.087,585.781 493.5,583C 575.833,582.333 658.167,582.333 740.5,583C 751.777,585.211 759.277,591.711 763,602.5C 763.667,647.167 763.667,691.833 763,736.5C 760.175,747.325 753.342,754.158 742.5,757C 703.501,757.5 664.501,757.667 625.5,757.5 Z" id="svg_38"/></g><g id="svg_39"><path style="opacity:1" fill="#fcfdfe" d="M 609.5,600.5 C 646.293,599.135 671.46,615.468 685,649.5C 692.504,681.66 683.337,707.827 657.5,728C 639.854,738.775 620.854,742.108 600.5,738C 562.68,724.491 545.513,697.658 549,657.5C 557.13,625.532 577.297,606.532 609.5,600.5 Z" id="svg_40"/></g><g id="svg_41"><path style="opacity:1" fill="#1196cd" d="M 609.5,604.5 C 646.586,603.437 670.753,620.437 682,655.5C 685.879,675.312 681.879,693.312 670,709.5C 663.744,701.455 655.577,696.621 645.5,695C 626.5,694.333 607.5,694.333 588.5,695C 579.435,697.397 571.935,702.23 566,709.5C 547.694,682.272 548.028,655.272 567,628.5C 578.702,615.905 592.869,607.905 609.5,604.5 Z" id="svg_42"/></g><g id="svg_43"><path style="opacity:1" fill="#fdfefe" d="M 611.5,625.5 C 631.058,624.353 643.891,633.02 650,651.5C 650.971,678.862 637.805,691.696 610.5,690C 591.837,683.531 583.67,670.365 586,650.5C 590.452,637.88 598.952,629.547 611.5,625.5 Z" id="svg_44"/></g><g id="svg_45"><path style="opacity:1" fill="#bbbaba" d="M 610.5,757.5 C 615.5,757.5 620.5,757.5 625.5,757.5C 625.5,781.833 625.5,806.167 625.5,830.5C 620.5,830.5 615.5,830.5 610.5,830.5C 610.5,806.167 610.5,781.833 610.5,757.5 Z" id="svg_46"/></g><g id="svg_47"><path style="opacity:1" fill="#ffc10b" d="M 610.5,830.5 C 615.5,830.5 620.5,830.5 625.5,830.5C 652.167,830.5 678.833,830.5 705.5,830.5C 705.5,847.5 705.5,864.5 705.5,881.5C 705.5,882.833 705.5,884.167 705.5,885.5C 705.5,902.833 705.5,920.167 705.5,937.5C 678.833,937.5 652.167,937.5 625.5,937.5C 620.5,937.5 615.5,937.5 610.5,937.5C 583.833,937.5 557.167,937.5 530.5,937.5C 530.5,901.833 530.5,866.167 530.5,830.5C 557.167,830.5 583.833,830.5 610.5,830.5 Z" id="svg_48"/></g><g id="svg_49"><path style="opacity:1" fill="#fffcf6" d="M 591.5,842.5 C 600.547,841.149 608.714,843.149 616,848.5C 620.883,844.317 626.55,842.317 633,842.5C 641.418,843.184 649.584,845.017 657.5,848C 661.833,848.333 666.167,848.667 670.5,849C 671.333,849.833 672.167,850.667 673,851.5C 673.667,875.167 673.667,898.833 673,922.5C 669.397,924.128 665.563,924.962 661.5,925C 628.138,927.214 594.805,926.88 561.5,924C 560.667,923.167 559.833,922.333 559,921.5C 558.333,898.167 558.333,874.833 559,851.5C 559.833,850.667 560.667,849.833 561.5,849C 571.911,848.354 581.911,846.187 591.5,842.5 Z" id="svg_50"/></g><g id="svg_51"><path style="opacity:1" fill="#ffc009" d="M 590.5,846.5 C 595.179,846.334 599.845,846.501 604.5,847C 608.127,847.564 611.293,849.064 614,851.5C 614.667,871.5 614.667,891.5 614,911.5C 609.045,907.528 603.378,905.862 597,906.5C 587.9,906.82 579.067,908.487 570.5,911.5C 570.333,892.164 570.5,872.83 571,853.5C 577.414,850.642 583.914,848.309 590.5,846.5 Z" id="svg_52"/></g><g id="svg_53"><path style="opacity:1" fill="#ffc004" d="M 619.5,910.5 C 618.503,891.007 618.17,871.34 618.5,851.5C 622.51,847.773 627.343,846.107 633,846.5C 637.531,846.719 642.031,847.219 646.5,848C 651.298,850.211 656.298,851.711 661.5,852.5C 661.5,872.167 661.5,891.833 661.5,911.5C 652.671,908.934 643.671,907.268 634.5,906.5C 628.892,905.818 623.892,907.152 619.5,910.5 Z" id="svg_54"/></g><g id="svg_55"><path style="opacity:1" fill="#ffc219" d="M 563.5,852.5 C 564.5,852.5 565.5,852.5 566.5,852.5C 566.169,874.007 566.503,895.34 567.5,916.5C 581.994,917.331 596.661,917.831 611.5,918C 613,918.5 614.5,919 616,919.5C 619.066,918.287 622.233,917.453 625.5,917C 638.513,916.596 651.513,916.596 664.5,917C 665.494,895.242 665.827,873.409 665.5,851.5C 667.39,851.297 668.89,851.963 670,853.5C 670.833,875.841 670.666,898.174 669.5,920.5C 634.216,924.285 598.883,924.452 563.5,921C 563,920.5 562.5,920 562,919.5C 561.333,897.5 561.333,875.5 562,853.5C 562.383,852.944 562.883,852.611 563.5,852.5 Z" id="svg_56"/></g><g id="svg_57"><path style="opacity:1" fill="#ffe081" d="M 563.5,852.5 C 564.568,851.566 565.901,851.232 567.5,851.5C 567.5,873.167 567.5,894.833 567.5,916.5C 566.503,895.34 566.169,874.007 566.5,852.5C 565.5,852.5 564.5,852.5 563.5,852.5 Z" id="svg_58"/></g><g id="svg_59"><path style="opacity:1" fill="#ffe28f" d="M 618.5,851.5 C 618.17,871.34 618.503,891.007 619.5,910.5C 619.44,911.043 619.107,911.376 618.5,911.5C 617.172,891.337 617.172,871.337 618.5,851.5 Z" id="svg_60"/></g><g id="svg_61"><path style="opacity:1" fill="#bcbcbc" d="M 905.5,891.5 C 848.001,890.335 790.335,890.168 732.5,891C 733.044,891.717 733.711,892.217 734.5,892.5C 739.415,895.246 743.082,899.079 745.5,904C 744.422,909.545 741.089,911.545 735.5,910C 724.634,902.608 714.634,894.442 705.5,885.5C 705.5,884.167 705.5,882.833 705.5,881.5C 714.288,874.228 723.288,867.061 732.5,860C 736.274,856.98 740.107,856.813 744,859.5C 745.901,862.747 745.735,865.913 743.5,869C 739.656,871.422 735.99,874.089 732.5,877C 888.5,877.5 1044.5,877.667 1200.5,877.5C 1200.5,882.167 1200.5,886.833 1200.5,891.5C 1102.17,891.5 1003.83,891.5 905.5,891.5 Z" id="svg_62"/></g><g id="svg_63"><path style="opacity:1" fill="#e3e3e3" d="M 905.5,891.5 C 848.331,891.168 791.331,891.501 734.5,892.5C 733.711,892.217 733.044,891.717 732.5,891C 790.335,890.168 848.001,890.335 905.5,891.5 Z" id="svg_64"/></g><g id="svg_65"><path style="opacity:1" fill="#ffc628" d="M 594.5,909.5 C 599.937,908.967 604.937,910.134 609.5,913C 599.5,913.667 589.5,913.667 579.5,913C 584.545,911.425 589.545,910.259 594.5,909.5 Z" id="svg_66"/></g><g id="svg_67"><path style="opacity:1" fill="#ffc72b" d="M 629.5,909.5 C 637.334,909.604 645.001,910.771 652.5,913C 642.5,913.667 632.5,913.667 622.5,913C 624.925,911.787 627.259,910.621 629.5,909.5 Z" id="svg_68"/></g><g id="svg_69"><path style="opacity:1" fill="#bbbbbb" d="M 610.5,937.5 C 615.5,937.5 620.5,937.5 625.5,937.5C 625.5,955.167 625.5,972.833 625.5,990.5C 620.5,990.5 615.5,990.5 610.5,990.5C 610.5,972.833 610.5,955.167 610.5,937.5 Z" id="svg_70"/></g></g></svg> \ No newline at end of file diff --git a/src/Engine.cpp b/src/Engine.cpp index 873d9461..22de100d 100644 --- a/src/Engine.cpp +++ b/src/Engine.cpp @@ -87,6 +87,15 @@ QObject* Engine::enable() generate_new_status_info_(); fill_status_(); + // Creates a default json structure for statuses and fills the tree model with it + entity_status_model_ = new models::StatusTreeModel(); + update_entity_status(backend::ID_ALL, backend::StatusKind::INVALID); + + // Creates the proxy model to allow filtering + entity_status_proxy_model_ = new models::StatusTreeModel(); + entity_status_proxy_model_->set_source_model(entity_status_model_); + + source_entity_id_model_ = new models::ListModel(new models::EntityItem()); fill_available_entity_id_list_(backend::EntityKind::HOST, "getDataDialogSourceEntityId"); destination_entity_id_model_ = new models::ListModel(new models::EntityItem()); @@ -109,6 +118,7 @@ QObject* Engine::enable() rootContext()->setContextProperty("issueModel", issue_model_); rootContext()->setContextProperty("logModel", log_model_); rootContext()->setContextProperty("statusModel", status_model_); + rootContext()->setContextProperty("entityStatusModel", entity_status_proxy_model_); rootContext()->setContextProperty("entityModelFirst", source_entity_id_model_); rootContext()->setContextProperty("entityModelSecond", destination_entity_id_model_); @@ -118,6 +128,7 @@ QObject* Engine::enable() rootContext()->setContextProperty("controller", controller_); addImportPath(":/imports"); + addImportPath(":/imports/TreeView"); load(QUrl(QLatin1String("qrc:/qml/main.qml"))); // Connect Callback Listener to this object @@ -127,6 +138,12 @@ QObject* Engine::enable() this, &Engine::new_callback_slot); + QObject::connect( + this, + &Engine::new_status_callback_signal, + this, + &Engine::new_status_callback_slot); + // Set enable as True enabled_ = true; @@ -186,6 +203,16 @@ Engine::~Engine() delete status_model_; } + if (entity_status_model_) + { + delete entity_status_model_; + } + + if (entity_status_proxy_model_) + { + delete entity_status_proxy_model_; + } + // Auxiliar models if (source_entity_id_model_) { @@ -833,12 +860,27 @@ void Engine::process_callback_queue() } } +void Engine::process_status_callback_queue() +{ + // It iterates while run_ is activate and the queue has elements + while (!status_callback_queue_.empty()) + { + process_status_callback_(); + } +} + bool Engine::are_callbacks_to_process_() { std::lock_guard<std::recursive_mutex> ml(callback_queue_mutex_); return callback_queue_.empty(); } +bool Engine::are_status_callbacks_to_process_() +{ + std::lock_guard<std::recursive_mutex> ml(status_callback_queue_mutex_); + return status_callback_queue_.empty(); +} + bool Engine::add_callback( backend::Callback callback) { @@ -851,11 +893,28 @@ bool Engine::add_callback( return true; } +bool Engine::add_callback( + backend::StatusCallback status_callback) +{ + std::lock_guard<std::recursive_mutex> ml(status_callback_queue_mutex_); + status_callback_queue_.append(status_callback); + + // Emit signal to specify there are new data + emit new_status_callback_signal(); + + return true; +} + void Engine::new_callback_slot() { process_callback_queue(); } +void Engine::new_status_callback_slot() +{ + process_status_callback_queue(); +} + bool Engine::process_callback_() { backend::Callback first_callback; @@ -871,6 +930,21 @@ bool Engine::process_callback_() return read_callback_(first_callback); } +bool Engine::process_status_callback_() +{ + backend::StatusCallback first_status_callback; + + { + std::lock_guard<std::recursive_mutex> ml(status_callback_queue_mutex_); + first_status_callback = status_callback_queue_.front(); + status_callback_queue_.pop_front(); + } + + qDebug() << "Processing status callback: " << backend::backend_id_to_models_id(first_status_callback.entity_id); + + return read_callback_(first_status_callback); +} + bool Engine::read_callback_( backend::Callback callback) { @@ -897,6 +971,325 @@ bool Engine::read_callback_( callback.entity_id, callback.entity_kind, callback.is_update); } +bool Engine::read_callback_( + backend::StatusCallback status_callback) +{ + // It should not read callbacks while a domain is being initialized + std::lock_guard<std::recursive_mutex> lock(initializing_monitor_); + + // Add callback to log model + add_log_callback_("New entity (" + backend_connection_.get_name(status_callback.entity_id) + ") status reported: " + + backend::status_kind_to_string(status_callback.status_kind), + utils::now()); + + // Update status in info model + if (last_entities_clicked_.dds.id == status_callback.entity_id) + { + info_model_->update(backend_connection_.get_info(status_callback.entity_id)); + } + + // Remove entities from status layout if needed + remove_inactive_entities_from_status_model(status_callback.entity_id); + + // update status model + return update_entity_status(status_callback.entity_id, status_callback.status_kind); +} + +bool Engine::update_entity_status( + const backend::EntityId& id, + backend::StatusKind kind) +{ + int counter = 0; + if (id == backend::ID_ALL) + { + auto empty_item = new models::StatusTreeItem(backend::ID_ALL, + std::string("No issues found"), backend::StatusLevel::OK, std::string("")); + entity_status_model_->addTopLevelItem(empty_item); + } + else + { + backend::StatusLevel new_status = backend::StatusLevel::OK; + std::string description = backend::entity_status_description(kind); + + switch (kind) + { + case backend::StatusKind::DEADLINE_MISSED: + { + backend::DeadlineMissedSample sample; + if (backend_connection_.get_status_data(id, sample)) + { + if (sample.status != backend::StatusLevel::OK) + { + backend::StatusLevel entity_status = backend_connection_.get_status(id); + auto entity_item = entity_status_model_->getTopLevelItem( + id, backend_connection_.get_name(id), entity_status, description); + new_status = sample.status; + std::string handle_string; + auto deadline_missed_item = new models::StatusTreeItem(id, kind, std::string("Deadline missed"), + sample.status, std::string(""), description); + auto total_count_item = new models::StatusTreeItem(id, kind, std::string("Total count:"), + sample.status, std::to_string(sample.deadline_missed_status.total_count()), std::string("")); + for (uint8_t handler : sample.deadline_missed_status.last_instance_handle()) + { + handle_string = handle_string + std::to_string(handler); + } + auto last_instance_handle_item = new models::StatusTreeItem(id, kind, + std::string("Last instance handle:"), sample.status, handle_string, std::string("")); + entity_status_model_->addItem(deadline_missed_item, total_count_item); + entity_status_model_->addItem(deadline_missed_item, last_instance_handle_item); + entity_status_model_->addItem(entity_item, deadline_missed_item); + counter = entity_item->recalculate_entity_counter(); + } + } + break; + } + case backend::StatusKind::INCOMPATIBLE_QOS: + { + backend::IncompatibleQosSample sample; + if (backend_connection_.get_status_data(id, sample)) + { + if (sample.status != backend::StatusLevel::OK) + { + std::string fastdds_version = "v2.12.0"; + backend::StatusLevel entity_status = backend_connection_.get_status(id); + auto entity_item = entity_status_model_->getTopLevelItem( + id, backend_connection_.get_name(id), entity_status, description); + new_status = sample.status; + auto incompatible_qos_item = new models::StatusTreeItem(id, kind, std::string("Incompatible QoS"), + sample.status, std::string(""), description); + for (eprosima::fastdds::statistics::QosPolicyCount_s policy : sample.incompatible_qos_status.policies()) + { + if (policy.count() > 0) + { + auto policy_item = new models::StatusTreeItem(id, kind, + std::string(backend::policy_id_to_string(policy.policy_id()) + ":"), + sample.status, std::to_string(policy.count()), + std::string("<html><style type=\"text/css\"></style>Check for compatible rules ") + + std::string("<a href=\"https://fast-dds.docs.eprosima.com/en/") + fastdds_version + + std::string("/fastdds/dds_layer/core/policy/standardQosPolicies.html") + + backend::policy_documentation_description(policy.policy_id()) + + std::string("\">here</a></html>")); + entity_status_model_->addItem(incompatible_qos_item, policy_item); + } + } + entity_status_model_->addItem(entity_item, incompatible_qos_item); + counter = entity_item->recalculate_entity_counter(); + } + } + break; + } + case backend::StatusKind::INCONSISTENT_TOPIC: + { + backend::InconsistentTopicSample sample; + if (backend_connection_.get_status_data(id, sample)) + { + if (sample.status != backend::StatusLevel::OK) + { + backend::StatusLevel entity_status = backend_connection_.get_status(id); + auto entity_item = entity_status_model_->getTopLevelItem( + id, backend_connection_.get_name(id), entity_status, description); + new_status = sample.status; + auto inconsistent_topic_item = new models::StatusTreeItem(id, kind, std::string("Inconsistent topics:"), + sample.status, std::to_string(sample.inconsistent_topic_status.total_count()), description); + entity_status_model_->addItem(entity_item, inconsistent_topic_item); + counter = entity_item->recalculate_entity_counter(); + } + } + break; + } + case backend::StatusKind::LIVELINESS_CHANGED: + { + backend::LivelinessChangedSample sample; + if (backend_connection_.get_status_data(id, sample)) + { + if (sample.status != backend::StatusLevel::OK) + { + backend::StatusLevel entity_status = backend_connection_.get_status(id); + auto entity_item = entity_status_model_->getTopLevelItem( + id, backend_connection_.get_name(id), entity_status, description); + new_status = sample.status; + auto liveliness_changed_item = new models::StatusTreeItem(id, kind, std::string("Liveliness changed"), + sample.status, std::string(""), description); + std::string handle_string; + auto alive_count_item = new models::StatusTreeItem(id, kind, std::string("Alive count:"), + sample.status, std::to_string(sample.liveliness_changed_status.alive_count()), std::string("")); + auto not_alive_count_item = new models::StatusTreeItem(id, kind, std::string("Not alive count:"), + sample.status, std::to_string(sample.liveliness_changed_status.not_alive_count()), std::string("")); + for (uint8_t handler : sample.liveliness_changed_status.last_publication_handle()) + { + handle_string = handle_string + std::to_string(handler); + } + auto last_publication_handle_item = new models::StatusTreeItem(id, kind, + std::string("Last publication handle:"), sample.status, handle_string, std::string("")); + + entity_status_model_->addItem(liveliness_changed_item, alive_count_item); + entity_status_model_->addItem(liveliness_changed_item, not_alive_count_item); + entity_status_model_->addItem(liveliness_changed_item, last_publication_handle_item); + entity_status_model_->addItem(entity_item, liveliness_changed_item); + counter = entity_item->recalculate_entity_counter(); + } + } + break; + } + case backend::StatusKind::LIVELINESS_LOST: + { + backend::LivelinessLostSample sample; + if (backend_connection_.get_status_data(id, sample)) + { + if (sample.status != backend::StatusLevel::OK) + { + backend::StatusLevel entity_status = backend_connection_.get_status(id); + auto entity_item = entity_status_model_->getTopLevelItem( + id, backend_connection_.get_name(id), entity_status, description); + new_status = sample.status; + auto liveliness_lost_item = new models::StatusTreeItem(id, kind, std::string("Liveliness lost:"), + sample.status, std::to_string(sample.liveliness_lost_status.total_count()), description); + entity_status_model_->addItem(entity_item, liveliness_lost_item); + counter = entity_item->recalculate_entity_counter(); + } + } + break; + } + case backend::StatusKind::SAMPLE_LOST: + { + backend::SampleLostSample sample; + if (backend_connection_.get_status_data(id, sample)) + { + if (sample.status != backend::StatusLevel::OK) + { + backend::StatusLevel entity_status = backend_connection_.get_status(id); + auto entity_item = entity_status_model_->getTopLevelItem( + id, backend_connection_.get_name(id), entity_status, description); + new_status = sample.status; + auto samples_lost_item = new models::StatusTreeItem(id, kind, std::string("Samples lost:"), + sample.status, std::to_string(sample.sample_lost_status.total_count()), description); + entity_status_model_->addItem(entity_item, samples_lost_item); + counter = entity_item->recalculate_entity_counter(); + } + } + break; + } + + case backend::StatusKind::CONNECTION_LIST: + case backend::StatusKind::PROXY: + //case backend::StatusKind::STATUSES_SIZE: + default: + { + // No entity status updates, as always returns OK + break; + } + } + if (new_status != backend::StatusLevel::OK) + { + if (new_status == backend::StatusLevel::ERROR) + { + std::map<backend::EntityId,uint32_t>::iterator it = controller_->status_counters.errors.find(id); + if(it != controller_->status_counters.errors.end()) + { + controller_->status_counters.total_errors -= controller_->status_counters.errors[id]; + } + controller_->status_counters.errors[id] = counter; + controller_->status_counters.total_errors += controller_->status_counters.errors[id]; + } + else if (new_status == backend::StatusLevel::WARNING) + { + std::map<backend::EntityId,uint32_t>::iterator it = controller_->status_counters.warnings.find(id); + if(it != controller_->status_counters.warnings.end()) + { + controller_->status_counters.total_warnings -= controller_->status_counters.warnings[id]; + } + controller_->status_counters.warnings[id] = counter; + controller_->status_counters.total_warnings += controller_->status_counters.warnings[id]; + } + // notify status model layout changed to refresh layout view + emit entity_status_proxy_model_->layoutAboutToBeChanged(); + + emit controller_->update_status_counters( + QString::number(controller_->status_counters.total_errors), + QString::number(controller_->status_counters.total_warnings)); + + // remove empty message if exists + if (entity_status_model_->is_empty()) + { + entity_status_model_->removeEmptyItem(); + } + + // update view + entity_status_proxy_model_->set_source_model(entity_status_model_); + + // notify status model layout changed to refresh layout view + emit entity_status_proxy_model_->layoutChanged(); + } + } + return true; +} + +bool Engine::remove_inactive_entities_from_status_model( + const backend::EntityId& id) +{ + // check if there are entities in the status model + if (!entity_status_model_->is_empty()) + { + // get info from id + EntityInfo entity_info = backend_connection_.get_info(id); + + // update status model if not alive + if (!entity_info["alive"]) + { + // remove item from tree + entity_status_model_->removeItem(entity_status_model_->getTopLevelItem(id, "", backend::StatusLevel::OK, "")); + + // add empty item if removed last item + if (entity_status_model_->rowCount(entity_status_model_->rootIndex()) == 0) + { + entity_status_model_->addTopLevelItem(new models::StatusTreeItem( + backend::ID_ALL, std::string("No issues found"), backend::StatusLevel::OK, std::string(""))); + } + + // update error counter + std::map<backend::EntityId,uint32_t>::iterator err_it = controller_->status_counters.errors.find(id); + if(err_it != controller_->status_counters.errors.end()) + { + //element found; + controller_->status_counters.total_errors -= err_it->second; + if (controller_->status_counters.total_errors < 0) + { + controller_->status_counters.total_errors = 0; + } + } + controller_->status_counters.errors.erase(id); + + // update warning counter + std::map<backend::EntityId,uint32_t>::iterator warn_it = controller_->status_counters.warnings.find(id); + if(warn_it != controller_->status_counters.warnings.end()) + { + //element found; + controller_->status_counters.total_warnings -= warn_it->second; + if (controller_->status_counters.total_warnings < 0) + { + controller_->status_counters.total_warnings = 0; + } + } + controller_->status_counters.warnings.erase(id); + + // refresh layout + emit entity_status_proxy_model_->layoutAboutToBeChanged(); + + emit controller_->update_status_counters( + QString::number(controller_->status_counters.total_errors), + QString::number(controller_->status_counters.total_warnings)); + + // update view + entity_status_proxy_model_->set_source_model(entity_status_model_); + + // notify status model layout changed to refresh layout view + emit entity_status_proxy_model_->layoutChanged(); + } + return true; + } + return false; +} + bool Engine::update_entity_generic( backend::EntityId entity_id, backend::EntityKind entity_kind, @@ -926,14 +1319,17 @@ bool Engine::update_entity_generic( entity_id, &Engine::update_topic, !is_update, is_last_clicked); case backend::EntityKind::PARTICIPANT: + remove_inactive_entities_from_status_model(entity_id); return update_entity( entity_id, &Engine::update_participant, !is_update, is_last_clicked); case backend::EntityKind::DATAWRITER: + remove_inactive_entities_from_status_model(entity_id); return update_entity( entity_id, &Engine::update_datawriter, !is_update, is_last_clicked); case backend::EntityKind::DATAREADER: + remove_inactive_entities_from_status_model(entity_id); return update_entity( entity_id, &Engine::update_datareader, !is_update, is_last_clicked); diff --git a/src/backend/Listener.cpp b/src/backend/Listener.cpp index a00375ed..26b57379 100644 --- a/src/backend/Listener.cpp +++ b/src/backend/Listener.cpp @@ -169,4 +169,12 @@ void Listener::on_topic_discovery( } } +void Listener::on_status_reported( + EntityId domain_id, + EntityId entity_id, + StatusKind status_kind) +{ + engine_->add_callback(StatusCallback(domain_id, entity_id, status_kind)); +} + } //namespace backend diff --git a/src/backend/SyncBackendConnection.cpp b/src/backend/SyncBackendConnection.cpp index 87e2f2eb..83aec3ae 100644 --- a/src/backend/SyncBackendConnection.cpp +++ b/src/backend/SyncBackendConnection.cpp @@ -619,6 +619,18 @@ std::string SyncBackendConnection::get_name( return backend::get_info_value(get_info(id), "name"); } +std::string SyncBackendConnection::get_alias( + EntityId id) +{ + return backend::get_info_value(get_info(id), "alias"); +} + +StatusLevel SyncBackendConnection:: get_status( + EntityId id) +{ + return StatisticsBackend::get_status(id); +} + std::vector<StatisticsData> SyncBackendConnection::get_data( DataKind data_kind, EntityId source_entity_id, @@ -718,6 +730,167 @@ bool SyncBackendConnection::data_available( return !data.empty(); } +bool SyncBackendConnection::get_status_data( + EntityId id, + ConnectionListSample& sample) +{ + try + { + StatisticsBackend::get_status_data(id, sample); + return true; + } + catch (const Error& e) + { + qWarning() << "Error retrieving sample: " << e.what(); + } + catch (const BadParameter& e) + { + qWarning() << "Bad Parameter retrieving sample " << e.what(); + } + return false; +} + +bool SyncBackendConnection::get_status_data( + EntityId id, + DeadlineMissedSample& sample) +{ + try + { + StatisticsBackend::get_status_data(id, sample); + return true; + } + catch (const Error& e) + { + qWarning() << "Error retrieving sample: " << e.what(); + } + catch (const BadParameter& e) + { + qWarning() << "Bad Parameter retrieving sample " << e.what(); + } + return false; +} + +bool SyncBackendConnection::get_status_data( + EntityId id, + IncompatibleQosSample& sample) +{ + try + { + StatisticsBackend::get_status_data(id, sample); + return true; + } + catch (const Error& e) + { + qWarning() << "Error retrieving sample: " << e.what(); + } + catch (const BadParameter& e) + { + qWarning() << "Bad Parameter retrieving sample " << e.what(); + } + return false; +} + +bool SyncBackendConnection::get_status_data( + EntityId id, + InconsistentTopicSample& sample) +{ + try + { + StatisticsBackend::get_status_data(id, sample); + return true; + } + catch (const Error& e) + { + qWarning() << "Error retrieving sample: " << e.what(); + } + catch (const BadParameter& e) + { + qWarning() << "Bad Parameter retrieving sample " << e.what(); + } + return false; +} + +bool SyncBackendConnection::get_status_data( + EntityId id, + LivelinessChangedSample& sample) +{ + try + { + StatisticsBackend::get_status_data(id, sample); + return true; + } + catch (const Error& e) + { + qWarning() << "Error retrieving sample: " << e.what(); + } + catch (const BadParameter& e) + { + qWarning() << "Bad Parameter retrieving sample " << e.what(); + } + return false; +} + +bool SyncBackendConnection::get_status_data( + EntityId id, + LivelinessLostSample& sample) +{ + try + { + StatisticsBackend::get_status_data(id, sample); + return true; + } + catch (const Error& e) + { + qWarning() << "Error retrieving sample: " << e.what(); + } + catch (const BadParameter& e) + { + qWarning() << "Bad Parameter retrieving sample " << e.what(); + } + return false; +} + +bool SyncBackendConnection::get_status_data( + EntityId id, + ProxySample& sample) +{ + try + { + StatisticsBackend::get_status_data(id, sample); + return true; + } + catch (const Error& e) + { + qWarning() << "Error retrieving sample: " << e.what(); + } + catch (const BadParameter& e) + { + qWarning() << "Bad Parameter retrieving sample " << e.what(); + } + return false; +} + +bool SyncBackendConnection::get_status_data( + EntityId id, + SampleLostSample& sample) +{ + try + { + StatisticsBackend::get_status_data(id, sample); + return true; + } + catch (const Error& e) + { + qWarning() << "Error retrieving sample: " << e.what(); + } + catch (const BadParameter& e) + { + qWarning() << "Bad Parameter retrieving sample " << e.what(); + } + return false; +} + + bool SyncBackendConnection::build_source_target_entities_vectors( DataKind data_kind, EntityId source_entity_id, diff --git a/src/backend/backend_utils.cpp b/src/backend/backend_utils.cpp index 034a66e1..b5c7c51d 100644 --- a/src/backend/backend_utils.cpp +++ b/src/backend/backend_utils.cpp @@ -26,6 +26,7 @@ #include <fastdds_monitor/backend/backend_types.h> #include <fastdds_monitor/model/model_types.h> #include <fastdds_monitor/utils.h> +#include <fastdds_statistics_backend/types/JSONTags.h> #include <QDebug> @@ -123,6 +124,41 @@ std::string statistic_kind_to_string( } } +std::string status_kind_to_string( + const StatusKind& status_kind) +{ + switch (status_kind) + { + case StatusKind::CONNECTION_LIST: + return "CONNECTION_LIST"; + case StatusKind::DEADLINE_MISSED: + return "DEADLINE_MISSED"; + case StatusKind::INCOMPATIBLE_QOS: + return "INCOMPATIBLE_QOS"; + case StatusKind::INCONSISTENT_TOPIC: + return "INCONSISTENT_TOPIC"; + case StatusKind::LIVELINESS_CHANGED: + return "LIVELINESS_CHANGED"; + case StatusKind::LIVELINESS_LOST: + return "LIVELINESS_LOST"; + case StatusKind::PROXY: + return "PROXY"; + case StatusKind::SAMPLE_LOST: + return "SAMPLE_LOST"; + case StatusKind::STATUSES_SIZE: + return "STATUSES_SIZE"; + case StatusKind::INVALID: + default: + return "INVALID"; + } +} + +std::string status_level_to_string( + const StatusLevel& status_level) +{ + return eprosima::statistics_backend::status_level_str[(int)status_level]; +} + std::string data_kind_to_string( const DataKind& data_kind) { @@ -378,4 +414,144 @@ std::string timestamp_to_string( return ss.str(); } +std::string policy_id_to_string( + const uint32_t& id) +{ + switch (id) + { + case 0: // INVALID_QOS_POLICY_ID + default: + return "INVALID"; + case 1: // USERDATA_QOS_POLICY_ID + return "UserData"; + case 2: // DURABILITY_QOS_POLICY_ID + return "Durability"; + case 3: // PRESENTATION_QOS_POLICY_ID + return "Presentation"; + case 4: // DEADLINE_QOS_POLICY_ID + return "Deadline"; + case 5: // LATENCYBUDGET_QOS_POLICY_ID + return "Latency budget"; + case 6: // OWNERSHIP_QOS_POLICY_ID + return "Ownership"; + case 7: // OWNERSHIPSTRENGTH_QOS_POLICY_ID + return "Ownership strength"; + case 8: // LIVELINESS_QOS_POLICY_ID + return "Liveliness"; + case 9: // TIMEBASEDFILTER_QOS_POLICY_ID + return "Time-based filter"; + case 10: // PARTITION_QOS_POLICY_ID + return "Partition"; + case 11: // RELIABILITY_QOS_POLICY_ID + return "Reliability"; + case 12: // DESTINATIONORDER_QOS_POLICY_ID + return "Destination order"; + case 13: // HISTORY_QOS_POLICY_ID + return "History"; + case 14: // RESOURCELIMITS_QOS_POLICY_ID + return "Resource limits"; + case 15: // ENTITYFACTORY_QOS_POLICY_ID + return "Entity factory"; + case 16: // WRITERDATALIFECYCLE_QOS_POLICY_ID + return "Writer data lifecycle"; + case 17: // READERDATALIFECYCLE_QOS_POLICY_ID + return "Reader data lifecycle"; + case 18: // TOPICDATA_QOS_POLICY_ID + return "Topic data"; + case 19: // GROUPDATA_QOS_POLICY_ID + return "Group data"; + case 20: // TRANSPORTPRIORITY_QOS_POLICY_ID + return "Transport priority"; + case 21: // LIFESPAN_QOS_POLICY_ID + return "Lifespan"; + case 22: // DURABILITYSERVICE_QOS_POLICY_ID + return "Durability service"; + case 23: // DATAREPRESENTATION_QOS_POLICY_ID + return "Data representation"; + case 24: // TYPECONSISTENCYENFORCEMENT_QOS_POLICY_ID + return "Type consistency enforcement"; + case 25: // DISABLEPOSITIVEACKS_QOS_POLICY_ID + return "Disable positive acks"; + case 26: // PARTICIPANTRESOURCELIMITS_QOS_POLICY_ID + return "Participant resource limits"; + case 27: // PROPERTYPOLICY_QOS_POLICY_ID + return "Property policy"; + case 28: // PUBLISHMODE_QOS_POLICY_ID + return "Publish mode"; + case 29: // READERRESOURCELIMITS_QOS_POLICY_ID + return "Reader resource limits"; + case 30: // RTPSENDPOINT_QOS_POLICY_ID + return "RTPS endpoint"; + case 31: // RTPSRELIABLEREADER_QOS_POLICY_ID + return "RTPS reliable reader"; + case 32: // RTPSRELIABLEWRITER_QOS_POLICY_ID + return "RTPS reliable writer"; + case 33: // TRANSPORTCONFIG_QOS_POLICY_ID + return "Transport config"; + case 34: // TYPECONSISTENCY_QOS_POLICY_ID + return "Type consistency"; + case 35: // WIREPROTOCOLCONFIG_QOS_POLICY_ID + return "Wire protocol config"; + case 36: // WRITERRESOURCELIMITS_QOS_POLICY_ID + return "Writer resource limits"; + } +} + +std::string entity_status_description( + const backend::StatusKind kind) +{ + switch (kind) { + case backend::StatusKind::CONNECTION_LIST: + return "List of connections that the entity is using"; + case backend::StatusKind::DEADLINE_MISSED: + return "Number of deadlines missed that were registered in the entity"; + case backend::StatusKind::INCOMPATIBLE_QOS: + return "Tracks the number of incompatible QoS detected in the entity"; + case backend::StatusKind::INCONSISTENT_TOPIC: + return "Status of Inconsistent topics of the topic of the entity"; + case backend::StatusKind::LIVELINESS_CHANGED: + return "Tracks the number of times that liveliness status changed (reader side)"; + case backend::StatusKind::LIVELINESS_LOST: + return "Tracks the number of times that liveliness was lost (writer side)"; + case backend::StatusKind::PROXY: + return "Collection of Parameters describing the Proxy Data of the entity"; + case backend::StatusKind::SAMPLE_LOST: + return "Tracks the number of times that the entity lost samples"; + //case backend::StatusKind::STATUSES_SIZE: + // return ""; + default: + case backend::StatusKind::INVALID: + return ""; + } +} + +std::string policy_documentation_description( + const uint32_t& id) +{ + switch (id) + { + case 2: // DURABILITY_QOS_POLICY_ID + return "#durability-compatibilityrule"; + case 3: // PRESENTATION_QOS_POLICY_ID + return "#presentation-compatibilityrule"; + case 4: // DEADLINE_QOS_POLICY_ID + return "#compatibility-rule"; + case 5: // LATENCYBUDGET_QOS_POLICY_ID + return "#latencybudget-compatibilityrule"; + case 6: // OWNERSHIP_QOS_POLICY_ID + return "#ownership-compatibilityrule"; + case 8: // LIVELINESS_QOS_POLICY_ID + return "#liveliness-compatibilityrule"; + case 11: // RELIABILITY_QOS_POLICY_ID + return "#reliability-compatibilityrule"; + case 12: // DESTINATIONORDER_QOS_POLICY_ID + return "#destinationorder-compatibilityrule"; + case 14: // RESOURCELIMITS_QOS_POLICY_ID + return "#consistency-rule"; + default: + return ""; + } +} + + } // namespace backend diff --git a/src/model/tree/StatusTreeItem.cpp b/src/model/tree/StatusTreeItem.cpp new file mode 100644 index 00000000..95ab319f --- /dev/null +++ b/src/model/tree/StatusTreeItem.cpp @@ -0,0 +1,317 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2021 Maurizio Ingrassia + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +// Copyright 2023 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// This file is part of eProsima Fast DDS Monitor. +// +// eProsima Fast DDS Monitor 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 3 of the License, or +// (at your option) any later version. +// +// eProsima Fast DDS Monitor 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 eProsima Fast DDS Monitor. If not, see <https://www.gnu.org/licenses/>. + +#include <iostream> + +#include <fastdds_monitor/backend/backend_types.h> +#include <fastdds_monitor/backend/backend_utils.h> +#include <fastdds_monitor/model/tree/StatusTreeItem.h> +#include <fastdds_monitor/model/tree/StatusTreeModel.h> + +namespace models { + +StatusTreeItem::StatusTreeItem() + : parent_item_(nullptr) + , id_(backend::ID_ALL) + , kind_(backend::StatusKind::INVALID) + , name_() + , status_level_(backend::StatusLevel::OK) + , value_() + , description_() + , is_active_(true) + , id_variant_(QVariant(backend::backend_id_to_models_id(id_))) + , kind_variant_(QVariant(QString::fromStdString(backend::status_kind_to_string(kind_)))) + , name_variant_(QVariant()) + , status_level_variant_(QVariant(QString::fromStdString(backend::status_level_to_string(status_level_)))) + , value_variant_(QVariant()) + , description_variant_(QVariant()) + , is_active_variant_(QVariant(true)) +{ +} + +StatusTreeItem::StatusTreeItem( + const QVariant& data) + : parent_item_(nullptr) + , id_(backend::ID_ALL) + , kind_(backend::StatusKind::INVALID) + , name_(data.toString().toStdString()) + , status_level_(backend::StatusLevel::OK) + , value_() + , description_() + , is_active_(true) + , id_variant_(QVariant(backend::backend_id_to_models_id(id_))) + , kind_variant_(QVariant(QString::fromStdString(backend::status_kind_to_string(kind_)))) + , name_variant_(QVariant(QString::fromStdString(name_))) + , status_level_variant_(QVariant(QString::fromStdString(backend::status_level_to_string(status_level_)))) + , value_variant_(QVariant()) + , description_variant_(QVariant()) + , is_active_variant_(QVariant(true)) +{ +} + +StatusTreeItem::StatusTreeItem( + const backend::EntityId& id, + const std::string& name, + const backend::StatusLevel& status_level, + const std::string& description) + : parent_item_(nullptr) + , id_(id) + , kind_(backend::StatusKind::INVALID) + , name_(name) + , status_level_(status_level) + , value_() + , description_(description) + , is_active_(true) + , id_variant_(QVariant(backend::backend_id_to_models_id(id_))) + , kind_variant_(QVariant(QString::fromStdString(backend::status_kind_to_string(kind_)))) + , name_variant_(QVariant(QString::fromStdString(name))) + , status_level_variant_(QVariant(QString::fromStdString(backend::status_level_to_string(status_level_)))) + , value_variant_(QVariant()) + , description_variant_(QVariant(QString::fromStdString(description))) + , is_active_variant_(QVariant(true)) +{ +} + +StatusTreeItem::StatusTreeItem( + const backend::EntityId& id, + const backend::StatusKind& kind, + const std::string& name, + const backend::StatusLevel& status_level, + const std::string& value, + const std::string& description) + : parent_item_(nullptr) + , id_(id) + , kind_(kind) + , name_(name) + , status_level_(status_level) + , value_(value) + , description_(description) + , is_active_(true) + , id_variant_(QVariant(backend::backend_id_to_models_id(id_))) + , kind_variant_(QVariant(QString::fromStdString(backend::status_kind_to_string(kind_)))) + , name_variant_(QVariant(QString::fromStdString(name_))) + , status_level_variant_(QVariant(QString::fromStdString(backend::status_level_to_string(status_level_)))) + , value_variant_(QVariant(QString::fromStdString(value))) + , description_variant_(QVariant(QString::fromStdString(description))) + , is_active_variant_(QVariant(true)) +{ +} + +StatusTreeItem::~StatusTreeItem() +{ + qDeleteAll(child_items_); +} + +StatusTreeItem* StatusTreeItem::parentItem() +{ + return parent_item_; +} + +void StatusTreeItem::setParentItem( + StatusTreeItem* parentItem) +{ + parent_item_ = parentItem; +} + +void StatusTreeItem::appendChild( + StatusTreeItem* item) +{ + if (item && !child_items_.contains(item)) + { + child_items_.append(item); + } +} + +void StatusTreeItem::removeChild( + StatusTreeItem* item) +{ + if (item) + { + child_items_.removeAll(item); + delete item; + } +} + +StatusTreeItem* StatusTreeItem::child( + int row) +{ + return child_items_.value(row); +} + +int StatusTreeItem::childCount() const +{ + return child_items_.count(); +} + +const QVariant& StatusTreeItem::entity_id() const +{ + return id_variant_; +} + +const QVariant& StatusTreeItem::status_kind() const +{ + return kind_variant_; +} + +const QVariant& StatusTreeItem::name() const +{ + return name_variant_; +} + +const QVariant& StatusTreeItem::status() const +{ + return status_level_variant_; +} + +const QVariant& StatusTreeItem::value() const +{ + return value_variant_; +} + +const QVariant& StatusTreeItem::description() const +{ + return description_variant_; +} + +const QVariant& StatusTreeItem::alive() const +{ + return is_active_variant_; +} + +void StatusTreeItem::setData( + const QVariant& data) +{ + name_variant_ = data; +} + +bool StatusTreeItem::isLeaf() const +{ + return child_items_.isEmpty(); +} + +int StatusTreeItem::depth() const +{ + int depth = 0; + StatusTreeItem* anchestor = parent_item_; + while (anchestor) + { + ++depth; + anchestor = anchestor->parentItem(); + } + + return depth; +} + +int StatusTreeItem::row() const +{ + if (parent_item_) + { + return parent_item_->child_items_.indexOf(const_cast<StatusTreeItem*>(this)); + } + + return 0; +} + +backend::EntityId StatusTreeItem::id() +{ + return id_; +} + +backend::StatusKind StatusTreeItem::kind() +{ + return kind_; +} + +backend::StatusLevel StatusTreeItem::status_level() +{ + return status_level_; +} + +void StatusTreeItem::status_level( + backend::StatusLevel val) +{ + status_level_ = val; + status_level_variant_ = QVariant(QString::fromStdString(backend::status_level_to_string(status_level_))); +} + +std::string StatusTreeItem::name_str() +{ + return name_; +} + +std::string StatusTreeItem::value_str() +{ + return value_; +} + +std::string StatusTreeItem::description_str() +{ + return description_; +} + +int StatusTreeItem::recalculate_entity_counter() +{ + int count = 0; + // check if top level item / entity item + if (id_ != backend::ID_ALL && kind_ == backend::StatusKind::INVALID) + { + for (int i = 0; i < child_items_.count(); i++) + { + try + { + if (child_items_.value(i)->childCount() > 0) + { + for (int j = 0; j < child_items_.value(i)->childCount(); j++) + { + count += child_items_.value(i)->child(j)->value().toInt(); + } + } + count += child_items_.value(i)->value().toInt(); + } + catch (...) {} + } + value_ = std::to_string(count); + value_variant_ = QVariant(QString::fromStdString(value_)); + } + return count; +} + +} // namespace models diff --git a/src/model/tree/StatusTreeModel.cpp b/src/model/tree/StatusTreeModel.cpp new file mode 100644 index 00000000..535d7e8d --- /dev/null +++ b/src/model/tree/StatusTreeModel.cpp @@ -0,0 +1,528 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2021 Maurizio Ingrassia + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +// Copyright 2023 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// This file is part of eProsima Fast DDS Monitor. +// +// eProsima Fast DDS Monitor 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 3 of the License, or +// (at your option) any later version. +// +// eProsima Fast DDS Monitor 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 eProsima Fast DDS Monitor. If not, see <https://www.gnu.org/licenses/>. + +#include <fastdds_monitor/model/tree/StatusTreeModel.h> + +#include <iostream> +#include <QQmlEngine> + +#include <fastdds_monitor/backend/backend_utils.h> + +namespace models { + +StatusTreeModel::StatusTreeModel( + QObject* parent) + : QAbstractItemModel(parent) + , source_model_(nullptr) + , root_item_{ new StatusTreeItem() } + , is_empty_(false) + , current_filter_(backend::ID_ALL) +{ + QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership); +} + +StatusTreeModel::~StatusTreeModel() +{ + delete root_item_; +} + +void StatusTreeModel::set_source_model( + StatusTreeModel* source_model) +{ + source_model_ = source_model; + filter(current_filter_); +} + +void StatusTreeModel::filter_proxy( + const QVariant& entity_id) +{ + if (source_model_) + { + filter(entity_id.toInt()); + } +} + +void StatusTreeModel::filter( + const backend::EntityId entity_id) +{ + clear(); + if (current_filter_ != entity_id) + { + current_filter_ = entity_id; + } + if (source_model_) + { + for (int i = 0; i < source_model_->rootItem()->childCount(); i++) + { + addTopLevelItem(filtered_copy(source_model_->rootItem()->child(i), entity_id)); + } + } +} + +StatusTreeItem* StatusTreeModel::filtered_copy( + StatusTreeItem* source, + const backend::EntityId entity_id) +{ + // copy source data in destination data + if (source->id() == entity_id || entity_id == backend::ID_ALL) + { + StatusTreeItem* destination = new StatusTreeItem( + source->id(), + source->kind(), + source->name_str(), + source->status_level(), + source->value_str(), + source->description_str()); + for (int i = 0; i < source->childCount(); i++) + { + addItem(destination, filtered_copy(source->child(i), entity_id)); + } + return destination; + } + return nullptr; +} + + +int StatusTreeModel::rowCount( + const QModelIndex& parent) const +{ + if (!parent.isValid()) + { + return root_item_->childCount(); + } + + return internalPointer(parent)->childCount(); +} + +int StatusTreeModel::columnCount( + const QModelIndex& /*parent*/) const +{ + // This is basically flatten as a list model + return 1; +} + +QModelIndex StatusTreeModel::index( + const int row, + const int column, + const QModelIndex& parent) const +{ + if (!hasIndex(row, column, parent)) + { + return {}; + } + + StatusTreeItem* item = root_item_; + if (parent.isValid()) + { + item = internalPointer(parent); + } + + if (auto child = item->child(row)) + { + return createIndex(row, column, child); + } + + return {}; +} + +QModelIndex StatusTreeModel::parent( + const QModelIndex& index) const +{ + if (!index.isValid()) + { + return {}; + } + + StatusTreeItem* childItem = internalPointer(index); + StatusTreeItem* parentItem = childItem->parentItem(); + + if (!parentItem) + { + return {}; + } + + if (parentItem == root_item_) + { + return {}; + } + + return createIndex(parentItem->row(), 0, parentItem); +} + + +QVariant StatusTreeModel::data( + const QModelIndex& index, + const int role) const +{ + return name(index, role); +} + +QVariant StatusTreeModel::name( + const QModelIndex& index, + const int /*role*/) const +{ + if (!index.isValid()) + { + return QVariant(); + } + + return internalPointer(index)->name(); +} + + +QVariant StatusTreeModel::id( + const QModelIndex& index, + const int /*role*/) const +{ + if (!index.isValid()) + { + return QVariant(); + } + + return internalPointer(index)->entity_id(); +} + +QVariant StatusTreeModel::status( + const QModelIndex& index, + const int /*role*/) const +{ + if (!index.isValid()) + { + return QVariant(); + } + + return internalPointer(index)->status(); +} + +QVariant StatusTreeModel::kind( + const QModelIndex& index, + const int /*role*/) const +{ + if (!index.isValid()) + { + return QVariant(); + } + + return internalPointer(index)->status_kind(); +} + +QVariant StatusTreeModel::value( + const QModelIndex& index, + const int /*role*/) const +{ + if (!index.isValid()) + { + return QVariant(); + } + + return internalPointer(index)->value(); +} + +QVariant StatusTreeModel::description( + const QModelIndex& index, + const int /*role*/) const +{ + if (!index.isValid()) + { + return QVariant(); + } + + return internalPointer(index)->description(); +} + +QVariant StatusTreeModel::alive( + const QModelIndex& index, + const int /*role*/) const +{ + if (!index.isValid()) + { + return QVariant(); + } + + return internalPointer(index)->alive(); +} + +bool StatusTreeModel::setData( + const QModelIndex& index, + const QVariant& value, + int /*role*/) +{ + if (!index.isValid()) + { + return false; + } + + if (auto item = internalPointer(index)) + { + item->setData(value); + emit dataChanged(index, index, {Qt::EditRole}); + } + + return false; +} + +bool StatusTreeModel::removeRow( + int /*row*/, + const QModelIndex& index) +{ + if (!index.isValid()) + { + return false; + } + + if (auto item = internalPointer(index)) + { + std::cout << "removing" << std::endl; + removeItem(item); + std::cout << "removed" << std::endl; + } + + return false; +} + +void StatusTreeModel::addTopLevelItem( + StatusTreeItem* child) +{ + if (child) + { + addItem(root_item_, child); + if (child->id() == backend::ID_ALL &&root_item_->childCount() == 1) + { + is_empty_ = true; + } + } +} + +void StatusTreeModel::addItem( + StatusTreeItem* parent, + StatusTreeItem* child) +{ + if (!child || !parent) + { + return; + } + + + // if parent is topLevelItem (entity item) + if (parent->id() != backend::ID_ALL && parent->kind() == backend::StatusKind::INVALID) + { + // For each status in the entity item + for (int i=0; i<parent->childCount(); i++) + { + // if overriding status, remove previous status + if (parent->child(i)->id() == child->id() && parent->child(i)->kind() == child->kind()) + { + emit layoutAboutToBeChanged(); + beginRemoveRows(QModelIndex(), qMax(parent->childCount() - 1, 0), qMax(parent->childCount(), 0)); + parent->removeChild(parent->child(i)); + endRemoveRows(); + emit layoutChanged(); + } + } + } + + emit layoutAboutToBeChanged(); + + // remove possible parent from new child + if (child->parentItem()) + { + beginRemoveRows(QModelIndex(), qMax(parent->childCount() - 1, 0), qMax(parent->childCount(), 0)); + child->parentItem()->removeChild(child); + endRemoveRows(); + } + + beginInsertRows(QModelIndex(), qMax(parent->childCount() - 1, 0), qMax(parent->childCount() - 1, 0)); + // set new parent in the child + child->setParentItem(parent); + // append child in parent's child list + parent->appendChild(child); + endInsertRows(); + + emit layoutChanged(); +} + +void StatusTreeModel::removeItem( + StatusTreeItem* item) +{ + if (!item) + { + return; + } + + emit layoutAboutToBeChanged(); + + if (item->parentItem()) + { + beginRemoveRows(QModelIndex(), qMax(item->childCount() - 1, 0), qMax(item->childCount(), 0)); + item->parentItem()->removeChild(item); + endRemoveRows(); + } + + emit layoutChanged(); +} + +StatusTreeItem* StatusTreeModel::rootItem() const +{ + return root_item_; +} + +QModelIndex StatusTreeModel::rootIndex() +{ + return {}; +} + +int StatusTreeModel::depth( + const QModelIndex& index) const +{ + int count = 0; + auto anchestor = index; + if (!index.isValid()) + { + return 0; + } + while (anchestor.parent().isValid()) + { + anchestor = anchestor.parent(); + ++count; + } + + return count; +} + +void StatusTreeModel::clear() +{ + emit layoutAboutToBeChanged(); + beginResetModel(); + delete root_item_; + root_item_ = new StatusTreeItem(); + endResetModel(); + emit layoutChanged(); +} + +StatusTreeItem* StatusTreeModel::internalPointer( + const QModelIndex& index) const +{ + return static_cast<StatusTreeItem*>(index.internalPointer()); +} + +bool StatusTreeModel::containsTopLevelItem( + StatusTreeItem* child) +{ + if (child) + { + return contains(root_item_, child); + } + return false; +} + +bool StatusTreeModel::contains( + StatusTreeItem* parent, + StatusTreeItem* child) +{ + if (!parent || !child) + { + return false; + } + + for (int i = 0; i < parent->childCount(); i++) + { + if (parent->child(i)->id() == child->id()) + { + return true; + } + } + return false; +} + +StatusTreeItem* StatusTreeModel::child( + int row) +{ + if (row >= 0 && row < root_item_->childCount()) + { + return root_item_->child(row); + } + + return nullptr; +} + +bool StatusTreeModel::is_empty() +{ + return is_empty_; +} + +void StatusTreeModel::removeEmptyItem() +{ + emit layoutAboutToBeChanged(); + for (int i=0; i<root_item_->childCount(); i++) + { + if (root_item_->child(i)->id() == backend::ID_ALL) + { + root_item_->removeChild(root_item_->child(i)); + is_empty_ = false; + break; + } + } + emit layoutChanged(); +} + +StatusTreeItem* StatusTreeModel::getTopLevelItem( + const backend::EntityId& id, + const std::string& data, + const backend::StatusLevel& status, + const std::string& description) +{ + // For each entity item in the three (root) + for (int i=0; i<root_item_->childCount(); i++) + { + // if exists + if (root_item_->child(i)->id() == id) + { + return root_item_->child(i); + } + } + + // if not existing, create new topLevelItem + StatusTreeItem* new_entity_item = new StatusTreeItem(id, data, status, description); + addTopLevelItem(new_entity_item); + return new_entity_item; +} + +} // namespace models