From 642ff0a242081dc0f068c902c6a6e834b36fe3cb Mon Sep 17 00:00:00 2001 From: Carlos Espinoza Curto <148376273+Carlosespicur@users.noreply.github.com> Date: Thu, 12 Sep 2024 15:10:53 +0200 Subject: [PATCH] Add IDL view (#230) * Add minor changes for Monitor compilation using refactored fastdds_statistics_backend Signed-off-by: Carlosespicur * Refs #21458: Add type IDL viewer Signed-off-by: Carlosespicur * Refs #21458: Add text selection and scrollbar in IDL viewer Signed-off-by: Carlosespicur * Refs #21458: Change margin and tab names in IDL viewer Signed-off-by: Carlosespicur * Refs #21458: Add hover event to display tab full name Signed-off-by: JesusPoderoso * Refs #21458: Add resources to project Signed-off-by: JesusPoderoso * Refs #21458: Add icons in the tabs Signed-off-by: JesusPoderoso * Refs #21611: Add Fast DDS Visualizer resources to project Signed-off-by: JesusPoderoso * Refs #21611: Increase FASTDDS_VISUALIZER icon size Signed-off-by: JesusPoderoso * Refs #21458: Uncrustify Signed-off-by: Carlosespicur * Refs #21459: Fix documentation CI to run only in 'Release' mode Signed-off-by: JesusPoderoso * Refs #21459: Add review comments Signed-off-by: Carlosespicur --------- Signed-off-by: Carlosespicur Signed-off-by: JesusPoderoso Co-authored-by: JesusPoderoso --- .github/workflows/reusable-workflow.yml | 11 +- docs/rst/ros/galactic/galactic.rst | 6 +- include/fastdds_monitor/Controller.h | 6 + include/fastdds_monitor/Engine.h | 8 + .../backend/SyncBackendConnection.h | 8 + qml.qrc | 10 ++ qml/DomainGraphLayout.qml | 15 +- qml/LeftPanel.qml | 5 + qml/Panels.qml | 1 + qml/TabLayout.qml | 158 +++++++++++++++++- qml/TopicMenu.qml | 4 + .../images/app_icons/FASTDDS_VISUALIZER.svg | 1 + .../images/app_logos/FASTDDS_VISUALIZER.png | Bin 0 -> 25945 bytes .../icons/domain_graph/domain_graph_black.svg | 1 + .../domain_graph_eProsimaLightBlue.svg | 1 + .../icons/domain_graph/domain_graph_grey.svg | 1 + .../icons/domain_graph/domain_graph_white.svg | 1 + resources/images/icons/idl/idl_black.svg | 1 + .../icons/idl/idl_eProsimaLightBlue.svg | 1 + resources/images/icons/idl/idl_grey.svg | 1 + resources/images/icons/idl/idl_white.svg | 1 + src/Controller.cpp | 12 ++ src/Engine.cpp | 15 +- src/backend/SyncBackendConnection.cpp | 20 +++ 24 files changed, 262 insertions(+), 26 deletions(-) create mode 100644 resources/images/app_icons/FASTDDS_VISUALIZER.svg create mode 100644 resources/images/app_logos/FASTDDS_VISUALIZER.png create mode 100644 resources/images/icons/domain_graph/domain_graph_black.svg create mode 100644 resources/images/icons/domain_graph/domain_graph_eProsimaLightBlue.svg create mode 100644 resources/images/icons/domain_graph/domain_graph_grey.svg create mode 100644 resources/images/icons/domain_graph/domain_graph_white.svg create mode 100644 resources/images/icons/idl/idl_black.svg create mode 100644 resources/images/icons/idl/idl_eProsimaLightBlue.svg create mode 100644 resources/images/icons/idl/idl_grey.svg create mode 100644 resources/images/icons/idl/idl_white.svg diff --git a/.github/workflows/reusable-workflow.yml b/.github/workflows/reusable-workflow.yml index ed364f3e..e6d95e84 100644 --- a/.github/workflows/reusable-workflow.yml +++ b/.github/workflows/reusable-workflow.yml @@ -134,13 +134,6 @@ jobs: documentation: runs-on: ubuntu-24.04 - strategy: - fail-fast: false - matrix: - cmake_build_type: - - Release - - Debug - steps: - name: Sync repository uses: eProsima/eProsima-CI/external/checkout@v0 @@ -151,7 +144,7 @@ jobs: - name: Install Fast DDS dependencies uses: eProsima/eProsima-CI/multiplatform/install_fastdds_dependencies@v0 with: - cmake_build_type: ${{ matrix.cmake_build_type }} + cmake_build_type: 'Release' - name: Fetch Fast DDS Monitor repositories uses: eProsima/eProsima-CI/multiplatform/vcs_import@v0 @@ -181,7 +174,7 @@ jobs: uses: eProsima/eProsima-CI/multiplatform/colcon_build_test@v0 with: packages_names: fastdds_monitor - cmake_build_type: ${{ matrix.cmake_build_type }} + cmake_build_type: 'Release' cmake_args: '-DTHIRDPARTY=ON -DBUILD_DOCUMENTATION=ON -DBUILD_DOCUMENTATION_TESTS=ON' colcon_meta_file: ${{ github.workspace }}/src/fastdds_monitor/.github/workflows/configurations/Linux/colcon.meta workspace: ${{ github.workspace }} diff --git a/docs/rst/ros/galactic/galactic.rst b/docs/rst/ros/galactic/galactic.rst index d23cef8a..a20f7e48 100644 --- a/docs/rst/ros/galactic/galactic.rst +++ b/docs/rst/ros/galactic/galactic.rst @@ -20,7 +20,7 @@ Installation from sources Follow the `ROS 2 galactic installation from sources documentation `_ Fast DDS is downloaded within the rest of the packages. -The only consideration here is to compile :code:`fastrtps` library with the Statistics Module activated. +The only consideration here is to compile :code:`fastdds` library with the Statistics Module activated. When compiling with colcon, the following arguments must be provided: .. code-block:: bash @@ -58,7 +58,7 @@ To execute each of the nodes, run the following commands in different terminals: .. code-block:: bash - export RMW_IMPLEMENTATION=rmw_fastrtps_cpp + export RMW_IMPLEMENTATION=rmw_fastdds_cpp export FASTDDS_STATISTICS="HISTORY_LATENCY_TOPIC;NETWORK_LATENCY_TOPIC;\ PUBLICATION_THROUGHPUT_TOPIC;SUBSCRIPTION_THROUGHPUT_TOPIC;RTPS_SENT_TOPIC;\ @@ -71,7 +71,7 @@ To execute each of the nodes, run the following commands in different terminals: .. code-block:: bash - export RMW_IMPLEMENTATION=rmw_fastrtps_cpp + export RMW_IMPLEMENTATION=rmw_fastdds_cpp export FASTDDS_STATISTICS="HISTORY_LATENCY_TOPIC;NETWORK_LATENCY_TOPIC;\ PUBLICATION_THROUGHPUT_TOPIC;SUBSCRIPTION_THROUGHPUT_TOPIC;RTPS_SENT_TOPIC;\ diff --git a/include/fastdds_monitor/Controller.h b/include/fastdds_monitor/Controller.h index e2cb20eb..c43f9ee4 100644 --- a/include/fastdds_monitor/Controller.h +++ b/include/fastdds_monitor/Controller.h @@ -276,6 +276,12 @@ public slots: QString get_domain_view_graph ( QString domain_id); + QString get_type_idl ( + QString entity_id); + + QString get_data_type_name( + QString entity_id); + signals: //! Signal to show the Error Dialog diff --git a/include/fastdds_monitor/Engine.h b/include/fastdds_monitor/Engine.h index 862394bb..0185f0fe 100644 --- a/include/fastdds_monitor/Engine.h +++ b/include/fastdds_monitor/Engine.h @@ -526,6 +526,14 @@ class Engine : public QQmlApplicationEngine //! Retrive a string list containing the available data kinds. std::vector get_data_kinds(); + //! Retrieve the data type name associated to a specific entity + std::string get_data_type_name( + const backend::EntityId& entity_id); + + //! Retrieve the IDL representation associated to a specific data type + std::string get_type_idl( + const backend::EntityId& entity_id); + //! Returns whether the data kind entered requires a target entity to be defined. bool data_kind_has_target( const QString& data_kind); diff --git a/include/fastdds_monitor/backend/SyncBackendConnection.h b/include/fastdds_monitor/backend/SyncBackendConnection.h index 023ad6ac..bf7bf2bd 100644 --- a/include/fastdds_monitor/backend/SyncBackendConnection.h +++ b/include/fastdds_monitor/backend/SyncBackendConnection.h @@ -120,6 +120,10 @@ class SyncBackendConnection std::string get_alias( backend::EntityId id); + //! Get the data type name of an entity from the Backend by calling \c get_info + std::string get_data_type_name( + backend::EntityId id); + //! Get the status level of an entity from the Backend by calling \c get_status StatusLevel get_status( backend::EntityId id); @@ -305,6 +309,10 @@ class SyncBackendConnection Graph get_domain_view_graph ( const EntityId& domain_id); + //! Retrieve the IDL representation of a topic data type in string format + std::string get_type_idl ( + const EntityId& entity_id); + protected: void change_unit_magnitude( diff --git a/qml.qrc b/qml.qrc index 8091aea8..d34f60a4 100644 --- a/qml.qrc +++ b/qml.qrc @@ -108,6 +108,10 @@ resources/images/icons/domain/domain_eProsimaLightBlue.svg resources/images/icons/domain/domain_grey.svg resources/images/icons/domain/domain_white.svg + resources/images/icons/domain_graph/domain_graph_black.svg + resources/images/icons/domain_graph/domain_graph_eProsimaLightBlue.svg + resources/images/icons/domain_graph/domain_graph_grey.svg + resources/images/icons/domain_graph/domain_graph_white.svg resources/images/icons/dynamicchart/dynamicchart_black.svg resources/images/icons/dynamicchart/dynamicchart_eProsimaLightBlue.svg resources/images/icons/dynamicchart/dynamicchart_grey.svg @@ -157,6 +161,10 @@ resources/images/icons/host/host_eProsimaLightBlue.svg resources/images/icons/host/host_grey.svg resources/images/icons/host/host_white.svg + resources/images/icons/idl/idl_black.svg + resources/images/icons/idl/idl_eProsimaLightBlue.svg + resources/images/icons/idl/idl_grey.svg + resources/images/icons/idl/idl_white.svg resources/images/icons/info/info_black.svg resources/images/icons/info/info_eProsimaDarkBlue.svg resources/images/icons/info/info_eProsimaLightBlue.svg @@ -250,6 +258,7 @@ resources/images/app_icons/FASTDDS.svg resources/images/app_icons/FASTDDS_MONITOR.svg resources/images/app_icons/FASTDDS_SPY.svg + resources/images/app_icons/FASTDDS_VISUALIZER.svg resources/images/app_icons/SAFEDDS.svg resources/images/app_icons/SHAPES_DEMO.svg resources/images/app_icons/SUSTAINML.svg @@ -262,6 +271,7 @@ resources/images/app_logos/FASTDDS.png resources/images/app_logos/FASTDDS_MONITOR.png resources/images/app_logos/FASTDDS_SPY.png + resources/images/app_logos/FASTDDS_VISUALIZER.png resources/images/app_logos/SAFEDDS.png resources/images/app_logos/SHAPES_DEMO.png resources/images/app_logos/SUSTAINML.png diff --git a/qml/DomainGraphLayout.qml b/qml/DomainGraphLayout.qml index 6cc819c0..3f5a0705 100644 --- a/qml/DomainGraphLayout.qml +++ b/qml/DomainGraphLayout.qml @@ -32,7 +32,7 @@ Item required property string component_id // mandatory to be included when object created // Public signals - signal update_tab_name(string new_name, string stack_id) // Update tab name based on selected domain id + signal update_tab_name(string new_name, string new_icon, 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) signal openLoadingGraphDialog() //l et tab layout know that graph is about to be generated @@ -1049,13 +1049,14 @@ Item visible: parent.visible anchors.horizontalCenter: parent.horizontalCenter source: modelData["app_id"] == "UNKNOWN_APP" ? "" : - "/resources/images/app_icons/" + modelData["app_id"] + ".svg" + "/resources/images/app_icons/" + modelData["app_id"] + ".svg" readonly property int amlip_offset_: 5 // AML_IP is wider than it is tall, so its size is increased a little // bit to make it look like the same aspect ratio as the other icons - sourceSize.width: modelData["app_id"] == "AML_IP" + // It also happens with FASTDDS_VISUALIZER + sourceSize.width: modelData["app_id"] == "AML_IP" || modelData["app_id"] == "FASTDDS_VISUALIZER" ? parent.height + amlip_offset_ : parent.height - sourceSize.height: modelData["app_id"] == "AML_IP" + sourceSize.height: modelData["app_id"] == "AML_IP" || modelData["app_id"] == "FASTDDS_VISUALIZER" ? parent.height + amlip_offset_ : parent.height } } @@ -1655,7 +1656,7 @@ Item { if (filtered_topics_.length == 1) { - domainGraphLayout.update_tab_name(topic_names[0] + " Topic View", component_id) + domainGraphLayout.update_tab_name(topic_names[0] + " Topic View", "topic", component_id) } else { @@ -1669,12 +1670,12 @@ Item print_topic_names += " and " + topic_names[topic_names.length-1] } - domainGraphLayout.update_tab_name(print_topic_names + " Topics View", component_id) + domainGraphLayout.update_tab_name(print_topic_names + " Topics View", "topic", component_id) } } else { - domainGraphLayout.update_tab_name("Domain " + domain_id + " View", component_id) + domainGraphLayout.update_tab_name("Domain " + domain_id + " View", "domain_graph", component_id) } } diff --git a/qml/LeftPanel.qml b/qml/LeftPanel.qml index 35a7435c..61da7c0c 100644 --- a/qml/LeftPanel.qml +++ b/qml/LeftPanel.qml @@ -41,6 +41,7 @@ RowLayout { signal explorerLogicalChanged(bool status) signal explorerEntityInfoChanged(bool status) signal open_topic_view(string domainEntityId, string domainId, string entityId) + signal open_idl_view(string entityId) signal refresh_domain_graph_view(string domainEntityId, string entityId) signal filter_entity_status_log(string entityId) @@ -107,6 +108,10 @@ RowLayout { leftPanel.open_topic_view(domainEntityId, domainId, entityId) } + function openIDLView(entityId) { + leftPanel.open_idl_view(entityId) + } + function refreshDomainGraphView(domainEntityId, entityId) { leftPanel.refresh_domain_graph_view(domainEntityId, entityId) } diff --git a/qml/Panels.qml b/qml/Panels.qml index 5e80df17..0dd5141d 100644 --- a/qml/Panels.qml +++ b/qml/Panels.qml @@ -100,6 +100,7 @@ RowLayout { 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) + onOpen_idl_view: tabs.open_idl_view(entityId) } Rectangle { diff --git a/qml/TabLayout.qml b/qml/TabLayout.qml index 905d4c8a..002cb058 100644 --- a/qml/TabLayout.qml +++ b/qml/TabLayout.qml @@ -34,15 +34,16 @@ Item { // Private properties property int current_: 0 // current tab displayed property int last_index_: 1 // force unique idx on QML components - property var tab_model_: [{"idx":0, "title":"New Tab", "stack_id": 0}] // tab model for tab bad and tab management + property var tab_model_: [{"idx":0, "title":"New Tab", "icon":"", "stack_id": 0}] // tab model for tab bad and tab management property bool disable_chart_selection_: false // flag to disable multiple chart view tabs readonly property var allowed_stack_components_: // list of allowed component names to be - ["view_selector", "chartsLayout", "domainGraphLayout_component"]// loaded in the tabs stack view + ["view_selector", "chartsLayout", "domainGraphLayout_component", "idlView_component"] // loaded in the tabs stack view // 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) + signal display_idl_content_(int stack_id, string content) // Read only design properties readonly property int max_tabs_: 15 @@ -51,10 +52,17 @@ Item { readonly property int tabs_height_: 36 readonly property int tabs_margins_: 15 readonly property int tab_icons_size_: 16 + readonly property int tab_icons_margins_: 8 readonly property int add_tab_width_: 50 readonly property int timer_ms_interval_: 500 readonly property int dialog_width_: 300 readonly property int dialog_height_: 152 + readonly property int idl_text_margin_: 30 + readonly property int hover_text_offset_: 50 + readonly property int elements_spacing_: 5 + readonly property int scrollbar_min_size_: 8 + readonly property int scrollbar_max_size_: 12 + readonly property real scroll_speed_: 0.05 readonly property string selected_tab_color_: "#ffffff" readonly property string selected_shadow_tab_color_: "#c0c0c0" readonly property string not_selected_tab_color_: "#f0f0f0" @@ -91,7 +99,8 @@ Item { property int stack_id: 0 property string customInitialItem: "view_selector" initialItem: customInitialItem == "chartsLayout" ? chartsLayout : - customInitialItem == "domainGraphLayout_component" ? domainGraphLayout_component : view_selector + customInitialItem == "domainGraphLayout_component" ? domainGraphLayout_component : + customInitialItem == "idlView_component" ? idlView_component : view_selector // override push transition to none pushEnter: Transition {} @@ -145,6 +154,7 @@ Item { if (!disable_chart_selection_) { tabLayout.tab_model_[current_]["title"] = "Chart View" + tabLayout.tab_model_[current_]["icon"] = "dynamicchart" if (stack.deep > 1) { stack.pop() @@ -222,6 +232,7 @@ Item { if (tabLayout.tab_model_[i]["stack_id"] == stack_id) { tabLayout.tab_model_[i]["title"] = new_name + tabLayout.tab_model_[i]["icon"] = new_icon // update model to set the visual change tab_list.model = tabLayout.tab_model_ @@ -279,6 +290,99 @@ Item { } } + Component { + id: idlView_component + Flickable + { + id: idlView + clip: true + boundsBehavior: Flickable.StopAtBounds + interactive: false + anchors.fill: parent + width: parent.width + height: parent.height + contentWidth: parent.width + contentHeight: idl_text.height + 2 * tabLayout.idl_text_margin_ + ScrollBar.vertical: ScrollBar { + id: vertical_bar + policy: ScrollBar.AlwaysOn + visible: idlView.contentHeight > idlView.height + stepSize: tabLayout.scroll_speed_ + anchors.top: parent.top; anchors.topMargin: 0 + anchors.right: parent.right; + hoverEnabled: true + + contentItem: Item { + implicitWidth: scrollbar_min_size_ + Rectangle { + anchors.fill: parent + anchors.topMargin: 1 + anchors.rightMargin: 2 + anchors.leftMargin: 2 + radius: width / 2 + color: vertical_bar.pressed ? Theme.eProsimaLightBlue : Theme.lightGrey + } + } + + background: Item { + implicitWidth: scrollbar_max_size_ + Rectangle { + anchors.fill: parent + color: vertical_bar.pressed ? Theme.lightGrey : Theme.grey + } + } + + Rectangle { + anchors.top: parent.top + height: 1 + width: parent.width + color: vertical_bar.pressed ? Theme.lightGrey : Theme.grey + } + } + + MouseArea { + anchors.fill: parent + onWheel: { + if(wheel.angleDelta.y > 0){ + vertical_bar.decrease() + }else{ + vertical_bar.increase() + } + } + } + + TextEdit + { + id: idl_text + text: "" + anchors.left: parent.left + anchors.top: parent.top + anchors.leftMargin: tabLayout.idl_text_margin_ + anchors.rightMargin: tabLayout.idl_text_margin_ + anchors.topMargin: tabLayout.idl_text_margin_ + width: parent.width - 2 * tabLayout.idl_text_margin_ + wrapMode: TextEdit.WrapAnywhere + + // Enable text selection in the view + readOnly: true + selectByMouse: true + selectByKeyboard: true + Connections + { + target: tabLayout + + function onDisplay_idl_content_(stack_id, content) + { + if (stack.stack_id == stack_id) + { + idl_text.text = content + } + } + } + } + } + } + Connections { target: tabLayout @@ -333,10 +437,20 @@ Item { GradientStop { position: 0.96; color: delegated_rect.color } GradientStop { position: 1.0; color: current_ == modelData["idx"] + 1 ? shadow_color : delegated_rect.color} } + // tab icon + IconSVG { + id: tab_icon + visible: modelData["title"] != "New Tab" && modelData["icon"] != "" + anchors.left: parent.left + anchors.leftMargin: tab_icons_margins_ + anchors.verticalCenter: parent.verticalCenter + name: modelData["icon"] + size: tab_icons_size_ + } Text { horizontalAlignment: Qt.AlignLeft; verticalAlignment: Qt.AlignVCenter - anchors.left: parent.left - anchors.leftMargin: tabs_margins_ + anchors.left: tab_icon.right + anchors.leftMargin: tab_icons_margins_ anchors.right: close_icon.visible ? close_icon.left : parent.right anchors.rightMargin: tabs_margins_ anchors.verticalCenter: parent.verticalCenter @@ -353,8 +467,20 @@ Item { name: "cross" size: tab_icons_size_ } + Label { + id: hover_label + visible: false + anchors.top: delegated_rect.bottom + anchors.topMargin: tabLayout.hover_text_offset_ + anchors.horizontalCenter: delegated_rect.horizontalCenter + ToolTip.text: modelData["title"] + ToolTip.visible: mouse_area.containsMouse + } + // tab selection action MouseArea { + id: mouse_area + hoverEnabled: true anchors.top: parent.top; anchors.bottom: parent.bottom; anchors.left: parent.left; anchors.right: close_icon.left; anchors.rightMargin: - tabs_margins_ onClicked: { @@ -403,9 +529,19 @@ Item { name: "plus" size: tab_icons_size_ } + Label { + visible: false + anchors.top: add_new_tab_button.bottom + anchors.topMargin: tabLayout.hover_text_offset_ + anchors.horizontalCenter: add_new_tab_button.horizontalCenter + ToolTip.text: "Add new tab" + ToolTip.visible: add_tab_mouse_area.containsMouse + } // add new tab action MouseArea { + id: add_tab_mouse_area anchors.fill: parent + hoverEnabled: true onClicked: { if (tabLayout.tab_model_.length < max_tabs_) tabLayout.create_new_tab() @@ -562,7 +698,7 @@ Item { initial_component = "view_selector"; } var idx = tabLayout.tab_model_.length - tabLayout.tab_model_[idx] = {"idx" : idx, "title": "New Tab", "stack_id":last_index_} + tabLayout.tab_model_[idx] = {"idx" : idx, "title": "New Tab", "icon":"", "stack_id":last_index_} var new_stack = stack_component.createObject(null, { "stack_id": tabLayout.tab_model_[idx]["stack_id"], "customInitialItem": initial_component }) last_index_++ @@ -619,6 +755,7 @@ Item { if (swap) { tabLayout.tab_model_[idx_prev]["title"] = tabLayout.tab_model_[i]["title"] + tabLayout.tab_model_[idx_prev]["icon"] = tabLayout.tab_model_[i]["icon"] tabLayout.tab_model_[idx_prev]["stack_id"] = tabLayout.tab_model_[i]["stack_id"] } // reorder model idx usage, and delete idx tab components (stack layout content) @@ -713,6 +850,15 @@ Item { filter_domain_view_by_topic_(tabLayout.tab_model_[current_]["stack_id"], domainEntityId, entityId) } + function open_idl_view(entityId) { + create_new_custom_tab_("idlView_component") + tabLayout.tab_model_[current_]["title"] = controller.get_data_type_name(entityId) + tabLayout.tab_model_[current_]["icon"] = "idl" + var content = controller.get_type_idl(entityId) + display_idl_content_(tabLayout.tab_model_[current_]["stack_id"], content) + refresh_layout(current_) + } + function refresh_domain_graph_view(domainEntityId, entityId) { for (var i=0; i \ No newline at end of file diff --git a/resources/images/app_logos/FASTDDS_VISUALIZER.png b/resources/images/app_logos/FASTDDS_VISUALIZER.png new file mode 100644 index 0000000000000000000000000000000000000000..05f53b4a0eef9310c392a10597c21707b46f6ad4 GIT binary patch literal 25945 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 literal 0 HcmV?d00001 diff --git a/resources/images/icons/domain_graph/domain_graph_black.svg b/resources/images/icons/domain_graph/domain_graph_black.svg new file mode 100644 index 00000000..72dc7fb5 --- /dev/null +++ b/resources/images/icons/domain_graph/domain_graph_black.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/resources/images/icons/domain_graph/domain_graph_eProsimaLightBlue.svg b/resources/images/icons/domain_graph/domain_graph_eProsimaLightBlue.svg new file mode 100644 index 00000000..a0f97583 --- /dev/null +++ b/resources/images/icons/domain_graph/domain_graph_eProsimaLightBlue.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/resources/images/icons/domain_graph/domain_graph_grey.svg b/resources/images/icons/domain_graph/domain_graph_grey.svg new file mode 100644 index 00000000..0d99d4b0 --- /dev/null +++ b/resources/images/icons/domain_graph/domain_graph_grey.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/resources/images/icons/domain_graph/domain_graph_white.svg b/resources/images/icons/domain_graph/domain_graph_white.svg new file mode 100644 index 00000000..6f168143 --- /dev/null +++ b/resources/images/icons/domain_graph/domain_graph_white.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/resources/images/icons/idl/idl_black.svg b/resources/images/icons/idl/idl_black.svg new file mode 100644 index 00000000..d6482309 --- /dev/null +++ b/resources/images/icons/idl/idl_black.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/resources/images/icons/idl/idl_eProsimaLightBlue.svg b/resources/images/icons/idl/idl_eProsimaLightBlue.svg new file mode 100644 index 00000000..f61b77d5 --- /dev/null +++ b/resources/images/icons/idl/idl_eProsimaLightBlue.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/resources/images/icons/idl/idl_grey.svg b/resources/images/icons/idl/idl_grey.svg new file mode 100644 index 00000000..9e6303c6 --- /dev/null +++ b/resources/images/icons/idl/idl_grey.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/resources/images/icons/idl/idl_white.svg b/resources/images/icons/idl/idl_white.svg new file mode 100644 index 00000000..b5cf6ed5 --- /dev/null +++ b/resources/images/icons/idl/idl_white.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/Controller.cpp b/src/Controller.cpp index 3f508b73..276e4e64 100644 --- a/src/Controller.cpp +++ b/src/Controller.cpp @@ -322,3 +322,15 @@ QString Controller::get_domain_view_graph( backend::Graph domain_view = engine_->get_domain_view_graph(backend::models_id_to_backend_id(entity_id)); return QString::fromUtf8(domain_view.dump().data(), int(domain_view.dump().size())); } + +QString Controller::get_type_idl( + QString entity_id) +{ + return utils::to_QString(engine_->get_type_idl(backend::models_id_to_backend_id(entity_id))); +} + +QString Controller::get_data_type_name( + QString entity_id) +{ + return utils::to_QString(engine_->get_data_type_name(backend::models_id_to_backend_id(entity_id))); +} diff --git a/src/Engine.cpp b/src/Engine.cpp index 0c959c18..27c2170d 100644 --- a/src/Engine.cpp +++ b/src/Engine.cpp @@ -280,7 +280,8 @@ void Engine::init_monitor( else { process_error( - "Error trying to initialize monitor in Discovery Server with locators: " + utils::to_string(discovery_server_locators), + "Error trying to initialize monitor in Discovery Server with locators: " + + utils::to_string(discovery_server_locators), ErrorType::INIT_DS_MONITOR); } } @@ -1708,6 +1709,18 @@ std::vector Engine::get_data_kinds() return backend_connection_.get_data_kinds(); } +std::string Engine::get_data_type_name( + const backend::EntityId& entity_id) +{ + return backend_connection_.get_data_type_name(entity_id); +} + +std::string Engine::get_type_idl( + const backend::EntityId& entity_id) +{ + return backend_connection_.get_type_idl(entity_id); +} + bool Engine::data_kind_has_target( const QString& data_kind) { diff --git a/src/backend/SyncBackendConnection.cpp b/src/backend/SyncBackendConnection.cpp index c3db8378..d7ec5a76 100644 --- a/src/backend/SyncBackendConnection.cpp +++ b/src/backend/SyncBackendConnection.cpp @@ -625,6 +625,12 @@ std::string SyncBackendConnection::get_alias( return backend::get_info_value(get_info(id), "alias"); } +std::string SyncBackendConnection::get_data_type_name( + backend::EntityId id) +{ + return backend::get_info_value(get_info(id), "data_type"); +} + StatusLevel SyncBackendConnection:: get_status( EntityId id) { @@ -953,6 +959,20 @@ Graph SyncBackendConnection::get_domain_view_graph ( } } +std::string SyncBackendConnection::get_type_idl( + const EntityId& id) +{ + try + { + return StatisticsBackend::get_type_idl(id); + } + catch (const std::exception& e) + { + qWarning() << "Fail getting the IDL type for entity id " << id.value() << ": " << e.what(); + return ""; + } +} + void SyncBackendConnection::change_unit_magnitude( std::vector& data, DataKind data_kind)