Skip to content

Commit 9c24f1d

Browse files
committed
Fix topic statistics source collision for subscriptions
1 parent ae29aeb commit 9c24f1d

3 files changed

Lines changed: 91 additions & 6 deletions

File tree

rclcpp/include/rclcpp/create_subscription.hpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,10 @@ create_subscription(
9292
qos);
9393

9494
subscription_topic_stats = std::make_shared<
95-
rclcpp::topic_statistics::SubscriptionTopicStatistics<ROSMessageType>
96-
>(node_topics_interface->get_node_base_interface()->get_name(), publisher);
95+
rclcpp::topic_statistics::SubscriptionTopicStatistics<ROSMessageType>>(
96+
node_topics_interface->get_node_base_interface()->get_name(),
97+
topic_name,
98+
publisher);
9799

98100
std::weak_ptr<
99101
rclcpp::topic_statistics::SubscriptionTopicStatistics<ROSMessageType>

rclcpp/include/rclcpp/topic_statistics/subscription_topic_statistics.hpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,16 +71,18 @@ class SubscriptionTopicStatistics
7171
* measure, and publish topic statistics data. This throws an invalid_argument
7272
* if the input publisher is null.
7373
*
74-
* \param node_name the name of the node, which created this instance, in order to denote
75-
* topic source
74+
* \param node_name the name of the node which created this instance
75+
* \param topic_name the name of the subscribed topic, used to uniquely
76+
* identify statistics sources for multiple subscriptions in the same node
7677
* \param publisher instance constructed by the node in order to publish statistics data.
7778
* This class owns the publisher.
7879
* \throws std::invalid_argument if publisher pointer is nullptr
7980
*/
8081
SubscriptionTopicStatistics(
8182
const std::string & node_name,
83+
const std::string & topic_name,
8284
rclcpp::Publisher<statistics_msgs::msg::MetricsMessage>::SharedPtr publisher)
83-
: node_name_(node_name),
85+
: node_name_(node_name + topic_name),
8486
publisher_(std::move(publisher))
8587
{
8688
// TODO(dbbonnie): ros-tooling/aws-roadmap/issues/226, received message age

rclcpp/test/rclcpp/topic_statistics/test_subscription_topic_statistics.cpp

Lines changed: 82 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,10 @@ class TestSubscriptionTopicStatistics : public SubscriptionTopicStatistics<Callb
8585
public:
8686
TestSubscriptionTopicStatistics(
8787
const std::string & node_name,
88+
const std::string & topic_name,
8889
rclcpp::Publisher<statistics_msgs::msg::MetricsMessage>::SharedPtr publisher)
89-
: SubscriptionTopicStatistics<CallbackMessageT>(node_name, publisher)
90+
: SubscriptionTopicStatistics<CallbackMessageT>(
91+
node_name, topic_name, publisher)
9092
{
9193
}
9294

@@ -391,6 +393,7 @@ TEST_F(TestSubscriptionTopicStatisticsFixture, test_manual_construction)
391393
// Construct a separate instance
392394
auto sub_topic_stats = std::make_unique<TestSubscriptionTopicStatistics<Empty>>(
393395
empty_subscriber->get_name(),
396+
"/test_topic",
394397
topic_stats_publisher);
395398

396399
// Expect no data has been collected / no samples received
@@ -586,3 +589,81 @@ TEST_F(TestSubscriptionTopicStatisticsFixture, test_receive_stats_include_window
586589
}
587590
}
588591
}
592+
/**
593+
* Test that multiple subscriptions on the same node produce differentiated
594+
* measurement_source_name values in their statistics messages.
595+
* Regression test for #2756.
596+
*/
597+
TEST_F(TestSubscriptionTopicStatisticsFixture, test_multiple_subscriptions_differentiated)
598+
{
599+
constexpr const char kTopicA[]{"/test_topic_a"};
600+
constexpr const char kTopicB[]{"/test_topic_b"};
601+
constexpr const uint64_t kNumStatsMessages{4};
602+
603+
auto pub_a = std::make_shared<EmptyPublisher>("pub_node_a", kTopicA);
604+
auto pub_b = std::make_shared<EmptyPublisher>("pub_node_b", kTopicB);
605+
606+
auto multi_sub_node = std::make_shared<rclcpp::Node>("multi_sub_node");
607+
608+
auto options = rclcpp::SubscriptionOptions();
609+
options.topic_stats_options.state = rclcpp::TopicStatisticsState::Enable;
610+
options.topic_stats_options.publish_period = defaultStatisticsPublishPeriod;
611+
612+
auto cb = [](Empty::UniquePtr msg) {(void) msg;};
613+
614+
auto sub_a = multi_sub_node->create_subscription<Empty>(
615+
kTopicA,
616+
rclcpp::QoS(rclcpp::KeepAll()),
617+
std::function<void(Empty::UniquePtr)>(cb),
618+
options);
619+
620+
auto sub_b = multi_sub_node->create_subscription<Empty>(
621+
kTopicB,
622+
rclcpp::QoS(rclcpp::KeepAll()),
623+
std::function<void(Empty::UniquePtr)>(cb),
624+
options);
625+
626+
auto stats_listener =
627+
std::make_shared<rclcpp::topic_statistics::MetricsMessageSubscriber>(
628+
"multi_sub_stats_listener",
629+
"/statistics",
630+
kNumStatsMessages);
631+
632+
rclcpp::executors::SingleThreadedExecutor ex;
633+
ex.add_node(pub_a);
634+
ex.add_node(pub_b);
635+
ex.add_node(multi_sub_node);
636+
ex.add_node(stats_listener);
637+
638+
ex.spin_until_future_complete(stats_listener->GetFuture(), kTestTimeout);
639+
640+
const auto received_messages = stats_listener->GetReceivedMessages();
641+
ASSERT_GE(received_messages.size(), kNumStatsMessages);
642+
643+
std::set<std::string> source_names;
644+
for (const auto & msg : received_messages) {
645+
source_names.insert(msg.measurement_source_name);
646+
}
647+
648+
bool found_topic_a = false;
649+
bool found_topic_b = false;
650+
651+
for (const auto & name : source_names) {
652+
if (name.find("test_topic_a") != std::string::npos) {
653+
found_topic_a = true;
654+
}
655+
656+
if (name.find("test_topic_b") != std::string::npos) {
657+
found_topic_b = true;
658+
}
659+
}
660+
661+
EXPECT_TRUE(found_topic_a)
662+
<< "No stats found containing topic_a in source name";
663+
664+
EXPECT_TRUE(found_topic_b)
665+
<< "No stats found containing topic_b in source name";
666+
667+
EXPECT_GT(source_names.size(), 1u)
668+
<< "Both subscriptions produced the same measurement_source_name";
669+
}

0 commit comments

Comments
 (0)