Implement functions to get publisher and subcription informations like QoS policies from topic name (#960)

Signed-off-by: Barry Xu <Barry.Xu@sony.com>
Signed-off-by: Miaofei <miaofei@amazon.com>
Co-authored-by: Miaofei Mei <ameision@hotmail.com>
This commit is contained in:
Barry Xu 2020-02-15 04:25:03 +08:00 committed by GitHub
parent 7c1721a0b3
commit 2d9c6ea3a7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 530 additions and 1 deletions

View file

@ -247,6 +247,7 @@ if(BUILD_TESTING)
"rmw" "rmw"
"rosidl_generator_cpp" "rosidl_generator_cpp"
"rosidl_typesupport_cpp" "rosidl_typesupport_cpp"
"test_msgs"
) )
target_link_libraries(test_node ${PROJECT_NAME}) target_link_libraries(test_node ${PROJECT_NAME})
endif() endif()

View file

@ -867,6 +867,58 @@ public:
size_t size_t
count_subscribers(const std::string & topic_name) const; count_subscribers(const std::string & topic_name) const;
/// Return the topic endpoint information about publishers on a given topic.
/**
* The returned parameter is a list of topic endpoint information, where each item will contain
* the node name, node namespace, topic type, endpoint type, topic endpoint's GID, and its QoS
* profile.
*
* When the `no_mangle` parameter is `true`, the provided `topic_name` should be a valid topic
* name for the middleware (useful when combining ROS with native middleware (e.g. DDS) apps).
* When the `no_mangle` parameter is `false`, the provided `topic_name` should follow
* ROS topic name conventions.
*
* `topic_name` may be a relative, private, or fully qualified topic name.
* A relative or private topic will be expanded using this node's namespace and name.
* The queried `topic_name` is not remapped.
*
* \param[in] topic_name the topic_name on which to find the publishers.
* \param[in] no_mangle if `true`, `topic_name` needs to be a valid middleware topic name,
* otherwise it should be a valid ROS topic name. Defaults to `false`.
* \return a list of TopicEndpointInfo representing all the publishers on this topic.
* \throws InvalidTopicNameError if the given topic_name is invalid.
* \throws std::runtime_error if internal error happens.
*/
RCLCPP_PUBLIC
std::vector<rclcpp::TopicEndpointInfo>
get_publishers_info_by_topic(const std::string & topic_name, bool no_mangle = false) const;
/// Return the topic endpoint information about subscriptions on a given topic.
/**
* The returned parameter is a list of topic endpoint information, where each item will contain
* the node name, node namespace, topic type, endpoint type, topic endpoint's GID, and its QoS
* profile.
*
* When the `no_mangle` parameter is `true`, the provided `topic_name` should be a valid topic
* name for the middleware (useful when combining ROS with native middleware (e.g. DDS) apps).
* When the `no_mangle` parameter is `false`, the provided `topic_name` should follow
* ROS topic name conventions.
*
* `topic_name` may be a relative, private, or fully qualified topic name.
* A relative or private topic will be expanded using this node's namespace and name.
* The queried `topic_name` is not remapped.
*
* \param[in] topic_name the topic_name on which to find the subscriptions.
* \param[in] no_mangle if `true`, `topic_name` needs to be a valid middleware topic name,
* otherwise it should be a valid ROS topic name. Defaults to `false`.
* \return a list of TopicEndpointInfo representing all the subscriptions on this topic.
* \throws InvalidTopicNameError if the given topic_name is invalid.
* \throws std::runtime_error if internal error happens.
*/
RCLCPP_PUBLIC
std::vector<rclcpp::TopicEndpointInfo>
get_subscriptions_info_by_topic(const std::string & topic_name, bool no_mangle = false) const;
/// Return a graph event, which will be set anytime a graph change occurs. /// Return a graph event, which will be set anytime a graph change occurs.
/* The graph Event object is a loan which must be returned. /* The graph Event object is a loan which must be returned.
* The Event object is scoped and therefore to return the loan just let it go * The Event object is scoped and therefore to return the loan just let it go

View file

@ -30,6 +30,7 @@
#include "rclcpp/node_interfaces/node_base_interface.hpp" #include "rclcpp/node_interfaces/node_base_interface.hpp"
#include "rclcpp/node_interfaces/node_graph_interface.hpp" #include "rclcpp/node_interfaces/node_graph_interface.hpp"
#include "rclcpp/visibility_control.hpp" #include "rclcpp/visibility_control.hpp"
#include "rmw/topic_endpoint_info_array.h"
namespace rclcpp namespace rclcpp
{ {
@ -117,6 +118,18 @@ public:
size_t size_t
count_graph_users() override; count_graph_users() override;
RCLCPP_PUBLIC
std::vector<rclcpp::TopicEndpointInfo>
get_publishers_info_by_topic(
const std::string & topic_name,
bool no_mangle = false) const override;
RCLCPP_PUBLIC
std::vector<rclcpp::TopicEndpointInfo>
get_subscriptions_info_by_topic(
const std::string & topic_name,
bool no_mangle = false) const override;
private: private:
RCLCPP_DISABLE_COPY(NodeGraph) RCLCPP_DISABLE_COPY(NodeGraph)

View file

@ -15,20 +15,120 @@
#ifndef RCLCPP__NODE_INTERFACES__NODE_GRAPH_INTERFACE_HPP_ #ifndef RCLCPP__NODE_INTERFACES__NODE_GRAPH_INTERFACE_HPP_
#define RCLCPP__NODE_INTERFACES__NODE_GRAPH_INTERFACE_HPP_ #define RCLCPP__NODE_INTERFACES__NODE_GRAPH_INTERFACE_HPP_
#include <algorithm>
#include <array>
#include <chrono> #include <chrono>
#include <map> #include <map>
#include <string> #include <string>
#include <utility> #include <utility>
#include <vector> #include <vector>
#include "rcl/graph.h"
#include "rcl/guard_condition.h" #include "rcl/guard_condition.h"
#include "rclcpp/event.hpp" #include "rclcpp/event.hpp"
#include "rclcpp/macros.hpp" #include "rclcpp/macros.hpp"
#include "rclcpp/qos.hpp"
#include "rclcpp/visibility_control.hpp" #include "rclcpp/visibility_control.hpp"
namespace rclcpp namespace rclcpp
{ {
enum class EndpointType
{
Invalid = RMW_ENDPOINT_INVALID,
Publisher = RMW_ENDPOINT_PUBLISHER,
Subscription = RMW_ENDPOINT_SUBSCRIPTION
};
/**
* Struct that contains topic endpoint information like the associated node name, node namespace,
* topic type, endpoint type, endpoint GID, and its QoS.
*/
class TopicEndpointInfo
{
public:
/// Construct a TopicEndpointInfo from a rcl_topic_endpoint_info_t.
RCLCPP_PUBLIC
explicit TopicEndpointInfo(const rcl_topic_endpoint_info_t & info)
: node_name_(info.node_name),
node_namespace_(info.node_namespace),
topic_type_(info.topic_type),
endpoint_type_(static_cast<rclcpp::EndpointType>(info.endpoint_type)),
qos_profile_({info.qos_profile.history, info.qos_profile.depth}, info.qos_profile)
{
std::copy(info.endpoint_gid, info.endpoint_gid + RMW_GID_STORAGE_SIZE, endpoint_gid_.begin());
}
/// Get a mutable reference to the node name.
RCLCPP_PUBLIC
std::string &
node_name();
/// Get a const reference to the node name.
RCLCPP_PUBLIC
const std::string &
node_name() const;
/// Get a mutable reference to the node namespace.
RCLCPP_PUBLIC
std::string &
node_namespace();
/// Get a const reference to the node namespace.
RCLCPP_PUBLIC
const std::string &
node_namespace() const;
/// Get a mutable reference to the topic type string.
RCLCPP_PUBLIC
std::string &
topic_type();
/// Get a const reference to the topic type string.
RCLCPP_PUBLIC
const std::string &
topic_type() const;
/// Get a mutable reference to the topic endpoint type.
RCLCPP_PUBLIC
rclcpp::EndpointType &
endpoint_type();
/// Get a const reference to the topic endpoint type.
RCLCPP_PUBLIC
const rclcpp::EndpointType &
endpoint_type() const;
/// Get a mutable reference to the GID of the topic endpoint.
RCLCPP_PUBLIC
std::array<uint8_t, RMW_GID_STORAGE_SIZE> &
endpoint_gid();
/// Get a const reference to the GID of the topic endpoint.
RCLCPP_PUBLIC
const std::array<uint8_t, RMW_GID_STORAGE_SIZE> &
endpoint_gid() const;
/// Get a mutable reference to the QoS profile of the topic endpoint.
RCLCPP_PUBLIC
rclcpp::QoS &
qos_profile();
/// Get a const reference to the QoS profile of the topic endpoint.
RCLCPP_PUBLIC
const rclcpp::QoS &
qos_profile() const;
private:
std::string node_name_;
std::string node_namespace_;
std::string topic_type_;
rclcpp::EndpointType endpoint_type_;
std::array<uint8_t, RMW_GID_STORAGE_SIZE> endpoint_gid_;
rclcpp::QoS qos_profile_;
};
namespace node_interfaces namespace node_interfaces
{ {
@ -150,6 +250,24 @@ public:
virtual virtual
size_t size_t
count_graph_users() = 0; count_graph_users() = 0;
/// Return the topic endpoint information about publishers on a given topic.
/**
* \sa rclcpp::Node::get_publishers_info_by_topic
*/
RCLCPP_PUBLIC
virtual
std::vector<rclcpp::TopicEndpointInfo>
get_publishers_info_by_topic(const std::string & topic_name, bool no_mangle = false) const = 0;
/// Return the topic endpoint information about subscriptions on a given topic.
/**
* \sa rclcpp::Node::get_subscriptions_info_by_topic
*/
RCLCPP_PUBLIC
virtual
std::vector<rclcpp::TopicEndpointInfo>
get_subscriptions_info_by_topic(const std::string & topic_name, bool no_mangle = false) const = 0;
}; };
} // namespace node_interfaces } // namespace node_interfaces

