diff --git a/rcl/include/rcl/client.h b/rcl/include/rcl/client.h index 39715f181..94290982a 100644 --- a/rcl/include/rcl/client.h +++ b/rcl/include/rcl/client.h @@ -409,6 +409,58 @@ RCL_PUBLIC bool rcl_client_is_valid(const rcl_client_t * client); +/// Get the actual qos settings of the client's request publisher. +/** + * Used to get the actual qos settings of the client's request publisher. + * The actual configuration applied when using RMW_*_SYSTEM_DEFAULT + * can only be resolved after the creation of the client, and it + * depends on the underlying rmw implementation. + * If the underlying setting in use can't be represented in ROS terms, + * it will be set to RMW_*_UNKNOWN. + * The returned struct is only valid as long as the rcl_client_t is valid. + * + *
+ * Attribute | Adherence + * ------------------ | ------------- + * Allocates Memory | No + * Thread-Safe | Yes + * Uses Atomics | No + * Lock-Free | Yes + * + * \param[in] client pointer to the rcl client + * \return qos struct if successful, otherwise `NULL` + */ +RCL_PUBLIC +RCL_WARN_UNUSED +const rmw_qos_profile_t * +rcl_client_request_publisher_get_actual_qos(const rcl_client_t * client); + +/// Get the actual qos settings of the client's response subscription. +/** + * Used to get the actual qos settings of the client's response subscription. + * The actual configuration applied when using RMW_*_SYSTEM_DEFAULT + * can only be resolved after the creation of the client, and it + * depends on the underlying rmw implementation. + * If the underlying setting in use can't be represented in ROS terms, + * it will be set to RMW_*_UNKNOWN. + * The returned struct is only valid as long as the rcl_client_t is valid. + * + *
+ * Attribute | Adherence + * ------------------ | ------------- + * Allocates Memory | No + * Thread-Safe | Yes + * Uses Atomics | No + * Lock-Free | Yes + * + * \param[in] client pointer to the rcl client + * \return qos struct if successful, otherwise `NULL` + */ +RCL_PUBLIC +RCL_WARN_UNUSED +const rmw_qos_profile_t * +rcl_client_response_subscription_get_actual_qos(const rcl_client_t * client); + #ifdef __cplusplus } #endif diff --git a/rcl/include/rcl/service.h b/rcl/include/rcl/service.h index 82c33c110..b102d14c9 100644 --- a/rcl/include/rcl/service.h +++ b/rcl/include/rcl/service.h @@ -440,6 +440,58 @@ RCL_PUBLIC bool rcl_service_is_valid(const rcl_service_t * service); +/// Get the actual qos settings of the service's request subscription. +/** + * Used to get the actual qos settings of the service's request subscription. + * The actual configuration applied when using RMW_*_SYSTEM_DEFAULT + * can only be resolved after the creation of the service, and it + * depends on the underlying rmw implementation. + * If the underlying setting in use can't be represented in ROS terms, + * it will be set to RMW_*_UNKNOWN. + * The returned struct is only valid as long as the rcl_service_t is valid. + * + *
+ * Attribute | Adherence + * ------------------ | ------------- + * Allocates Memory | No + * Thread-Safe | Yes + * Uses Atomics | No + * Lock-Free | Yes + * + * \param[in] service pointer to the rcl service + * \return qos struct if successful, otherwise `NULL` + */ +RCL_PUBLIC +RCL_WARN_UNUSED +const rmw_qos_profile_t * +rcl_service_request_subscription_get_actual_qos(const rcl_service_t * service); + +/// Get the actual qos settings of the service's response publisher. +/** + * Used to get the actual qos settings of the service's response publisher. + * The actual configuration applied when using RMW_*_SYSTEM_DEFAULT + * can only be resolved after the creation of the service, and it + * depends on the underlying rmw implementation. + * If the underlying setting in use can't be represented in ROS terms, + * it will be set to RMW_*_UNKNOWN. + * The returned struct is only valid as long as the rcl_service_t is valid. + * + *
+ * Attribute | Adherence + * ------------------ | ------------- + * Allocates Memory | No + * Thread-Safe | Yes + * Uses Atomics | No + * Lock-Free | Yes + * + * \param[in] service pointer to the rcl service + * \return qos struct if successful, otherwise `NULL` + */ +RCL_PUBLIC +RCL_WARN_UNUSED +const rmw_qos_profile_t * +rcl_service_response_publisher_get_actual_qos(const rcl_service_t * service); + #ifdef __cplusplus } #endif diff --git a/rcl/src/rcl/client.c b/rcl/src/rcl/client.c index 433ed3f04..bd38efa7a 100644 --- a/rcl/src/rcl/client.c +++ b/rcl/src/rcl/client.c @@ -36,6 +36,8 @@ extern "C" struct rcl_client_impl_s { rcl_client_options_t options; + rmw_qos_profile_t actual_request_publisher_qos; + rmw_qos_profile_t actual_response_subscription_qos; rmw_client_t * rmw_handle; atomic_int_least64_t sequence_number; }; @@ -111,6 +113,33 @@ rcl_client_init( RCL_SET_ERROR_MSG(rmw_get_error_string().str); goto fail; } + + // get actual qos, and store it + rmw_ret_t rmw_ret = rmw_client_request_publisher_get_actual_qos( + client->impl->rmw_handle, + &client->impl->actual_request_publisher_qos); + + if (RMW_RET_OK != rmw_ret) { + RCL_SET_ERROR_MSG(rmw_get_error_string().str); + goto fail; + } + + rmw_ret = rmw_client_response_subscription_get_actual_qos( + client->impl->rmw_handle, + &client->impl->actual_response_subscription_qos); + + if (RMW_RET_OK != rmw_ret) { + RCL_SET_ERROR_MSG(rmw_get_error_string().str); + goto fail; + } + + // ROS specific namespacing conventions avoidance + // is not retrieved by get_actual_qos + client->impl->actual_request_publisher_qos.avoid_ros_namespace_conventions = + options->qos.avoid_ros_namespace_conventions; + client->impl->actual_response_subscription_qos.avoid_ros_namespace_conventions = + options->qos.avoid_ros_namespace_conventions; + // options client->impl->options = *options; atomic_init(&client->impl->sequence_number, 0); @@ -280,6 +309,24 @@ rcl_client_is_valid(const rcl_client_t * client) client->impl->rmw_handle, "client's rmw handle is invalid", return false); return true; } + +const rmw_qos_profile_t * +rcl_client_request_publisher_get_actual_qos(const rcl_client_t * client) +{ + if (!rcl_client_is_valid(client)) { + return NULL; + } + return &client->impl->actual_request_publisher_qos; +} + +const rmw_qos_profile_t * +rcl_client_response_subscription_get_actual_qos(const rcl_client_t * client) +{ + if (!rcl_client_is_valid(client)) { + return NULL; + } + return &client->impl->actual_response_subscription_qos; +} #ifdef __cplusplus } #endif diff --git a/rcl/src/rcl/service.c b/rcl/src/rcl/service.c index f97110659..42222b996 100644 --- a/rcl/src/rcl/service.c +++ b/rcl/src/rcl/service.c @@ -33,6 +33,8 @@ extern "C" struct rcl_service_impl_s { rcl_service_options_t options; + rmw_qos_profile_t actual_request_subscription_qos; + rmw_qos_profile_t actual_response_publisher_qos; rmw_service_t * rmw_handle; }; @@ -122,6 +124,31 @@ rcl_service_init( RCL_SET_ERROR_MSG(rmw_get_error_string().str); goto fail; } + // get actual qos, and store it + rmw_ret_t rmw_ret = rmw_service_request_subscription_get_actual_qos( + service->impl->rmw_handle, + &service->impl->actual_request_subscription_qos); + + if (RMW_RET_OK != rmw_ret) { + RCL_SET_ERROR_MSG(rmw_get_error_string().str); + goto fail; + } + + rmw_ret = rmw_service_response_publisher_get_actual_qos( + service->impl->rmw_handle, + &service->impl->actual_response_publisher_qos); + + if (RMW_RET_OK != rmw_ret) { + RCL_SET_ERROR_MSG(rmw_get_error_string().str); + goto fail; + } + + // ROS specific namespacing conventions is not retrieved by get_actual_qos + service->impl->actual_request_subscription_qos.avoid_ros_namespace_conventions = + options->qos.avoid_ros_namespace_conventions; + service->impl->actual_response_publisher_qos.avoid_ros_namespace_conventions = + options->qos.avoid_ros_namespace_conventions; + // options service->impl->options = *options; RCUTILS_LOG_DEBUG_NAMED(ROS_PACKAGE_NAME, "Service initialized"); @@ -301,6 +328,24 @@ rcl_service_is_valid(const rcl_service_t * service) return true; } +const rmw_qos_profile_t * +rcl_service_request_subscription_get_actual_qos(const rcl_service_t * service) +{ + if (!rcl_service_is_valid(service)) { + return NULL; + } + return &service->impl->actual_request_subscription_qos; +} + +const rmw_qos_profile_t * +rcl_service_response_publisher_get_actual_qos(const rcl_service_t * service) +{ + if (!rcl_service_is_valid(service)) { + return NULL; + } + return &service->impl->actual_response_publisher_qos; +} + #ifdef __cplusplus } #endif diff --git a/rcl/test/rcl/test_client.cpp b/rcl/test/rcl/test_client.cpp index 72bfa3ce9..9c3330b54 100644 --- a/rcl/test/rcl/test_client.cpp +++ b/rcl/test/rcl/test_client.cpp @@ -91,6 +91,26 @@ TEST_F(TestClientFixture, test_client_nominal) { EXPECT_EQ(rmw_qos_profile_services_default.depth, client_internal_options->qos.depth); EXPECT_EQ(rmw_qos_profile_services_default.durability, client_internal_options->qos.durability); + const rmw_qos_profile_t * request_publisher_qos = + rcl_client_request_publisher_get_actual_qos(&client); + EXPECT_EQ(rmw_qos_profile_services_default.reliability, request_publisher_qos->reliability); + EXPECT_EQ(rmw_qos_profile_services_default.history, request_publisher_qos->history); + EXPECT_EQ(rmw_qos_profile_services_default.depth, request_publisher_qos->depth); + EXPECT_EQ(rmw_qos_profile_services_default.durability, request_publisher_qos->durability); + EXPECT_EQ( + rmw_qos_profile_services_default.avoid_ros_namespace_conventions, + request_publisher_qos->avoid_ros_namespace_conventions); + + const rmw_qos_profile_t * response_subscription_qos = + rcl_client_response_subscription_get_actual_qos(&client); + EXPECT_EQ(rmw_qos_profile_services_default.reliability, response_subscription_qos->reliability); + EXPECT_EQ(rmw_qos_profile_services_default.history, response_subscription_qos->history); + EXPECT_EQ(rmw_qos_profile_services_default.depth, response_subscription_qos->depth); + EXPECT_EQ(rmw_qos_profile_services_default.durability, response_subscription_qos->durability); + EXPECT_EQ( + rmw_qos_profile_services_default.avoid_ros_namespace_conventions, + response_subscription_qos->avoid_ros_namespace_conventions); + // Check the return code of initialization and that the service name matches what's expected ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string().str; EXPECT_EQ(strcmp(rcl_client_get_service_name(&client), expected_topic_name), 0); @@ -261,6 +281,8 @@ TEST_F(TestClientFixture, test_client_bad_arguments) { RCL_RET_CLIENT_INVALID, rcl_send_request( nullptr, &client_request, &sequence_number)) << rcl_get_error_string().str; EXPECT_EQ(24, sequence_number); + EXPECT_EQ(nullptr, rcl_client_request_publisher_get_actual_qos(nullptr)); + EXPECT_EQ(nullptr, rcl_client_response_subscription_get_actual_qos(nullptr)); // Not init client EXPECT_EQ(nullptr, rcl_client_get_rmw_handle(&client)); @@ -277,6 +299,8 @@ TEST_F(TestClientFixture, test_client_bad_arguments) { RCL_RET_CLIENT_INVALID, rcl_send_request( &client, &client_request, &sequence_number)) << rcl_get_error_string().str; EXPECT_EQ(24, sequence_number); + EXPECT_EQ(nullptr, rcl_client_request_publisher_get_actual_qos(&client)); + EXPECT_EQ(nullptr, rcl_client_response_subscription_get_actual_qos(&client)); } TEST_F(TestClientFixture, test_client_init_fini_maybe_fail) diff --git a/rcl/test/rcl/test_service.cpp b/rcl/test/rcl/test_service.cpp index b4f6df67c..047eb3ebb 100644 --- a/rcl/test/rcl/test_service.cpp +++ b/rcl/test/rcl/test_service.cpp @@ -93,6 +93,20 @@ TEST_F(CLASSNAME(TestServiceFixture, RMW_IMPLEMENTATION), test_service_nominal) ret = rcl_service_init(&service, this->node_ptr, ts, topic, &service_options); EXPECT_EQ(RCL_RET_ALREADY_INIT, ret) << rcl_get_error_string().str; + const rmw_qos_profile_t * request_subscription_qos = + rcl_service_request_subscription_get_actual_qos(&service); + EXPECT_EQ(rmw_qos_profile_services_default.reliability, request_subscription_qos->reliability); + EXPECT_EQ(rmw_qos_profile_services_default.history, request_subscription_qos->history); + EXPECT_EQ(rmw_qos_profile_services_default.depth, request_subscription_qos->depth); + EXPECT_EQ(rmw_qos_profile_services_default.durability, request_subscription_qos->durability); + + const rmw_qos_profile_t * response_publisher_qos = + rcl_service_response_publisher_get_actual_qos(&service); + EXPECT_EQ(rmw_qos_profile_services_default.reliability, response_publisher_qos->reliability); + EXPECT_EQ(rmw_qos_profile_services_default.history, response_publisher_qos->history); + EXPECT_EQ(rmw_qos_profile_services_default.depth, response_publisher_qos->depth); + EXPECT_EQ(rmw_qos_profile_services_default.durability, response_publisher_qos->durability); + ret = rcl_service_fini(&service, this->node_ptr); EXPECT_EQ(RCL_RET_OK, ret) << rcl_get_error_string().str; @@ -388,6 +402,8 @@ TEST_F(CLASSNAME(TestServiceFixture, RMW_IMPLEMENTATION), test_bad_arguments) { RCL_RET_SERVICE_INVALID, rcl_send_response(nullptr, &header.request_id, &service_response)); EXPECT_EQ( RCL_RET_SERVICE_INVALID, rcl_take_request(nullptr, &(header.request_id), &service_request)); + EXPECT_EQ(nullptr, rcl_service_request_subscription_get_actual_qos(nullptr)); + EXPECT_EQ(nullptr, rcl_service_response_publisher_get_actual_qos(nullptr)); EXPECT_EQ(nullptr, rcl_service_get_service_name(&service)); EXPECT_EQ(nullptr, rcl_service_get_options(&service)); @@ -404,6 +420,9 @@ TEST_F(CLASSNAME(TestServiceFixture, RMW_IMPLEMENTATION), test_bad_arguments) { RCL_RET_BAD_ALLOC, rcl_service_init( &service, this->node_ptr, ts, topic, &service_options_bad_alloc)) << rcl_get_error_string().str; + + EXPECT_EQ(nullptr, rcl_service_request_subscription_get_actual_qos(&service)); + EXPECT_EQ(nullptr, rcl_service_response_publisher_get_actual_qos(&service)); } /* Name failed tests