diff --git a/rviz_common/CMakeLists.txt b/rviz_common/CMakeLists.txt index 17452a79a..3b91bf540 100644 --- a/rviz_common/CMakeLists.txt +++ b/rviz_common/CMakeLists.txt @@ -213,6 +213,7 @@ set(rviz_common_source_files src/rviz_common/ros_integration/ros_client_abstraction.cpp src/rviz_common/ros_integration/ros_node_abstraction.cpp src/rviz_common/ros_topic_display.cpp + src/rviz_common/ros_topic_utils.cpp src/rviz_common/scaled_image_widget.cpp src/rviz_common/screenshot_dialog.cpp src/rviz_common/selection_panel.cpp @@ -387,6 +388,7 @@ if(BUILD_TESTING) test/properties/qos_profile_property_test.cpp test/visualizer_app_test.cpp test/ros_node_abstraction_test.cpp + test/ros_topic_utils_test.cpp test/transformation/identity_frame_transformer_test.cpp ${TEST_SUPPORT_OBJECTS} ${SKIP_DISPLAY_TESTS} diff --git a/rviz_common/include/rviz_common/ros_topic_utils.hpp b/rviz_common/include/rviz_common/ros_topic_utils.hpp new file mode 100644 index 000000000..bac2e49fb --- /dev/null +++ b/rviz_common/include/rviz_common/ros_topic_utils.hpp @@ -0,0 +1,54 @@ +// Copyright (c) 2026, Open Source Robotics Foundation, Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// * Neither the name of the copyright holder nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +#ifndef RVIZ_COMMON__ROS_TOPIC_UTILS_HPP_ +#define RVIZ_COMMON__ROS_TOPIC_UTILS_HPP_ + +#include + +#include "rviz_common/visibility_control.hpp" + +namespace rviz_common +{ + +/// Return true if the given topic or service name is hidden. +/** + * Following the ROS 2 naming conventions, a name is hidden if any of its + * '/'-separated tokens starts with an underscore, e.g. the internal + * "/_buf_cpu" channels created by buffer-aware rmw implementations or + * the "/_action/feedback" topics used by actions. + * This mirrors rclpy's topic_or_service_is_hidden(), which is what + * "ros2 topic list" uses to hide such names by default. + */ +RVIZ_COMMON_PUBLIC +bool isTopicOrServiceHidden(const std::string & name); + +} // namespace rviz_common + +#endif // RVIZ_COMMON__ROS_TOPIC_UTILS_HPP_ diff --git a/rviz_common/src/rviz_common/add_display_dialog.cpp b/rviz_common/src/rviz_common/add_display_dialog.cpp index 3ad48c81c..8380cda23 100644 --- a/rviz_common/src/rviz_common/add_display_dialog.cpp +++ b/rviz_common/src/rviz_common/add_display_dialog.cpp @@ -34,6 +34,7 @@ #include #include // NOLINT(build/include_order) cpplint predates C++20 headers +#include #include #include #include @@ -60,6 +61,7 @@ #include "rviz_common/load_resource.hpp" #include "rviz_common/logging.hpp" #include "rviz_common/ros_integration/ros_node_abstraction.hpp" +#include "rviz_common/ros_topic_utils.hpp" namespace rviz_common { @@ -152,6 +154,10 @@ void getPluginGroups( std::map> topic_names_and_types = rviz_ros_node.lock()->get_topic_names_and_types(); + std::erase_if( + topic_names_and_types, + [](const auto & map_pair) {return isTopicOrServiceHidden(map_pair.first);}); + for (const auto & map_pair : topic_names_and_types) { QString topic = QString::fromStdString(map_pair.first); if (map_pair.second.empty()) { diff --git a/rviz_common/src/rviz_common/properties/ros_topic_multi_type_property.cpp b/rviz_common/src/rviz_common/properties/ros_topic_multi_type_property.cpp index fcae55a0c..957193251 100644 --- a/rviz_common/src/rviz_common/properties/ros_topic_multi_type_property.cpp +++ b/rviz_common/src/rviz_common/properties/ros_topic_multi_type_property.cpp @@ -35,6 +35,8 @@ #include #include +#include "rviz_common/ros_topic_utils.hpp" + namespace rviz_common { namespace properties @@ -49,6 +51,9 @@ void RosTopicMultiTypeProperty::fillTopicList() rviz_ros_node_.lock()->get_topic_names_and_types(); for (const auto & topic : published_topics) { + if (isTopicOrServiceHidden(topic.first)) { + continue; + } // Only add topics whose type matches one of the allowed types. for (const auto & type : topic.second) { if (message_types_.contains(QString::fromStdString(type))) { diff --git a/rviz_common/src/rviz_common/properties/ros_topic_property.cpp b/rviz_common/src/rviz_common/properties/ros_topic_property.cpp index d816e8947..a96f0aae7 100644 --- a/rviz_common/src/rviz_common/properties/ros_topic_property.cpp +++ b/rviz_common/src/rviz_common/properties/ros_topic_property.cpp @@ -37,6 +37,7 @@ #include "rviz_common/properties/ros_topic_property.hpp" #include "rviz_common/ros_integration/ros_node_abstraction_iface.hpp" +#include "rviz_common/ros_topic_utils.hpp" namespace rviz_common { @@ -80,6 +81,9 @@ void RosTopicProperty::fillTopicList() rviz_ros_node_.lock()->get_topic_names_and_types(); for (const auto & topic : published_topics) { + if (isTopicOrServiceHidden(topic.first)) { + continue; + } // Only add topics whose type matches. for (const auto & type : topic.second) { if (type == std_message_type) { diff --git a/rviz_common/src/rviz_common/ros_topic_utils.cpp b/rviz_common/src/rviz_common/ros_topic_utils.cpp new file mode 100644 index 000000000..6226e1d6f --- /dev/null +++ b/rviz_common/src/rviz_common/ros_topic_utils.cpp @@ -0,0 +1,53 @@ +// Copyright (c) 2026, Open Source Robotics Foundation, Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// * Neither the name of the copyright holder nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +#include "rviz_common/ros_topic_utils.hpp" + +#include + +namespace rviz_common +{ + +bool isTopicOrServiceHidden(const std::string & name) +{ + size_t start = 0; + while (start < name.size()) { + size_t end = name.find('/', start); + if (end == std::string::npos) { + end = name.size(); + } + if (end > start && name[start] == '_') { + return true; + } + start = end + 1; + } + return false; +} + +} // namespace rviz_common diff --git a/rviz_common/test/ros_topic_utils_test.cpp b/rviz_common/test/ros_topic_utils_test.cpp new file mode 100644 index 000000000..ed3e72b1b --- /dev/null +++ b/rviz_common/test/ros_topic_utils_test.cpp @@ -0,0 +1,56 @@ +// Copyright (c) 2026, Open Source Robotics Foundation, Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// * Neither the name of the copyright holder nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + + +#include + +#include "rviz_common/ros_topic_utils.hpp" + +using rviz_common::isTopicOrServiceHidden; + +TEST(IsTopicOrServiceHidden, regular_topics_are_not_hidden) { + EXPECT_FALSE(isTopicOrServiceHidden("/camera/image")); + EXPECT_FALSE(isTopicOrServiceHidden("/camera/image/compressed")); + EXPECT_FALSE(isTopicOrServiceHidden("relative/topic")); + EXPECT_FALSE(isTopicOrServiceHidden("")); + EXPECT_FALSE(isTopicOrServiceHidden("/")); +} + +TEST(IsTopicOrServiceHidden, underscore_inside_a_token_is_not_hidden) { + EXPECT_FALSE(isTopicOrServiceHidden("/foo_bar/baz")); + EXPECT_FALSE(isTopicOrServiceHidden("/foo/bar_baz")); +} + +TEST(IsTopicOrServiceHidden, token_starting_with_underscore_is_hidden) { + EXPECT_TRUE(isTopicOrServiceHidden("/camera/image/_buf_cpu")); + EXPECT_TRUE(isTopicOrServiceHidden("/_private/topic")); + EXPECT_TRUE(isTopicOrServiceHidden("/fibonacci/_action/feedback")); + EXPECT_TRUE(isTopicOrServiceHidden("relative/_hidden")); + EXPECT_TRUE(isTopicOrServiceHidden("_hidden")); +}