View file

@ -364,6 +364,18 @@ Node::count_subscribers(const std::string & topic_name) const
return node_graph_->count_subscribers(topic_name); return node_graph_->count_subscribers(topic_name);
} }
std::vector<rclcpp::TopicEndpointInfo>
Node::get_publishers_info_by_topic(const std::string & topic_name, bool no_mangle) const
{
return node_graph_->get_publishers_info_by_topic(topic_name, no_mangle);
}
std::vector<rclcpp::TopicEndpointInfo>
Node::get_subscriptions_info_by_topic(const std::string & topic_name, bool no_mangle) const
{
return node_graph_->get_subscriptions_info_by_topic(topic_name, no_mangle);
}
const std::vector<rclcpp::callback_group::CallbackGroup::WeakPtr> & const std::vector<rclcpp::callback_group::CallbackGroup::WeakPtr> &
Node::get_callback_groups() const Node::get_callback_groups() const
{ {

View file

@ -21,9 +21,11 @@
#include <vector> #include <vector>
#include "rcl/graph.h" #include "rcl/graph.h"
#include "rclcpp/exceptions.hpp" #include "rcl/remap.h"
#include "rclcpp/event.hpp" #include "rclcpp/event.hpp"
#include "rclcpp/exceptions.hpp"
#include "rclcpp/graph_listener.hpp" #include "rclcpp/graph_listener.hpp"
#include "rclcpp/node_interfaces/node_graph_interface.hpp"
using rclcpp::node_interfaces::NodeGraph; using rclcpp::node_interfaces::NodeGraph;
using rclcpp::exceptions::throw_from_rcl_error; using rclcpp::exceptions::throw_from_rcl_error;
@ -369,3 +371,189 @@ NodeGraph::count_graph_users()
{ {
return graph_users_count_.load(); return graph_users_count_.load();
} }
static
std::vector<rclcpp::TopicEndpointInfo>
convert_to_topic_info_list(const rcl_topic_endpoint_info_array_t & info_array)
{
std::vector<rclcpp::TopicEndpointInfo> topic_info_list;
for (size_t i = 0; i < info_array.count; ++i) {
topic_info_list.push_back(rclcpp::TopicEndpointInfo(info_array.info_array[i]));
}
return topic_info_list;
}
template<const char * EndpointType, typename FunctionT>
static std::vector<rclcpp::TopicEndpointInfo>
get_info_by_topic(
rclcpp::node_interfaces::NodeBaseInterface * node_base,
const std::string & topic_name,
bool no_mangle,
FunctionT rcl_get_info_by_topic)
{
std::string fqdn;
auto rcl_node_handle = node_base->get_rcl_node_handle();
if (no_mangle) {
fqdn = topic_name;
} else {
fqdn = rclcpp::expand_topic_or_service_name(
topic_name,
rcl_node_get_name(rcl_node_handle),
rcl_node_get_namespace(rcl_node_handle),
false); // false = not a service
// Get the node options
const rcl_node_options_t * node_options = rcl_node_get_options(rcl_node_handle);
if (nullptr == node_options) {
throw std::runtime_error("Need valid node options in get_info_by_topic()");
}
const rcl_arguments_t * global_args = nullptr;
if (node_options->use_global_arguments) {
global_args = &(rcl_node_handle->context->global_arguments);
}
char * remapped_topic_name = nullptr;
rcl_ret_t ret = rcl_remap_topic_name(
&(node_options->arguments),
global_args,
fqdn.c_str(),
rcl_node_get_name(rcl_node_handle),
rcl_node_get_namespace(rcl_node_handle),
node_options->allocator,
&remapped_topic_name);
if (RCL_RET_OK != ret) {
throw_from_rcl_error(ret, std::string("Failed to remap topic name ") + fqdn);
} else if (nullptr != remapped_topic_name) {
fqdn = remapped_topic_name;
node_options->allocator.deallocate(remapped_topic_name, node_options->allocator.state);
}
}
rcutils_allocator_t allocator = rcutils_get_default_allocator();
rcl_topic_endpoint_info_array_t info_array = rcl_get_zero_initialized_topic_endpoint_info_array();
rcl_ret_t ret =
rcl_get_info_by_topic(rcl_node_handle, &allocator, fqdn.c_str(), no_mangle, &info_array);
if (RCL_RET_OK != ret) {
auto error_msg =
std::string("Failed to get information by topic for ") + EndpointType + std::string(":");
if (RCL_RET_UNSUPPORTED == ret) {
error_msg += std::string("function not supported by RMW_IMPLEMENTATION");
} else {
error_msg += rcl_get_error_string().str;
}
rcl_reset_error();
if (RCL_RET_OK != rcl_topic_endpoint_info_array_fini(&info_array, &allocator)) {
error_msg += std::string(", failed also to cleanup topic info array, leaking memory: ") +
rcl_get_error_string().str;
rcl_reset_error();
}
throw_from_rcl_error(ret, error_msg);
}
std::vector<rclcpp::TopicEndpointInfo> topic_info_list = convert_to_topic_info_list(info_array);
ret = rcl_topic_endpoint_info_array_fini(&info_array, &allocator);
if (RCL_RET_OK != ret) {
throw_from_rcl_error(ret, "rcl_topic_info_array_fini failed.");
}
return topic_info_list;
}
static const char kPublisherEndpointTypeName[] = "publishers";
std::vector<rclcpp::TopicEndpointInfo>
NodeGraph::get_publishers_info_by_topic(
const std::string & topic_name,
bool no_mangle) const
{
return get_info_by_topic<kPublisherEndpointTypeName>(
node_base_,
topic_name,
no_mangle,
rcl_get_publishers_info_by_topic);
}
static const char kSubscriptionEndpointTypeName[] = "subscriptions";
std::vector<rclcpp::TopicEndpointInfo>
NodeGraph::get_subscriptions_info_by_topic(
const std::string & topic_name,
bool no_mangle) const
{
return get_info_by_topic<kSubscriptionEndpointTypeName>(
node_base_,
topic_name,
no_mangle,
rcl_get_subscriptions_info_by_topic);
}
std::string &
rclcpp::TopicEndpointInfo::node_name()
{
return node_name_;
}
const std::string &
rclcpp::TopicEndpointInfo::node_name() const
{
return node_name_;
}
std::string &
rclcpp::TopicEndpointInfo::node_namespace()
{
return node_namespace_;
}
const std::string &
rclcpp::TopicEndpointInfo::node_namespace() const
{
return node_namespace_;
}
std::string &
rclcpp::TopicEndpointInfo::topic_type()
{
return topic_type_;
}
const std::string &
rclcpp::TopicEndpointInfo::topic_type() const
{
return topic_type_;
}
rclcpp::EndpointType &
rclcpp::TopicEndpointInfo::endpoint_type()
{
return endpoint_type_;
}
const rclcpp::EndpointType &
rclcpp::TopicEndpointInfo::endpoint_type() const
{
return endpoint_type_;
}
std::array<uint8_t, RMW_GID_STORAGE_SIZE> &
rclcpp::TopicEndpointInfo::endpoint_gid()
{
return endpoint_gid_;
}
const std::array<uint8_t, RMW_GID_STORAGE_SIZE> &
rclcpp::TopicEndpointInfo::endpoint_gid() const
{
return endpoint_gid_;
}
rclcpp::QoS &
rclcpp::TopicEndpointInfo::qos_profile()
{
return qos_profile_;
}
const rclcpp::QoS &
rclcpp::TopicEndpointInfo::qos_profile() const
{
return qos_profile_;
}

View file

@ -26,6 +26,7 @@
#include "rclcpp/rclcpp.hpp" #include "rclcpp/rclcpp.hpp"
#include "rcpputils/filesystem_helper.hpp" #include "rcpputils/filesystem_helper.hpp"
#include "test_msgs/msg/basic_types.hpp"
class TestNode : public ::testing::Test class TestNode : public ::testing::Test
{ {
@ -2510,3 +2511,119 @@ TEST_F(TestNode, set_on_parameters_set_callback_set_on_parameters_set_callback)
node->set_parameter(rclcpp::Parameter("intparam", 40)); node->set_parameter(rclcpp::Parameter("intparam", 40));
}, rclcpp::exceptions::ParameterModifiedInCallbackException); }, rclcpp::exceptions::ParameterModifiedInCallbackException);
} }
// test that calling get_publishers_info_by_topic and get_subscriptions_info_by_topic
TEST_F(TestNode, get_publishers_subscriptions_info_by_topic) {
auto node = std::make_shared<rclcpp::Node>("my_node", "/ns");
std::string topic_name = "test_topic_info";
std::string fq_topic_name = rclcpp::expand_topic_or_service_name(
topic_name, node->get_name(), node->get_namespace());
// Lists should be empty
EXPECT_TRUE(node->get_publishers_info_by_topic(fq_topic_name).empty());
EXPECT_TRUE(node->get_subscriptions_info_by_topic(fq_topic_name).empty());
// Add a publisher
rclcpp::QoSInitialization qos_initialization =
{
RMW_QOS_POLICY_HISTORY_KEEP_ALL,
10
};
rmw_qos_profile_t rmw_qos_profile_default =
{
RMW_QOS_POLICY_HISTORY_KEEP_ALL,
10,
RMW_QOS_POLICY_RELIABILITY_BEST_EFFORT,
RMW_QOS_POLICY_DURABILITY_TRANSIENT_LOCAL,
{1, 12345},
{20, 9887665},
RMW_QOS_POLICY_LIVELINESS_MANUAL_BY_TOPIC,
{5, 23456},
false
};
rclcpp::QoS qos = rclcpp::QoS(qos_initialization, rmw_qos_profile_default);
auto publisher = node->create_publisher<test_msgs::msg::BasicTypes>(topic_name, qos);
// List should have one item
auto publisher_list = node->get_publishers_info_by_topic(fq_topic_name);
EXPECT_EQ(publisher_list.size(), (size_t)1);
// Subscription list should be empty
EXPECT_TRUE(node->get_subscriptions_info_by_topic(fq_topic_name).empty());
// Verify publisher list has the right data.
EXPECT_EQ(node->get_name(), publisher_list[0].node_name());
EXPECT_EQ(node->get_namespace(), publisher_list[0].node_namespace());
EXPECT_EQ("test_msgs/msg/BasicTypes", publisher_list[0].topic_type());
EXPECT_EQ(rclcpp::EndpointType::Publisher, publisher_list[0].endpoint_type());
auto actual_qos_profile = publisher_list[0].qos_profile().get_rmw_qos_profile();
auto assert_qos_profile = [](const rmw_qos_profile_t & qos1, const rmw_qos_profile_t & qos2) {
// Depth and history are skipped because they are not retrieved.
EXPECT_EQ(qos1.reliability, qos2.reliability);
EXPECT_EQ(qos1.durability, qos2.durability);
EXPECT_EQ(memcmp(&qos1.deadline, &qos2.deadline, sizeof(struct rmw_time_t)), 0);
EXPECT_EQ(memcmp(&qos1.lifespan, &qos2.lifespan, sizeof(struct rmw_time_t)), 0);
EXPECT_EQ(qos1.liveliness, qos2.liveliness);
EXPECT_EQ(
memcmp(
&qos1.liveliness_lease_duration,
&qos2.liveliness_lease_duration,
sizeof(struct rmw_time_t)),
0);
};
assert_qos_profile(qos.get_rmw_qos_profile(), actual_qos_profile);
// Add a subscription
rclcpp::QoSInitialization qos_initialization2 =
{
RMW_QOS_POLICY_HISTORY_KEEP_LAST,
0
};
rmw_qos_profile_t rmw_qos_profile_default2 =
{
RMW_QOS_POLICY_HISTORY_KEEP_LAST,
0,
RMW_QOS_POLICY_RELIABILITY_RELIABLE,
RMW_QOS_POLICY_DURABILITY_VOLATILE,
{15, 1678},
{29, 2345},
RMW_QOS_POLICY_LIVELINESS_MANUAL_BY_NODE,
{5, 23456},
false
};
rclcpp::QoS qos2 = rclcpp::QoS(qos_initialization2, rmw_qos_profile_default2);
auto callback = [](const test_msgs::msg::BasicTypes::SharedPtr msg) {
(void)msg;
};
auto subscriber =
node->create_subscription<test_msgs::msg::BasicTypes>(topic_name, qos2, callback);
// Both lists should have one item
publisher_list = node->get_publishers_info_by_topic(fq_topic_name);
auto subscription_list = node->get_subscriptions_info_by_topic(fq_topic_name);
EXPECT_EQ(publisher_list.size(), (size_t)1);
EXPECT_EQ(subscription_list.size(), (size_t)1);
// Verify publisher and subscription list has the right data.
EXPECT_EQ(node->get_name(), publisher_list[0].node_name());
EXPECT_EQ(node->get_namespace(), publisher_list[0].node_namespace());
EXPECT_EQ("test_msgs/msg/BasicTypes", publisher_list[0].topic_type());
EXPECT_EQ(rclcpp::EndpointType::Publisher, publisher_list[0].endpoint_type());
auto publisher_qos_profile = publisher_list[0].qos_profile().get_rmw_qos_profile();
assert_qos_profile(qos.get_rmw_qos_profile(), publisher_qos_profile);
EXPECT_EQ(node->get_name(), subscription_list[0].node_name());
EXPECT_EQ(node->get_namespace(), subscription_list[0].node_namespace());
EXPECT_EQ("test_msgs/msg/BasicTypes", subscription_list[0].topic_type());
EXPECT_EQ(rclcpp::EndpointType::Subscription, subscription_list[0].endpoint_type());
auto subscription_qos_profile = subscription_list[0].qos_profile().get_rmw_qos_profile();
assert_qos_profile(qos2.get_rmw_qos_profile(), subscription_qos_profile);
// Error cases
EXPECT_THROW(
{
publisher_list = node->get_publishers_info_by_topic("13");
}, rclcpp::exceptions::InvalidTopicNameError);
EXPECT_THROW(
{
subscription_list = node->get_subscriptions_info_by_topic("13");
}, rclcpp::exceptions::InvalidTopicNameError);
}

