ROS 2 rclcpp + rcl - humble  humble
ROS 2 C++ Client Library with ROS Client Library
client.c
1 // Copyright 2016 Open Source Robotics Foundation, Inc.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #ifdef __cplusplus
16 extern "C"
17 {
18 #endif
19 
20 #include "rcl/client.h"
21 
22 #include <stdio.h>
23 #include <string.h>
24 
25 #include "rcl/error_handling.h"
26 #include "rcl/node.h"
27 #include "rcutils/logging_macros.h"
28 #include "rcutils/macros.h"
29 #include "rcutils/stdatomic_helper.h"
30 #include "rmw/error_handling.h"
31 #include "rmw/rmw.h"
32 #include "tracetools/tracetools.h"
33 
34 #include "./common.h"
35 
37 {
38  rcl_client_options_t options;
39  rmw_qos_profile_t actual_request_publisher_qos;
40  rmw_qos_profile_t actual_response_subscription_qos;
41  rmw_client_t * rmw_handle;
42  atomic_int_least64_t sequence_number;
43 };
44 
47 {
48  static rcl_client_t null_client = {0};
49  return null_client;
50 }
51 
54  rcl_client_t * client,
55  const rcl_node_t * node,
56  const rosidl_service_type_support_t * type_support,
57  const char * service_name,
58  const rcl_client_options_t * options)
59 {
60  rcl_ret_t fail_ret = RCL_RET_ERROR;
61 
62  // check the options and allocator first, so the allocator can be passed to errors
63  RCL_CHECK_ARGUMENT_FOR_NULL(options, RCL_RET_INVALID_ARGUMENT);
64  rcl_allocator_t * allocator = (rcl_allocator_t *)&options->allocator;
65  RCL_CHECK_ALLOCATOR_WITH_MSG(allocator, "invalid allocator", return RCL_RET_INVALID_ARGUMENT);
66  RCL_CHECK_ARGUMENT_FOR_NULL(client, RCL_RET_INVALID_ARGUMENT);
67  if (!rcl_node_is_valid(node)) {
68  return RCL_RET_NODE_INVALID; // error already set
69  }
70  RCL_CHECK_ARGUMENT_FOR_NULL(type_support, RCL_RET_INVALID_ARGUMENT);
71  RCL_CHECK_ARGUMENT_FOR_NULL(service_name, RCL_RET_INVALID_ARGUMENT);
72  RCUTILS_LOG_DEBUG_NAMED(
73  ROS_PACKAGE_NAME, "Initializing client for service name '%s'", service_name);
74  if (client->impl) {
75  RCL_SET_ERROR_MSG("client already initialized, or memory was unintialized");
76  return RCL_RET_ALREADY_INIT;
77  }
78 
79  // Expand the given service name.
80  char * remapped_service_name = NULL;
82  node,
83  service_name,
84  *allocator,
85  true,
86  false,
87  &remapped_service_name);
88  if (ret != RCL_RET_OK) {
91  } else if (RCL_RET_BAD_ALLOC != ret) {
92  ret = RCL_RET_ERROR;
93  }
94  goto cleanup;
95  }
96  RCUTILS_LOG_DEBUG_NAMED(
97  ROS_PACKAGE_NAME, "Expanded and remapped service name '%s'", remapped_service_name);
98 
99  // Allocate space for the implementation struct.
100  client->impl = (rcl_client_impl_t *)allocator->allocate(
101  sizeof(rcl_client_impl_t), allocator->state);
102  RCL_CHECK_FOR_NULL_WITH_MSG(
103  client->impl, "allocating memory failed", ret = RCL_RET_BAD_ALLOC; goto cleanup);
104  // Fill out implementation struct.
105  // rmw handle (create rmw client)
106  // TODO(wjwwood): pass along the allocator to rmw when it supports it
107  client->impl->rmw_handle = rmw_create_client(
109  type_support,
110  remapped_service_name,
111  &options->qos);
112  if (!client->impl->rmw_handle) {
113  RCL_SET_ERROR_MSG(rmw_get_error_string().str);
114  goto fail;
115  }
116 
117  // get actual qos, and store it
118  rmw_ret_t rmw_ret = rmw_client_request_publisher_get_actual_qos(
119  client->impl->rmw_handle,
120  &client->impl->actual_request_publisher_qos);
121 
122  if (RMW_RET_OK != rmw_ret) {
123  RCL_SET_ERROR_MSG(rmw_get_error_string().str);
124  goto fail;
125  }
126 
127  rmw_ret = rmw_client_response_subscription_get_actual_qos(
128  client->impl->rmw_handle,
129  &client->impl->actual_response_subscription_qos);
130 
131  if (RMW_RET_OK != rmw_ret) {
132  RCL_SET_ERROR_MSG(rmw_get_error_string().str);
133  goto fail;
134  }
135 
136  // ROS specific namespacing conventions avoidance
137  // is not retrieved by get_actual_qos
138  client->impl->actual_request_publisher_qos.avoid_ros_namespace_conventions =
139  options->qos.avoid_ros_namespace_conventions;
140  client->impl->actual_response_subscription_qos.avoid_ros_namespace_conventions =
141  options->qos.avoid_ros_namespace_conventions;
142 
143  // options
144  client->impl->options = *options;
145  atomic_init(&client->impl->sequence_number, 0);
146  RCUTILS_LOG_DEBUG_NAMED(ROS_PACKAGE_NAME, "Client initialized");
147  ret = RCL_RET_OK;
148  TRACEPOINT(
150  (const void *)client,
151  (const void *)node,
152  (const void *)client->impl->rmw_handle,
153  remapped_service_name);
154  goto cleanup;
155 fail:
156  if (client->impl) {
157  allocator->deallocate(client->impl, allocator->state);
158  client->impl = NULL;
159  }
160  ret = fail_ret;
161  // Fall through to cleanup
162 cleanup:
163  allocator->deallocate(remapped_service_name, allocator->state);
164  return ret;
165 }
166 
167 rcl_ret_t
169 {
170  RCUTILS_CAN_RETURN_WITH_ERROR_OF(RCL_RET_INVALID_ARGUMENT);
171  RCUTILS_CAN_RETURN_WITH_ERROR_OF(RCL_RET_NODE_INVALID);
172  RCUTILS_CAN_RETURN_WITH_ERROR_OF(RCL_RET_ERROR);
173 
174  RCUTILS_LOG_DEBUG_NAMED(ROS_PACKAGE_NAME, "Finalizing client");
175  rcl_ret_t result = RCL_RET_OK;
176  RCL_CHECK_ARGUMENT_FOR_NULL(client, RCL_RET_INVALID_ARGUMENT);
178  return RCL_RET_NODE_INVALID; // error already set
179  }
180  if (client->impl) {
181  rcl_allocator_t allocator = client->impl->options.allocator;
182  rmw_node_t * rmw_node = rcl_node_get_rmw_handle(node);
183  if (!rmw_node) {
185  }
186  rmw_ret_t ret = rmw_destroy_client(rmw_node, client->impl->rmw_handle);
187  if (ret != RMW_RET_OK) {
188  RCL_SET_ERROR_MSG(rmw_get_error_string().str);
189  result = RCL_RET_ERROR;
190  }
191  allocator.deallocate(client->impl, allocator.state);
192  client->impl = NULL;
193  }
194  RCUTILS_LOG_DEBUG_NAMED(ROS_PACKAGE_NAME, "Client finalized");
195  return result;
196 }
197 
200 {
201  // !!! MAKE SURE THAT CHANGES TO THESE DEFAULTS ARE REFLECTED IN THE HEADER DOC STRING
202  static rcl_client_options_t default_options;
203  // Must set the allocator and qos after because they are not a compile time constant.
204  default_options.qos = rmw_qos_profile_services_default;
205  default_options.allocator = rcl_get_default_allocator();
206  return default_options;
207 }
208 
209 const char *
211 {
212  if (!rcl_client_is_valid(client)) {
213  return NULL; // error already set
214  }
215  return client->impl->rmw_handle->service_name;
216 }
217 
218 #define _client_get_options(client) & client->impl->options;
219 
220 const rcl_client_options_t *
222 {
223  if (!rcl_client_is_valid(client)) {
224  return NULL; // error already set
225  }
226  return _client_get_options(client);
227 }
228 
229 rmw_client_t *
231 {
232  if (!rcl_client_is_valid(client)) {
233  return NULL; // error already set
234  }
235  return client->impl->rmw_handle;
236 }
237 
238 rcl_ret_t
239 rcl_send_request(const rcl_client_t * client, const void * ros_request, int64_t * sequence_number)
240 {
241  RCUTILS_LOG_DEBUG_NAMED(ROS_PACKAGE_NAME, "Client sending service request");
242  if (!rcl_client_is_valid(client)) {
243  return RCL_RET_CLIENT_INVALID; // error already set
244  }
245  RCL_CHECK_ARGUMENT_FOR_NULL(ros_request, RCL_RET_INVALID_ARGUMENT);
246  RCL_CHECK_ARGUMENT_FOR_NULL(sequence_number, RCL_RET_INVALID_ARGUMENT);
247  *sequence_number = rcutils_atomic_load_int64_t(&client->impl->sequence_number);
248  if (rmw_send_request(
249  client->impl->rmw_handle, ros_request, sequence_number) != RMW_RET_OK)
250  {
251  RCL_SET_ERROR_MSG(rmw_get_error_string().str);
252  return RCL_RET_ERROR;
253  }
254  rcutils_atomic_exchange_int64_t(&client->impl->sequence_number, *sequence_number);
255  return RCL_RET_OK;
256 }
257 
258 rcl_ret_t
260  const rcl_client_t * client,
261  rmw_service_info_t * request_header,
262  void * ros_response)
263 {
264  RCUTILS_LOG_DEBUG_NAMED(ROS_PACKAGE_NAME, "Client taking service response");
265  if (!rcl_client_is_valid(client)) {
266  return RCL_RET_CLIENT_INVALID; // error already set
267  }
268 
269  RCL_CHECK_ARGUMENT_FOR_NULL(request_header, RCL_RET_INVALID_ARGUMENT);
270  RCL_CHECK_ARGUMENT_FOR_NULL(ros_response, RCL_RET_INVALID_ARGUMENT);
271 
272  bool taken = false;
273  request_header->source_timestamp = 0;
274  request_header->received_timestamp = 0;
275  if (rmw_take_response(
276  client->impl->rmw_handle, request_header, ros_response, &taken) != RMW_RET_OK)
277  {
278  RCL_SET_ERROR_MSG(rmw_get_error_string().str);
279  return RCL_RET_ERROR;
280  }
281  RCUTILS_LOG_DEBUG_NAMED(
282  ROS_PACKAGE_NAME, "Client take response succeeded: %s", taken ? "true" : "false");
283  if (!taken) {
285  }
286  return RCL_RET_OK;
287 }
288 
289 rcl_ret_t
291  const rcl_client_t * client,
292  rmw_request_id_t * request_header,
293  void * ros_response)
294 {
295  rmw_service_info_t header;
296  header.request_id = *request_header;
297  rcl_ret_t ret = rcl_take_response_with_info(client, &header, ros_response);
298  *request_header = header.request_id;
299  return ret;
300 }
301 
302 bool
304 {
305  RCL_CHECK_FOR_NULL_WITH_MSG(client, "client pointer is invalid", return false);
306  RCL_CHECK_FOR_NULL_WITH_MSG(
307  client->impl, "client's rmw implementation is invalid", return false);
308  RCL_CHECK_FOR_NULL_WITH_MSG(
309  client->impl->rmw_handle, "client's rmw handle is invalid", return false);
310  return true;
311 }
312 
313 const rmw_qos_profile_t *
315 {
316  if (!rcl_client_is_valid(client)) {
317  return NULL;
318  }
319  return &client->impl->actual_request_publisher_qos;
320 }
321 
322 const rmw_qos_profile_t *
324 {
325  if (!rcl_client_is_valid(client)) {
326  return NULL;
327  }
328  return &client->impl->actual_response_subscription_qos;
329 }
330 
331 rcl_ret_t
333  const rcl_client_t * client,
334  rcl_event_callback_t callback,
335  const void * user_data)
336 {
337  if (!rcl_client_is_valid(client)) {
338  // error state already set
340  }
341 
342  return rmw_client_set_on_new_response_callback(
343  client->impl->rmw_handle,
344  callback,
345  user_data);
346 }
347 
348 #ifdef __cplusplus
349 }
350 #endif
#define rcl_get_default_allocator
Return a properly initialized rcl_allocator_t with default values.
Definition: allocator.h:37
#define RCL_CHECK_ALLOCATOR_WITH_MSG(allocator, msg, fail_statement)
Check that the given allocator is initialized, or fail with a message.
Definition: allocator.h:56
rcutils_allocator_t rcl_allocator_t
Encapsulation of an allocator.
Definition: allocator.h:31
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.
Definition: client.c:259
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.
Definition: client.c:314
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.
Definition: client.c:210
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_client_fini(rcl_client_t *client, rcl_node_t *node)
Finalize a rcl_client_t.
Definition: client.c:168
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.
Definition: client.c:53
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.
Definition: client.c:199
RCL_PUBLIC RCL_WARN_UNUSED rmw_client_t * rcl_client_get_rmw_handle(const rcl_client_t *client)
Return the rmw client handle.
Definition: client.c:230
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.
Definition: client.c:46
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
Definition: client.c:290
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.
Definition: client.c:332
RCL_PUBLIC bool rcl_client_is_valid(const rcl_client_t *client)
Check that the client is valid.
Definition: client.c:303
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.
Definition: client.c:239
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.
Definition: client.c:323
RCL_PUBLIC RCL_WARN_UNUSED const rcl_client_options_t * rcl_client_get_options(const rcl_client_t *client)
Return the rcl client options.
Definition: client.c:221
RCL_PUBLIC bool rcl_node_is_valid(const rcl_node_t *node)
Return true if the node is valid, else false.
Definition: node.c:421
RCL_PUBLIC RCL_WARN_UNUSED rmw_node_t * rcl_node_get_rmw_handle(const rcl_node_t *node)
Return the rmw node handle.
Definition: node.c:485
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.
Definition: node.c:411
Options available for a rcl_client_t.
Definition: client.h:44
rcl_allocator_t allocator
Custom allocator for the client, used for incidental allocations.
Definition: client.h:49
rmw_qos_profile_t qos
Middleware quality of service settings for the client.
Definition: client.h:46
Structure which encapsulates a ROS Client.
Definition: client.h:37
rcl_client_impl_t * impl
Pointer to the client implementation.
Definition: client.h:39
Structure which encapsulates a ROS Node.
Definition: node.h:42
#define RCL_RET_CLIENT_INVALID
Invalid rcl_client_t given return code.
Definition: types.h:76
#define RCL_RET_CLIENT_TAKE_FAILED
Failed to take a response from the client return code.
Definition: types.h:78
#define RCL_RET_UNKNOWN_SUBSTITUTION
Topic name substitution is unknown.
Definition: types.h:50
#define RCL_RET_SERVICE_NAME_INVALID
Service name (same as topic name) does not pass validation.
Definition: types.h:48
#define RCL_RET_ALREADY_INIT
rcl_init() already called return code.
Definition: types.h:40
#define RCL_RET_OK
Success return code.
Definition: types.h:26
#define RCL_RET_BAD_ALLOC
Failed to allocate memory return code.
Definition: types.h:32
#define RCL_RET_INVALID_ARGUMENT
Invalid argument return code.
Definition: types.h:34
#define RCL_RET_ERROR
Unspecified error return code.
Definition: types.h:28
#define RCL_RET_NODE_INVALID
Invalid rcl_node_t given return code.
Definition: types.h:56
rmw_ret_t rcl_ret_t
The type that holds an rcl return code.
Definition: types.h:23