diff --git a/rcl/include/rcl/graph.h b/rcl/include/rcl/graph.h index 13133b6ba..2049359c8 100644 --- a/rcl/include/rcl/graph.h +++ b/rcl/include/rcl/graph.h @@ -584,6 +584,98 @@ rcl_count_subscribers( const char * topic_name, size_t * count); +/// Return the number of clients on a given service. +/** + * The `node` parameter must point to a valid node. + * + * The `service_name` parameter must not be `NULL`, and must not be an empty string. + * It should also follow the service name rules. + * + * See: https://design.ros2.org/articles/topic_and_service_names.html + * + * The `count` parameter must point to a valid size_t. + * The `count` parameter is the output for this function and will be set. + * + * In the event that error handling needs to allocate memory, this function + * will try to use the node's allocator. + * + * The service name is not automatically remapped by this function. + * If there is a client created with service name `foo` and remap rule `foo:=bar` then calling + * this with `service_name` set to `bar` will return a count of 1, and with `service_name` set to `foo` + * will return a count of 0. + * /sa rcl_remap_service_name() + * + *
+ * Attribute | Adherence + * ------------------ | ------------- + * Allocates Memory | Yes + * Thread-Safe | No + * Uses Atomics | No + * Lock-Free | Maybe [1] + * [1] implementation may need to protect the data structure with a lock + * + * \param[in] node the handle to the node being used to query the ROS graph + * \param[in] service_name the name of the service in question + * \param[out] count number of clients on the given service + * \return #RCL_RET_OK if the query was successful, or + * \return #RCL_RET_NODE_INVALID if the node is invalid, or + * \return #RCL_RET_INVALID_ARGUMENT if any arguments are invalid, or + * \return #RCL_RET_ERROR if an unspecified error occurs. + */ +RCL_PUBLIC +RCL_WARN_UNUSED +rcl_ret_t +rcl_count_clients( + const rcl_node_t * node, + const char * service_name, + size_t * count); + +/// Return the number of servers on a given service. +/** + * The `node` parameter must point to a valid node. + * + * The `service_name` parameter must not be `NULL`, and must not be an empty string. + * It should also follow the service name rules. + * + * See: https://design.ros2.org/articles/topic_and_service_names.html + * + * The `count` parameter must point to a valid size_t. + * The `count` parameter is the output for this function and will be set. + * + * In the event that error handling needs to allocate memory, this function + * will try to use the node's allocator. + * + * The service name is not automatically remapped by this function. + * If there is a server created with service name `foo` and remap rule `foo:=bar` then calling + * this with `service_name` set to `bar` will return a count of 1, and with `service_name` set to `foo` + * will return a count of 0. + * /sa rcl_remap_service_name() + * + *
+ * Attribute | Adherence + * ------------------ | ------------- + * Allocates Memory | Yes + * Thread-Safe | No + * Uses Atomics | No + * Lock-Free | Maybe [1] + * [1] implementation may need to protect the data structure with a lock + * + * \param[in] node the handle to the node being used to query the ROS graph + * \param[in] service_name the name of the service in question + * \param[out] count number of services on the given service + * \return #RCL_RET_OK if the query was successful, or + * \return #RCL_RET_NODE_INVALID if the node is invalid, or + * \return #RCL_RET_INVALID_ARGUMENT if any arguments are invalid, or + * \return #RCL_RET_ERROR if an unspecified error occurs. + */ +RCL_PUBLIC +RCL_WARN_UNUSED +rcl_ret_t +rcl_count_services( + const rcl_node_t * node, + const char * service_name, + size_t * count); + /// Wait for there to be a specified number of publishers on a given topic. /** * The `node` parameter must point to a valid node. diff --git a/rcl/src/rcl/graph.c b/rcl/src/rcl/graph.c index 5ba0a0a67..323f0199b 100644 --- a/rcl/src/rcl/graph.c +++ b/rcl/src/rcl/graph.c @@ -456,6 +456,44 @@ rcl_count_subscribers( return rcl_convert_rmw_ret_to_rcl_ret(rmw_ret); } +rcl_ret_t +rcl_count_clients( + const rcl_node_t * node, + const char * service_name, + size_t * count) +{ + if (!rcl_node_is_valid(node)) { + return RCL_RET_NODE_INVALID; // error already set + } + const rcl_node_options_t * node_options = rcl_node_get_options(node); + if (!node_options) { + return RCL_RET_NODE_INVALID; // shouldn't happen, but error is already set if so + } + RCL_CHECK_ARGUMENT_FOR_NULL(service_name, RCL_RET_INVALID_ARGUMENT); + RCL_CHECK_ARGUMENT_FOR_NULL(count, RCL_RET_INVALID_ARGUMENT); + rmw_ret_t rmw_ret = rmw_count_clients(rcl_node_get_rmw_handle(node), service_name, count); + return rcl_convert_rmw_ret_to_rcl_ret(rmw_ret); +} + +rcl_ret_t +rcl_count_services( + const rcl_node_t * node, + const char * service_name, + size_t * count) +{ + if (!rcl_node_is_valid(node)) { + return RCL_RET_NODE_INVALID; // error already set + } + const rcl_node_options_t * node_options = rcl_node_get_options(node); + if (!node_options) { + return RCL_RET_NODE_INVALID; // shouldn't happen, but error is already set if so + } + RCL_CHECK_ARGUMENT_FOR_NULL(service_name, RCL_RET_INVALID_ARGUMENT); + RCL_CHECK_ARGUMENT_FOR_NULL(count, RCL_RET_INVALID_ARGUMENT); + rmw_ret_t rmw_ret = rmw_count_services(rcl_node_get_rmw_handle(node), service_name, count); + return rcl_convert_rmw_ret_to_rcl_ret(rmw_ret); +} + typedef rcl_ret_t (* count_entities_func_t)( const rcl_node_t * node, const char * topic_name, diff --git a/rcl/test/rcl/test_graph.cpp b/rcl/test/rcl/test_graph.cpp index 742dba992..1d42c7acc 100644 --- a/rcl/test/rcl/test_graph.cpp +++ b/rcl/test/rcl/test_graph.cpp @@ -716,6 +716,80 @@ TEST_F( rcl_reset_error(); } +/* Test the rcl_count_clients function. + * + * This does not test content of the response. + */ +TEST_F( + CLASSNAME(TestGraphFixture, RMW_IMPLEMENTATION), + test_rcl_count_clients +) { + rcl_ret_t ret; + rcl_node_t zero_node = rcl_get_zero_initialized_node(); + const char * service_name = "/topic_test_rcl_count_clients"; + size_t count; + // invalid node + ret = rcl_count_clients(nullptr, service_name, &count); + EXPECT_EQ(RCL_RET_NODE_INVALID, ret) << rcl_get_error_string().str; + rcl_reset_error(); + ret = rcl_count_clients(&zero_node, service_name, &count); + EXPECT_EQ(RCL_RET_NODE_INVALID, ret) << rcl_get_error_string().str; + rcl_reset_error(); + ret = rcl_count_clients(this->old_node_ptr, service_name, &count); + EXPECT_EQ(RCL_RET_NODE_INVALID, ret) << rcl_get_error_string().str; + rcl_reset_error(); + // invalid topic name + ret = rcl_count_clients(this->node_ptr, nullptr, &count); + EXPECT_EQ(RCL_RET_INVALID_ARGUMENT, ret) << rcl_get_error_string().str; + rcl_reset_error(); + // TODO(wjwwood): test valid strings with invalid topic names in them + // invalid count + ret = rcl_count_clients(this->node_ptr, service_name, nullptr); + EXPECT_EQ(RCL_RET_INVALID_ARGUMENT, ret) << rcl_get_error_string().str; + rcl_reset_error(); + // valid call + ret = rcl_count_clients(this->node_ptr, service_name, &count); + EXPECT_EQ(RCL_RET_OK, ret) << rcl_get_error_string().str; + rcl_reset_error(); +} + +/* Test the rcl_count_services function. + * + * This does not test content of the response. + */ +TEST_F( + CLASSNAME(TestGraphFixture, RMW_IMPLEMENTATION), + test_rcl_count_services +) { + rcl_ret_t ret; + rcl_node_t zero_node = rcl_get_zero_initialized_node(); + const char * service_name = "/topic_test_rcl_count_services"; + size_t count; + // invalid node + ret = rcl_count_services(nullptr, service_name, &count); + EXPECT_EQ(RCL_RET_NODE_INVALID, ret) << rcl_get_error_string().str; + rcl_reset_error(); + ret = rcl_count_services(&zero_node, service_name, &count); + EXPECT_EQ(RCL_RET_NODE_INVALID, ret) << rcl_get_error_string().str; + rcl_reset_error(); + ret = rcl_count_services(this->old_node_ptr, service_name, &count); + EXPECT_EQ(RCL_RET_NODE_INVALID, ret) << rcl_get_error_string().str; + rcl_reset_error(); + // invalid topic name + ret = rcl_count_services(this->node_ptr, nullptr, &count); + EXPECT_EQ(RCL_RET_INVALID_ARGUMENT, ret) << rcl_get_error_string().str; + rcl_reset_error(); + // TODO(wjwwood): test valid strings with invalid topic names in them + // invalid count + ret = rcl_count_services(this->node_ptr, service_name, nullptr); + EXPECT_EQ(RCL_RET_INVALID_ARGUMENT, ret) << rcl_get_error_string().str; + rcl_reset_error(); + // valid call + ret = rcl_count_services(this->node_ptr, service_name, &count); + EXPECT_EQ(RCL_RET_OK, ret) << rcl_get_error_string().str; + rcl_reset_error(); +} + /* Test the rcl_wait_for_publishers function. */ TEST_F(