25 #include "rcl/error_handling.h"
27 #include "rcl/node_type_cache.h"
30 #include "rcutils/logging_macros.h"
31 #include "rcutils/macros.h"
32 #include "rcutils/stdatomic_helper.h"
33 #include "rmw/error_handling.h"
35 #include "service_msgs/msg/service_event_info.h"
36 #include "tracetools/tracetools.h"
38 #include "rosidl_runtime_c/service_type_support_struct.h"
41 #include "./service_event_publisher.h"
46 rmw_qos_profile_t actual_request_publisher_qos;
47 rmw_qos_profile_t actual_response_subscription_qos;
48 rmw_client_t * rmw_handle;
49 atomic_int_least64_t sequence_number;
51 char * remapped_service_name;
52 rosidl_type_hash_t type_hash;
65 unconfigure_service_introspection(
70 if (client_impl == NULL) {
74 if (client_impl->service_event_publisher == NULL) {
78 rcl_ret_t ret = rcl_service_event_publisher_fini(client_impl->service_event_publisher, node);
80 allocator->deallocate(client_impl->service_event_publisher, allocator->state);
81 client_impl->service_event_publisher = NULL;
90 const rosidl_service_type_support_t * type_support,
91 const char * service_name,
104 RCUTILS_LOG_DEBUG_NAMED(
105 ROS_PACKAGE_NAME,
"Initializing client for service name '%s'", service_name);
107 RCL_SET_ERROR_MSG(
"client already initialized, or memory was unintialized");
114 RCL_CHECK_FOR_NULL_WITH_MSG(
115 client->
impl,
"allocating memory failed",
125 &client->
impl->remapped_service_name);
132 goto free_client_impl;
134 RCUTILS_LOG_DEBUG_NAMED(
135 ROS_PACKAGE_NAME,
"Expanded and remapped service name '%s'",
136 client->
impl->remapped_service_name);
141 client->
impl->rmw_handle = rmw_create_client(
144 client->
impl->remapped_service_name,
146 if (!client->
impl->rmw_handle) {
147 RCL_SET_ERROR_MSG(rmw_get_error_string().str);
149 goto free_remapped_service_name;
153 rmw_ret_t rmw_ret = rmw_client_request_publisher_get_actual_qos(
154 client->
impl->rmw_handle,
155 &client->
impl->actual_request_publisher_qos);
156 if (RMW_RET_OK != rmw_ret) {
157 RCL_SET_ERROR_MSG(rmw_get_error_string().str);
158 ret = rcl_convert_rmw_ret_to_rcl_ret(rmw_ret);
162 rmw_ret = rmw_client_response_subscription_get_actual_qos(
163 client->
impl->rmw_handle,
164 &client->
impl->actual_response_subscription_qos);
165 if (RMW_RET_OK != rmw_ret) {
166 RCL_SET_ERROR_MSG(rmw_get_error_string().str);
167 ret = rcl_convert_rmw_ret_to_rcl_ret(rmw_ret);
173 client->
impl->actual_request_publisher_qos.avoid_ros_namespace_conventions =
174 options->
qos.avoid_ros_namespace_conventions;
175 client->
impl->actual_response_subscription_qos.avoid_ros_namespace_conventions =
176 options->
qos.avoid_ros_namespace_conventions;
179 client->
impl->options = *options;
180 atomic_init(&client->
impl->sequence_number, 0);
182 const rosidl_type_hash_t * hash = type_support->get_type_hash_func(type_support);
184 RCL_SET_ERROR_MSG(
"Failed to get the type hash");
189 if (
RCL_RET_OK != rcl_node_type_cache_register_type(
190 node, hash, type_support->get_type_description_func(type_support),
191 type_support->get_type_description_sources_func(type_support)))
193 rcutils_reset_error();
194 RCL_SET_ERROR_MSG(
"Failed to register type for client");
198 client->
impl->type_hash = *hash;
200 RCUTILS_LOG_DEBUG_NAMED(ROS_PACKAGE_NAME,
"Client initialized");
201 TRACETOOLS_TRACEPOINT(
203 (
const void *)client,
205 (
const void *)client->
impl->rmw_handle,
206 client->
impl->remapped_service_name);
212 if (RMW_RET_OK != rmw_ret) {
213 RCUTILS_SAFE_FWRITE_TO_STDERR(rmw_get_error_string().str);
214 RCUTILS_SAFE_FWRITE_TO_STDERR(
"\n");
217 free_remapped_service_name:
218 allocator->deallocate(client->
impl->remapped_service_name, allocator->state);
219 client->
impl->remapped_service_name = NULL;
222 allocator->deallocate(client->
impl, allocator->state);
235 RCUTILS_LOG_DEBUG_NAMED(ROS_PACKAGE_NAME,
"Finalizing client");
249 rcl_ret_t rcl_ret = unconfigure_service_introspection(node, client->
impl, &allocator);
251 RCL_SET_ERROR_MSG(rcl_get_error_string().str);
255 rmw_ret_t ret = rmw_destroy_client(rmw_node, client->
impl->rmw_handle);
256 if (ret != RMW_RET_OK) {
257 RCL_SET_ERROR_MSG(rmw_get_error_string().str);
262 ROSIDL_TYPE_HASH_VERSION_UNSET != client->
impl->type_hash.version &&
263 RCL_RET_OK != rcl_node_type_cache_unregister_type(node, &client->
impl->type_hash))
265 RCUTILS_SAFE_FWRITE_TO_STDERR(rcl_get_error_string().str);
269 allocator.deallocate(client->
impl->remapped_service_name, allocator.state);
270 client->
impl->remapped_service_name = NULL;
272 allocator.deallocate(client->
impl, allocator.state);
275 RCUTILS_LOG_DEBUG_NAMED(ROS_PACKAGE_NAME,
"Client finalized");
285 default_options.
qos = rmw_qos_profile_services_default;
287 return default_options;
296 return client->
impl->rmw_handle->service_name;
305 return &client->
impl->options;
314 return client->
impl->rmw_handle;
320 RCUTILS_LOG_DEBUG_NAMED(ROS_PACKAGE_NAME,
"Client sending service request");
326 *sequence_number = rcutils_atomic_load_int64_t(&client->
impl->sequence_number);
327 if (rmw_send_request(
328 client->
impl->rmw_handle, ros_request, sequence_number) != RMW_RET_OK)
330 RCL_SET_ERROR_MSG(rmw_get_error_string().str);
333 rcutils_atomic_exchange_int64_t(&client->
impl->sequence_number, *sequence_number);
335 if (client->
impl->service_event_publisher != NULL) {
337 rmw_ret_t rmw_ret = rmw_get_gid_for_client(client->
impl->rmw_handle, &gid);
338 if (rmw_ret != RMW_RET_OK) {
339 RCL_SET_ERROR_MSG(rmw_get_error_string().str);
340 return rcl_convert_rmw_ret_to_rcl_ret(rmw_ret);
342 rcl_ret_t ret = rcl_send_service_event_message(
343 client->
impl->service_event_publisher,
344 service_msgs__msg__ServiceEventInfo__REQUEST_SENT,
349 RCL_SET_ERROR_MSG(rcl_get_error_string().str);
359 rmw_service_info_t * request_header,
362 RCUTILS_LOG_DEBUG_NAMED(ROS_PACKAGE_NAME,
"Client taking service response");
371 request_header->source_timestamp = 0;
372 request_header->received_timestamp = 0;
373 if (rmw_take_response(
374 client->
impl->rmw_handle, request_header, ros_response, &taken) != RMW_RET_OK)
376 RCL_SET_ERROR_MSG(rmw_get_error_string().str);
379 RCUTILS_LOG_DEBUG_NAMED(
380 ROS_PACKAGE_NAME,
"Client take response succeeded: %s", taken ?
"true" :
"false");
385 if (client->
impl->service_event_publisher != NULL) {
387 rmw_ret_t rmw_ret = rmw_get_gid_for_client(client->
impl->rmw_handle, &gid);
388 if (rmw_ret != RMW_RET_OK) {
389 RCL_SET_ERROR_MSG(rmw_get_error_string().str);
390 return rcl_convert_rmw_ret_to_rcl_ret(rmw_ret);
392 rcl_ret_t ret = rcl_send_service_event_message(
393 client->
impl->service_event_publisher,
394 service_msgs__msg__ServiceEventInfo__RESPONSE_RECEIVED,
396 request_header->request_id.sequence_number,
399 RCL_SET_ERROR_MSG(rcl_get_error_string().str);
409 rmw_request_id_t * request_header,
412 rmw_service_info_t header;
413 header.request_id = *request_header;
415 *request_header = header.request_id;
422 RCL_CHECK_FOR_NULL_WITH_MSG(client,
"client pointer is invalid",
return false);
423 RCL_CHECK_FOR_NULL_WITH_MSG(
424 client->
impl,
"client's rmw implementation is invalid",
return false);
425 RCL_CHECK_FOR_NULL_WITH_MSG(
426 client->
impl->rmw_handle,
"client's rmw handle is invalid",
return false);
430 const rmw_qos_profile_t *
436 return &client->
impl->actual_request_publisher_qos;
439 const rmw_qos_profile_t *
445 return &client->
impl->actual_response_subscription_qos;
451 rcl_event_callback_t callback,
452 const void * user_data)
459 return rmw_client_set_on_new_response_callback(
460 client->
impl->rmw_handle,
470 const rosidl_service_type_support_t * type_support,
472 rcl_service_introspection_state_t introspection_state)
483 if (introspection_state == RCL_SERVICE_INTROSPECTION_OFF) {
484 return unconfigure_service_introspection(node, client->
impl, &allocator);
487 if (client->
impl->service_event_publisher == NULL) {
490 client->
impl->service_event_publisher = allocator.allocate(
492 RCL_CHECK_FOR_NULL_WITH_MSG(
495 *client->
impl->service_event_publisher = rcl_get_zero_initialized_service_event_publisher();
496 rcl_ret_t ret = rcl_service_event_publisher_init(
497 client->
impl->service_event_publisher, node, clock, publisher_options,
498 client->
impl->remapped_service_name, type_support);
500 allocator.deallocate(client->
impl->service_event_publisher, allocator.state);
501 client->
impl->service_event_publisher = NULL;
506 return rcl_service_event_publisher_change_state(
507 client->
impl->service_event_publisher, introspection_state);
#define rcl_get_default_allocator
Return a properly initialized rcl_allocator_t with default values.
#define RCL_CHECK_ALLOCATOR_WITH_MSG(allocator, msg, fail_statement)
Check that the given allocator is initialized, or fail with a message.
rcutils_allocator_t rcl_allocator_t
Encapsulation of an allocator.
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_take_response_with_info(const rcl_client_t *client, rmw_service_info_t *request_header, void *ros_response)
Take a ROS response using a client.
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_client_configure_service_introspection(rcl_client_t *client, rcl_node_t *node, rcl_clock_t *clock, const rosidl_service_type_support_t *type_support, const rcl_publisher_options_t publisher_options, rcl_service_introspection_state_t introspection_state)
Configures service introspection features for the client.
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 request publisher.
RCL_PUBLIC RCL_WARN_UNUSED const char * rcl_client_get_service_name(const rcl_client_t *client)
Get the name of the service that this client will request a response from.
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_client_fini(rcl_client_t *client, rcl_node_t *node)
Finalize a rcl_client_t.
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_client_init(rcl_client_t *client, const rcl_node_t *node, const rosidl_service_type_support_t *type_support, const char *service_name, const rcl_client_options_t *options)
Initialize a rcl client.
RCL_PUBLIC RCL_WARN_UNUSED rcl_client_options_t rcl_client_get_default_options(void)
Return the default client options in a rcl_client_options_t.
RCL_PUBLIC RCL_WARN_UNUSED rmw_client_t * rcl_client_get_rmw_handle(const rcl_client_t *client)
Return the rmw client handle.
RCL_PUBLIC RCL_WARN_UNUSED rcl_client_t rcl_get_zero_initialized_client(void)
Return a rcl_client_t struct with members set to NULL.
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_take_response(const rcl_client_t *client, rmw_request_id_t *request_header, void *ros_response)
backwards compatibility function that takes a rmw_request_id_t only
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_client_set_on_new_response_callback(const rcl_client_t *client, rcl_event_callback_t callback, const void *user_data)
Set the on new response callback function for the client.
RCL_PUBLIC bool rcl_client_is_valid(const rcl_client_t *client)
Check that the client is valid.
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_send_request(const rcl_client_t *client, const void *ros_request, int64_t *sequence_number)
Send a ROS request using a client.
RCL_PUBLIC RCL_WARN_UNUSED const rmw_qos_profile_t * rcl_client_response_subscription_get_actual_qos(const rcl_client_t *client)
Get the actual qos settings of the client's response subscription.
RCL_PUBLIC RCL_WARN_UNUSED const rcl_client_options_t * rcl_client_get_options(const rcl_client_t *client)
Return the rcl client options.
RCL_PUBLIC bool rcl_node_is_valid(const rcl_node_t *node)
Return true if the node is valid, else false.
RCL_PUBLIC RCL_WARN_UNUSED rmw_node_t * rcl_node_get_rmw_handle(const rcl_node_t *node)
Return the rmw node handle.
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_node_resolve_name(const rcl_node_t *node, const char *input_name, rcl_allocator_t allocator, bool is_service, bool only_expand, char **output_name)
Expand a given name into a fully-qualified topic name and apply remapping rules.
RCL_PUBLIC bool rcl_node_is_valid_except_context(const rcl_node_t *node)
Return true if node is valid, except for the context being valid.
Options available for a rcl_client_t.
rcl_allocator_t allocator
Custom allocator for the client, used for incidental allocations.
rmw_qos_profile_t qos
Middleware quality of service settings for the client.
Structure which encapsulates a ROS Client.
rcl_client_impl_t * impl
Pointer to the client implementation.
Encapsulation of a time source.
Structure which encapsulates a ROS Node.
Options available for a rcl publisher.
#define RCL_RET_CLIENT_INVALID
Invalid rcl_client_t given return code.
#define RCL_RET_CLIENT_TAKE_FAILED
Failed to take a response from the client return code.
#define RCL_RET_UNKNOWN_SUBSTITUTION
Topic name substitution is unknown.
#define RCL_RET_SERVICE_NAME_INVALID
Service name (same as topic name) does not pass validation.
#define RCL_RET_ALREADY_INIT
rcl_init() already called return code.
#define RCL_RET_OK
Success return code.
#define RCL_RET_BAD_ALLOC
Failed to allocate memory return code.
#define RCL_RET_INVALID_ARGUMENT
Invalid argument return code.
#define RCL_RET_ERROR
Unspecified error return code.
#define RCL_RET_NODE_INVALID
Invalid rcl_node_t given return code.
rmw_ret_t rcl_ret_t
The type that holds an rcl return code.