View file

@ -439,6 +439,22 @@ public:
size_t size_t
count_subscribers(const std::string & topic_name) const; count_subscribers(const std::string & topic_name) const;
/// Return the topic endpoint information about publishers on a given topic.
/**
* \sa rclcpp::Node::get_publishers_info_by_topic
*/
RCLCPP_LIFECYCLE_PUBLIC
std::vector<rclcpp::TopicEndpointInfo>
get_publishers_info_by_topic(const std::string & topic_name, bool no_mangle = false) const;
/// Return the topic endpoint information about subscriptions on a given topic.
/**
* \sa rclcpp::Node::get_subscriptions_info_by_topic
*/
RCLCPP_LIFECYCLE_PUBLIC
std::vector<rclcpp::TopicEndpointInfo>
get_subscriptions_info_by_topic(const std::string & topic_name, bool no_mangle = false) const;
/// Return a graph event, which will be set anytime a graph change occurs. /// Return a graph event, which will be set anytime a graph change occurs.
/* The graph Event object is a loan which must be returned. /* The graph Event object is a loan which must be returned.
* The Event object is scoped and therefore to return the load just let it go * The Event object is scoped and therefore to return the load just let it go

View file

@ -290,6 +290,18 @@ LifecycleNode::count_subscribers(const std::string & topic_name) const
return node_graph_->count_subscribers(topic_name); return node_graph_->count_subscribers(topic_name);
} }
std::vector<rclcpp::TopicEndpointInfo>
LifecycleNode::get_publishers_info_by_topic(const std::string & topic_name, bool no_mangle) const
{
return node_graph_->get_publishers_info_by_topic(topic_name, no_mangle);
}
std::vector<rclcpp::TopicEndpointInfo>
LifecycleNode::get_subscriptions_info_by_topic(const std::string & topic_name, bool no_mangle) const
{
return node_graph_->get_subscriptions_info_by_topic(topic_name, no_mangle);
}
const std::vector<rclcpp::callback_group::CallbackGroup::WeakPtr> & const std::vector<rclcpp::callback_group::CallbackGroup::WeakPtr> &
LifecycleNode::get_callback_groups() const LifecycleNode::get_callback_groups() const
{ {