ROS 2 rclcpp + rcl - lyrical  lyrical
ROS 2 C++ Client Library with ROS Client Library
graph.c
1 // Copyright 2016-2017 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 <stddef.h>
21 
22 #include "rcl/graph.h"
23 #include "rcl/error_handling.h"
24 #include "rcl/guard_condition.h"
25 #include "rcl/wait.h"
26 
27 #include "rcutils/allocator.h"
28 #include "rcutils/error_handling.h"
29 #include "rcutils/macros.h"
30 #include "rcutils/time.h"
31 #include "rcutils/types.h"
32 
33 #include "rmw/error_handling.h"
34 #include "rmw/get_node_info_and_types.h"
35 #include "rmw/get_service_endpoint_info.h"
36 #include "rmw/get_service_names_and_types.h"
37 #include "rmw/get_topic_endpoint_info.h"
38 #include "rmw/get_topic_names_and_types.h"
39 #include "rmw/names_and_types.h"
40 #include "rmw/rmw.h"
41 #include "rmw/service_endpoint_info_array.h"
42 #include "rmw/topic_endpoint_info_array.h"
43 #include "rmw/validate_namespace.h"
44 #include "rmw/validate_node_name.h"
45 
46 #include "./common.h"
47 
49 __validate_node_name_and_namespace(
50  const char * node_name,
51  const char * node_namespace)
52 {
53  int validation_result = 0;
54  rmw_ret_t rmw_ret = rmw_validate_namespace(node_namespace, &validation_result, NULL);
55 
56  if (RMW_RET_OK != rmw_ret) {
57  RCL_SET_ERROR_MSG(rmw_get_error_string().str);
58  return rcl_convert_rmw_ret_to_rcl_ret(rmw_ret);
59  }
60  if (validation_result != RMW_NAMESPACE_VALID) {
61  const char * msg = rmw_namespace_validation_result_string(validation_result);
62  RCL_SET_ERROR_MSG_WITH_FORMAT_STRING("%s, result: %d", msg, validation_result);
64  }
65 
66  validation_result = 0;
67  rmw_ret = rmw_validate_node_name(node_name, &validation_result, NULL);
68  if (RMW_RET_OK != rmw_ret) {
69  RCL_SET_ERROR_MSG(rmw_get_error_string().str);
70  return rcl_convert_rmw_ret_to_rcl_ret(rmw_ret);
71  }
72  if (RMW_NODE_NAME_VALID != validation_result) {
73  const char * msg = rmw_node_name_validation_result_string(validation_result);
74  RCL_SET_ERROR_MSG_WITH_FORMAT_STRING("%s, result: %d", msg, validation_result);
76  }
77 
78  return RCL_RET_OK;
79 }
80 
83  const rcl_node_t * node,
84  rcl_allocator_t * allocator,
85  bool no_demangle,
86  const char * node_name,
87  const char * node_namespace,
88  rcl_names_and_types_t * topic_names_and_types)
89 {
90  if (!rcl_node_is_valid(node)) {
91  return RCL_RET_NODE_INVALID;
92  }
93  RCL_CHECK_ALLOCATOR_WITH_MSG(allocator, "invalid allocator", return RCL_RET_INVALID_ARGUMENT);
94  RCL_CHECK_ARGUMENT_FOR_NULL(node_name, RCL_RET_INVALID_ARGUMENT);
95  RCL_CHECK_ARGUMENT_FOR_NULL(node_namespace, RCL_RET_INVALID_ARGUMENT);
96  RCL_CHECK_ARGUMENT_FOR_NULL(topic_names_and_types, RCL_RET_INVALID_ARGUMENT);
97 
98  const char * valid_namespace = "/";
99  if (strlen(node_namespace) > 0) {
100  valid_namespace = node_namespace;
101  }
102  rmw_ret_t rmw_ret = rmw_names_and_types_check_zero(topic_names_and_types);
103  if (RMW_RET_OK != rmw_ret) {
104  return rcl_convert_rmw_ret_to_rcl_ret(rmw_ret);
105  }
106  rcl_ret_t rcl_ret = __validate_node_name_and_namespace(node_name, valid_namespace);
107  if (RCL_RET_OK != rcl_ret) {
108  return rcl_ret;
109  }
110  rcutils_allocator_t rcutils_allocator = *allocator;
111  rmw_ret = rmw_get_publisher_names_and_types_by_node(
113  &rcutils_allocator,
114  node_name,
115  valid_namespace,
116  no_demangle,
117  topic_names_and_types
118  );
119  return rcl_convert_rmw_ret_to_rcl_ret(rmw_ret);
120 }
121 
122 rcl_ret_t
124  const rcl_node_t * node,
125  rcl_allocator_t * allocator,
126  bool no_demangle,
127  const char * node_name,
128  const char * node_namespace,
129  rcl_names_and_types_t * topic_names_and_types)
130 {
131  if (!rcl_node_is_valid(node)) {
132  return RCL_RET_NODE_INVALID;
133  }
134  RCL_CHECK_ALLOCATOR_WITH_MSG(allocator, "invalid allocator", return RCL_RET_INVALID_ARGUMENT);
135  RCL_CHECK_ARGUMENT_FOR_NULL(node_name, RCL_RET_INVALID_ARGUMENT);
136  RCL_CHECK_ARGUMENT_FOR_NULL(node_namespace, RCL_RET_INVALID_ARGUMENT);
137  RCL_CHECK_ARGUMENT_FOR_NULL(topic_names_and_types, RCL_RET_INVALID_ARGUMENT);
138 
139  const char * valid_namespace = "/";
140  if (strlen(node_namespace) > 0) {
141  valid_namespace = node_namespace;
142  }
143  rmw_ret_t rmw_ret;
144  rmw_ret = rmw_names_and_types_check_zero(topic_names_and_types);
145  if (rmw_ret != RMW_RET_OK) {
146  return rcl_convert_rmw_ret_to_rcl_ret(rmw_ret);
147  }
148  rcutils_allocator_t rcutils_allocator = *allocator;
149  rcl_ret_t rcl_ret = __validate_node_name_and_namespace(node_name, valid_namespace);
150  if (RCL_RET_OK != rcl_ret) {
151  return rcl_ret;
152  }
153  rmw_ret = rmw_get_subscriber_names_and_types_by_node(
155  &rcutils_allocator,
156  node_name,
157  valid_namespace,
158  no_demangle,
159  topic_names_and_types
160  );
161  return rcl_convert_rmw_ret_to_rcl_ret(rmw_ret);
162 }
163 
164 rcl_ret_t
166  const rcl_node_t * node,
167  rcl_allocator_t * allocator,
168  const char * node_name,
169  const char * node_namespace,
170  rcl_names_and_types_t * service_names_and_types)
171 {
172  if (!rcl_node_is_valid(node)) {
173  return RCL_RET_NODE_INVALID;
174  }
175  RCL_CHECK_ALLOCATOR_WITH_MSG(allocator, "invalid allocator", return RCL_RET_INVALID_ARGUMENT);
176  RCL_CHECK_ARGUMENT_FOR_NULL(node_name, RCL_RET_INVALID_ARGUMENT);
177  RCL_CHECK_ARGUMENT_FOR_NULL(node_namespace, RCL_RET_INVALID_ARGUMENT);
178  RCL_CHECK_ARGUMENT_FOR_NULL(service_names_and_types, RCL_RET_INVALID_ARGUMENT);
179 
180  const char * valid_namespace = "/";
181  if (strlen(node_namespace) > 0) {
182  valid_namespace = node_namespace;
183  }
184  rmw_ret_t rmw_ret;
185  rmw_ret = rmw_names_and_types_check_zero(service_names_and_types);
186  if (rmw_ret != RMW_RET_OK) {
187  return rcl_convert_rmw_ret_to_rcl_ret(rmw_ret);
188  }
189  rcl_ret_t rcl_ret = __validate_node_name_and_namespace(node_name, valid_namespace);
190  if (RCL_RET_OK != rcl_ret) {
191  return rcl_ret;
192  }
193  rcutils_allocator_t rcutils_allocator = *allocator;
194  rmw_ret = rmw_get_service_names_and_types_by_node(
196  &rcutils_allocator,
197  node_name,
198  valid_namespace,
199  service_names_and_types
200  );
201  return rcl_convert_rmw_ret_to_rcl_ret(rmw_ret);
202 }
203 
204 rcl_ret_t
206  const rcl_node_t * node,
207  rcl_allocator_t * allocator,
208  const char * node_name,
209  const char * node_namespace,
210  rcl_names_and_types_t * service_names_and_types)
211 {
212  if (!rcl_node_is_valid(node)) {
213  return RCL_RET_NODE_INVALID;
214  }
215  RCL_CHECK_ALLOCATOR_WITH_MSG(allocator, "invalid allocator", return RCL_RET_INVALID_ARGUMENT);
216  RCL_CHECK_ARGUMENT_FOR_NULL(node_name, RCL_RET_INVALID_ARGUMENT);
217  RCL_CHECK_ARGUMENT_FOR_NULL(node_namespace, RCL_RET_INVALID_ARGUMENT);
218  RCL_CHECK_ARGUMENT_FOR_NULL(service_names_and_types, RCL_RET_INVALID_ARGUMENT);
219 
220  const char * valid_namespace = "/";
221  if (strlen(node_namespace) > 0) {
222  valid_namespace = node_namespace;
223  }
224  rmw_ret_t rmw_ret;
225  rmw_ret = rmw_names_and_types_check_zero(service_names_and_types);
226  if (rmw_ret != RMW_RET_OK) {
227  return rcl_convert_rmw_ret_to_rcl_ret(rmw_ret);
228  }
229  rcl_ret_t rcl_ret = __validate_node_name_and_namespace(node_name, valid_namespace);
230  if (RCL_RET_OK != rcl_ret) {
231  return rcl_ret;
232  }
233  rcutils_allocator_t rcutils_allocator = *allocator;
234  rmw_ret = rmw_get_client_names_and_types_by_node(
236  &rcutils_allocator,
237  node_name,
238  valid_namespace,
239  service_names_and_types
240  );
241  return rcl_convert_rmw_ret_to_rcl_ret(rmw_ret);
242 }
243 
244 rcl_ret_t
246  const rcl_node_t * node,
247  rcl_allocator_t * allocator,
248  bool no_demangle,
249  rcl_names_and_types_t * topic_names_and_types)
250 {
251  if (!rcl_node_is_valid(node)) {
252  return RCL_RET_NODE_INVALID; // error already set
253  }
254  RCL_CHECK_ALLOCATOR_WITH_MSG(allocator, "invalid allocator", return RCL_RET_INVALID_ARGUMENT);
255  RCL_CHECK_ARGUMENT_FOR_NULL(topic_names_and_types, RCL_RET_INVALID_ARGUMENT);
256  rmw_ret_t rmw_ret;
257  rmw_ret = rmw_names_and_types_check_zero(topic_names_and_types);
258  if (rmw_ret != RMW_RET_OK) {
259  return rcl_convert_rmw_ret_to_rcl_ret(rmw_ret);
260  }
261  rcutils_allocator_t rcutils_allocator = *allocator;
262  rmw_ret = rmw_get_topic_names_and_types(
264  &rcutils_allocator,
265  no_demangle,
266  topic_names_and_types
267  );
268  return rcl_convert_rmw_ret_to_rcl_ret(rmw_ret);
269 }
270 
271 rcl_ret_t
273  const rcl_node_t * node,
274  rcl_allocator_t * allocator,
275  rcl_names_and_types_t * service_names_and_types)
276 {
277  if (!rcl_node_is_valid(node)) {
278  return RCL_RET_NODE_INVALID; // error already set
279  }
280  RCL_CHECK_ALLOCATOR_WITH_MSG(allocator, "invalid allocator", return RCL_RET_INVALID_ARGUMENT);
281  RCL_CHECK_ARGUMENT_FOR_NULL(service_names_and_types, RCL_RET_INVALID_ARGUMENT);
282  rmw_ret_t rmw_ret;
283  rmw_ret = rmw_names_and_types_check_zero(service_names_and_types);
284  if (rmw_ret != RMW_RET_OK) {
285  return rcl_convert_rmw_ret_to_rcl_ret(rmw_ret);
286  }
287  rcutils_allocator_t rcutils_allocator = *allocator;
288  rmw_ret = rmw_get_service_names_and_types(
290  &rcutils_allocator,
291  service_names_and_types
292  );
293  return rcl_convert_rmw_ret_to_rcl_ret(rmw_ret);
294 }
295 
296 rcl_ret_t
298  rcl_names_and_types_t * names_and_types,
299  size_t size,
300  rcl_allocator_t * allocator)
301 {
302  RCUTILS_CAN_RETURN_WITH_ERROR_OF(RCL_RET_INVALID_ARGUMENT);
303 
304  RCL_CHECK_ARGUMENT_FOR_NULL(names_and_types, RCL_RET_INVALID_ARGUMENT);
306  rmw_ret_t rmw_ret = rmw_names_and_types_init(names_and_types, size, allocator);
307  return rcl_convert_rmw_ret_to_rcl_ret(rmw_ret);
308 }
309 
310 rcl_ret_t
312 {
313  RCUTILS_CAN_RETURN_WITH_ERROR_OF(RCL_RET_INVALID_ARGUMENT);
314 
315  RCL_CHECK_ARGUMENT_FOR_NULL(topic_names_and_types, RCL_RET_INVALID_ARGUMENT);
316  rmw_ret_t rmw_ret = rmw_names_and_types_fini(topic_names_and_types);
317  return rcl_convert_rmw_ret_to_rcl_ret(rmw_ret);
318 }
319 
320 rcl_ret_t
322  const rcl_node_t * node,
323  rcl_allocator_t allocator,
324  rcutils_string_array_t * node_names,
325  rcutils_string_array_t * node_namespaces)
326 {
327  if (!rcl_node_is_valid(node)) {
328  return RCL_RET_NODE_INVALID; // error already set
329  }
330  RCL_CHECK_ARGUMENT_FOR_NULL(node_names, RCL_RET_INVALID_ARGUMENT);
331  if (node_names->size != 0) {
332  RCL_SET_ERROR_MSG("node_names size is not zero");
334  }
335  if (node_names->data) {
336  RCL_SET_ERROR_MSG("node_names is not null");
338  }
339  RCL_CHECK_ARGUMENT_FOR_NULL(node_namespaces, RCL_RET_INVALID_ARGUMENT);
340  if (node_namespaces->size != 0) {
341  RCL_SET_ERROR_MSG("node_namespaces size is not zero");
343  }
344  if (node_namespaces->data) {
345  RCL_SET_ERROR_MSG("node_namespaces is not null");
347  }
348  (void)allocator; // to be used in rmw_get_node_names in the future
349  rmw_ret_t rmw_ret = rmw_get_node_names(
351  node_names,
352  node_namespaces);
353 
354  if (RMW_RET_OK != rmw_ret) {
355  return rcl_convert_rmw_ret_to_rcl_ret(rmw_ret);
356  }
357 
358  // Check that none of the node names are NULL or empty
359  for (size_t i = 0u; i < node_names->size; ++i) {
360  if (!node_names->data[i]) {
361  RCL_SET_ERROR_MSG("NULL node name returned by the RMW layer");
363  }
364  if (!strcmp(node_names->data[i], "")) {
365  RCL_SET_ERROR_MSG("empty node name returned by the RMW layer");
367  }
368  }
369  // Check that none of the node namespaces are NULL
370  for (size_t i = 0u; i < node_namespaces->size; ++i) {
371  if (!node_namespaces->data[i]) {
372  RCL_SET_ERROR_MSG("NULL node namespace returned by the RMW layer");
374  }
375  }
376  return RCL_RET_OK;
377 }
378 
379 rcl_ret_t
381  const rcl_node_t * node,
382  rcl_allocator_t allocator,
383  rcutils_string_array_t * node_names,
384  rcutils_string_array_t * node_namespaces,
385  rcutils_string_array_t * enclaves)
386 {
387  if (!rcl_node_is_valid(node)) {
388  return RCL_RET_NODE_INVALID; // error already set
389  }
390  RCL_CHECK_ARGUMENT_FOR_NULL(node_names, RCL_RET_INVALID_ARGUMENT);
391  if (node_names->size != 0) {
392  RCL_SET_ERROR_MSG("node_names size is not zero");
394  }
395  if (node_names->data) {
396  RCL_SET_ERROR_MSG("node_names is not null");
398  }
399  RCL_CHECK_ARGUMENT_FOR_NULL(node_namespaces, RCL_RET_INVALID_ARGUMENT);
400  if (node_namespaces->size != 0) {
401  RCL_SET_ERROR_MSG("node_namespaces size is not zero");
403  }
404  if (node_namespaces->data) {
405  RCL_SET_ERROR_MSG("node_namespaces is not null");
407  }
408  RCL_CHECK_ARGUMENT_FOR_NULL(enclaves, RCL_RET_INVALID_ARGUMENT);
409  if (enclaves->size != 0) {
410  RCL_SET_ERROR_MSG("enclaves size is not zero");
412  }
413  if (enclaves->data) {
414  RCL_SET_ERROR_MSG("enclaves is not null");
416  }
417  (void)allocator; // to be used in rmw_get_node_names in the future
418  rmw_ret_t rmw_ret = rmw_get_node_names_with_enclaves(
420  node_names,
421  node_namespaces,
422  enclaves);
423  return rcl_convert_rmw_ret_to_rcl_ret(rmw_ret);
424 }
425 
426 rcl_ret_t
428  const rcl_node_t * node,
429  const char * topic_name,
430  size_t * count)
431 {
432  if (!rcl_node_is_valid(node)) {
433  return RCL_RET_NODE_INVALID; // error already set
434  }
435  const rcl_node_options_t * node_options = rcl_node_get_options(node);
436  if (!node_options) {
437  return RCL_RET_NODE_INVALID; // shouldn't happen, but error is already set if so
438  }
439  RCL_CHECK_ARGUMENT_FOR_NULL(topic_name, RCL_RET_INVALID_ARGUMENT);
440  RCL_CHECK_ARGUMENT_FOR_NULL(count, RCL_RET_INVALID_ARGUMENT);
441  rmw_ret_t rmw_ret = rmw_count_publishers(rcl_node_get_rmw_handle(node), topic_name, count);
442  return rcl_convert_rmw_ret_to_rcl_ret(rmw_ret);
443 }
444 
445 rcl_ret_t
447  const rcl_node_t * node,
448  const char * topic_name,
449  size_t * count)
450 {
451  if (!rcl_node_is_valid(node)) {
452  return RCL_RET_NODE_INVALID; // error already set
453  }
454  const rcl_node_options_t * node_options = rcl_node_get_options(node);
455  if (!node_options) {
456  return RCL_RET_NODE_INVALID; // shouldn't happen, but error is already set if so
457  }
458  RCL_CHECK_ARGUMENT_FOR_NULL(topic_name, RCL_RET_INVALID_ARGUMENT);
459  RCL_CHECK_ARGUMENT_FOR_NULL(count, RCL_RET_INVALID_ARGUMENT);
460  rmw_ret_t rmw_ret = rmw_count_subscribers(rcl_node_get_rmw_handle(node), topic_name, count);
461  return rcl_convert_rmw_ret_to_rcl_ret(rmw_ret);
462 }
463 
464 rcl_ret_t
466  const rcl_node_t * node,
467  const char * service_name,
468  size_t * count)
469 {
470  if (!rcl_node_is_valid(node)) {
471  return RCL_RET_NODE_INVALID; // error already set
472  }
473  const rcl_node_options_t * node_options = rcl_node_get_options(node);
474  if (!node_options) {
475  return RCL_RET_NODE_INVALID; // shouldn't happen, but error is already set if so
476  }
477  RCL_CHECK_ARGUMENT_FOR_NULL(service_name, RCL_RET_INVALID_ARGUMENT);
478  RCL_CHECK_ARGUMENT_FOR_NULL(count, RCL_RET_INVALID_ARGUMENT);
479  rmw_ret_t rmw_ret = rmw_count_clients(rcl_node_get_rmw_handle(node), service_name, count);
480  return rcl_convert_rmw_ret_to_rcl_ret(rmw_ret);
481 }
482 
483 rcl_ret_t
485  const rcl_node_t * node,
486  const char * service_name,
487  size_t * count)
488 {
489  if (!rcl_node_is_valid(node)) {
490  return RCL_RET_NODE_INVALID; // error already set
491  }
492  const rcl_node_options_t * node_options = rcl_node_get_options(node);
493  if (!node_options) {
494  return RCL_RET_NODE_INVALID; // shouldn't happen, but error is already set if so
495  }
496  RCL_CHECK_ARGUMENT_FOR_NULL(service_name, RCL_RET_INVALID_ARGUMENT);
497  RCL_CHECK_ARGUMENT_FOR_NULL(count, RCL_RET_INVALID_ARGUMENT);
498  rmw_ret_t rmw_ret = rmw_count_services(rcl_node_get_rmw_handle(node), service_name, count);
499  return rcl_convert_rmw_ret_to_rcl_ret(rmw_ret);
500 }
501 
502 typedef rcl_ret_t (* count_entities_func_t)(
503  const rcl_node_t * node,
504  const char * topic_name,
505  size_t * count);
506 
507 rcl_ret_t
508 _rcl_wait_for_entities(
509  const rcl_node_t * node,
510  rcl_allocator_t * allocator,
511  const char * topic_name,
512  const size_t expected_count,
513  rcutils_duration_value_t timeout,
514  bool * success,
515  count_entities_func_t count_entities_func)
516 {
517  if (!rcl_node_is_valid(node)) {
518  return RCL_RET_NODE_INVALID;
519  }
520  RCL_CHECK_ALLOCATOR_WITH_MSG(allocator, "invalid allocator", return RCL_RET_INVALID_ARGUMENT);
521  RCL_CHECK_ARGUMENT_FOR_NULL(topic_name, RCL_RET_INVALID_ARGUMENT);
522  RCL_CHECK_ARGUMENT_FOR_NULL(success, RCL_RET_INVALID_ARGUMENT);
523 
524  rcl_ret_t ret = RCL_RET_OK;
525  *success = false;
526 
527  // We can avoid waiting if there are already the expected number of entities
528  size_t count = 0u;
529  ret = count_entities_func(node, topic_name, &count);
530  if (ret != RCL_RET_OK) {
531  // Error message already set
532  return ret;
533  }
534  if (expected_count <= count) {
535  *success = true;
536  return RCL_RET_OK;
537  }
538 
539  // Create a wait set and add the node graph guard condition to it
541  ret = rcl_wait_set_init(
542  &wait_set, 0, 1, 0, 0, 0, 0, node->context, *allocator);
543  if (ret != RCL_RET_OK) {
544  // Error message already set
545  return ret;
546  }
547 
548  const rcl_guard_condition_t * guard_condition = rcl_node_get_graph_guard_condition(node);
549  if (!guard_condition) {
550  // Error message already set
551  ret = RCL_RET_ERROR;
552  goto cleanup;
553  }
554 
555  // Add it to the wait set
556  ret = rcl_wait_set_add_guard_condition(&wait_set, guard_condition, NULL);
557  if (ret != RCL_RET_OK) {
558  // Error message already set
559  goto cleanup;
560  }
561 
562  // Get current time
563  // We use system time to be consistent with the clock used by rcl_wait()
564  rcutils_time_point_value_t start;
565  rcutils_ret_t time_ret = rcutils_system_time_now(&start);
566  if (time_ret != RCUTILS_RET_OK) {
567  rcutils_error_string_t error = rcutils_get_error_string();
568  rcutils_reset_error();
569  RCL_SET_ERROR_MSG(error.str);
570  ret = RCL_RET_ERROR;
571  goto cleanup;
572  }
573 
574  // Wait for expected count or timeout
575  rcl_ret_t wait_ret;
576  while (true) {
577  // Use separate 'wait_ret' code to avoid returning spurious TIMEOUT value
578  wait_ret = rcl_wait(&wait_set, timeout);
579  if (wait_ret != RCL_RET_OK && wait_ret != RCL_RET_TIMEOUT) {
580  // Error message already set
581  ret = wait_ret;
582  break;
583  }
584 
585  // Check count
586  ret = count_entities_func(node, topic_name, &count);
587  if (ret != RCL_RET_OK) {
588  // Error already set
589  break;
590  }
591  if (expected_count <= count) {
592  *success = true;
593  break;
594  }
595 
596  // If we're not waiting indefinitely, compute time remaining
597  if (timeout >= 0) {
598  rcutils_time_point_value_t now;
599  time_ret = rcutils_system_time_now(&now);
600  if (time_ret != RCUTILS_RET_OK) {
601  rcutils_error_string_t error = rcutils_get_error_string();
602  rcutils_reset_error();
603  RCL_SET_ERROR_MSG(error.str);
604  ret = RCL_RET_ERROR;
605  break;
606  }
607  timeout = timeout - (now - start);
608  if (timeout <= 0) {
609  ret = RCL_RET_TIMEOUT;
610  break;
611  }
612  }
613 
614  // Clear wait set for next iteration
615  ret = rcl_wait_set_clear(&wait_set);
616  if (ret != RCL_RET_OK) {
617  // Error message already set
618  break;
619  }
620  }
621 
622  rcl_ret_t cleanup_ret;
623 cleanup:
624  // Cleanup
625  cleanup_ret = rcl_wait_set_fini(&wait_set);
626  if (cleanup_ret != RCL_RET_OK) {
627  // If we got two unexpected errors, return the earlier error
628  if (ret != RCL_RET_OK && ret != RCL_RET_TIMEOUT) {
629  // Error message already set
630  ret = cleanup_ret;
631  }
632  }
633 
634  return ret;
635 }
636 
637 rcl_ret_t
639  const rcl_node_t * node,
640  rcl_allocator_t * allocator,
641  const char * topic_name,
642  const size_t expected_count,
643  rcutils_duration_value_t timeout,
644  bool * success)
645 {
646  return _rcl_wait_for_entities(
647  node,
648  allocator,
649  topic_name,
650  expected_count,
651  timeout,
652  success,
654 }
655 
656 rcl_ret_t
658  const rcl_node_t * node,
659  rcl_allocator_t * allocator,
660  const char * topic_name,
661  const size_t expected_count,
662  rcutils_duration_value_t timeout,
663  bool * success)
664 {
665  return _rcl_wait_for_entities(
666  node,
667  allocator,
668  topic_name,
669  expected_count,
670  timeout,
671  success,
673 }
674 
675 
676 rcl_ret_t
678  const rcl_node_t * node,
679  rcl_allocator_t * allocator,
680  const char * service_name,
681  const size_t expected_count,
682  rcutils_duration_value_t timeout,
683  bool * success)
684 {
685  return _rcl_wait_for_entities(
686  node,
687  allocator,
688  service_name,
689  expected_count,
690  timeout,
691  success,
693 }
694 
695 rcl_ret_t
697  const rcl_node_t * node,
698  rcl_allocator_t * allocator,
699  const char * service_name,
700  const size_t expected_count,
701  rcutils_duration_value_t timeout,
702  bool * success)
703 {
704  return _rcl_wait_for_entities(
705  node,
706  allocator,
707  service_name,
708  expected_count,
709  timeout,
710  success,
712 }
713 
714 typedef rmw_ret_t (* get_topic_endpoint_info_func_t)(
715  const rmw_node_t * node,
716  rcutils_allocator_t * allocator,
717  const char * topic_name,
718  bool no_mangle,
719  rmw_topic_endpoint_info_array_t * info_array);
720 
721 rcl_ret_t
722 __rcl_get_info_by_topic(
723  const rcl_node_t * node,
724  rcutils_allocator_t * allocator,
725  const char * topic_name,
726  bool no_mangle,
727  rmw_topic_endpoint_info_array_t * info_array,
728  get_topic_endpoint_info_func_t get_topic_endpoint_info)
729 {
730  if (!rcl_node_is_valid(node)) {
731  return RCL_RET_NODE_INVALID; // error already set.
732  }
733  const rcl_node_options_t * node_options = rcl_node_get_options(node);
734  if (!node_options) {
735  return RCL_RET_NODE_INVALID; // shouldn't happen, but error is already set if so
736  }
737  RCL_CHECK_ALLOCATOR_WITH_MSG(allocator, "invalid allocator", return RCL_RET_INVALID_ARGUMENT);
738  RCL_CHECK_ARGUMENT_FOR_NULL(topic_name, RCL_RET_INVALID_ARGUMENT);
739  rmw_error_string_t error_string;
740  rmw_ret_t rmw_ret = rmw_topic_endpoint_info_array_check_zero(info_array);
741  if (rmw_ret != RMW_RET_OK) {
742  error_string = rmw_get_error_string();
743  rmw_reset_error();
744  RCL_SET_ERROR_MSG_WITH_FORMAT_STRING(
745  "rmw_topic_endpoint_info_array_t must be zero initialized: %s,\n"
746  "Use rmw_get_zero_initialized_topic_endpoint_info_array",
747  error_string.str);
748  return rcl_convert_rmw_ret_to_rcl_ret(rmw_ret);
749  }
750  rmw_ret = get_topic_endpoint_info(
752  allocator,
753  topic_name,
754  no_mangle,
755  info_array);
756  if (rmw_ret != RMW_RET_OK) {
757  error_string = rmw_get_error_string();
758  rmw_reset_error();
759  RCL_SET_ERROR_MSG(error_string.str);
760  }
761  return rcl_convert_rmw_ret_to_rcl_ret(rmw_ret);
762 }
763 
764 rcl_ret_t
766  const rcl_node_t * node,
767  rcutils_allocator_t * allocator,
768  const char * topic_name,
769  bool no_mangle,
770  rmw_topic_endpoint_info_array_t * publishers_info)
771 {
772  return __rcl_get_info_by_topic(
773  node,
774  allocator,
775  topic_name,
776  no_mangle,
777  publishers_info,
778  rmw_get_publishers_info_by_topic);
779 }
780 
781 rcl_ret_t
783  const rcl_node_t * node,
784  rcutils_allocator_t * allocator,
785  const char * topic_name,
786  bool no_mangle,
787  rmw_topic_endpoint_info_array_t * subscriptions_info)
788 {
789  return __rcl_get_info_by_topic(
790  node,
791  allocator,
792  topic_name,
793  no_mangle,
794  subscriptions_info,
795  rmw_get_subscriptions_info_by_topic);
796 }
797 
798 typedef rmw_ret_t (* get_service_endpoint_info_func_t)(
799  const rmw_node_t * node,
800  rcutils_allocator_t * allocator,
801  const char * service_name,
802  bool no_mangle,
803  rmw_service_endpoint_info_array_t * info_array);
804 
805 rcl_ret_t
806 __rcl_get_info_by_service(
807  const rcl_node_t * node,
808  rcutils_allocator_t * allocator,
809  const char * service_name,
810  bool no_mangle,
811  rmw_service_endpoint_info_array_t * info_array,
812  get_service_endpoint_info_func_t get_service_endpoint_info)
813 {
814  if (!rcl_node_is_valid(node)) {
815  return RCL_RET_NODE_INVALID; // error already set.
816  }
817  const rcl_node_options_t * node_options = rcl_node_get_options(node);
818  if (!node_options) {
819  return RCL_RET_NODE_INVALID; // shouldn't happen, but error is already set if so
820  }
821  RCL_CHECK_ALLOCATOR_WITH_MSG(allocator, "invalid allocator", return RCL_RET_INVALID_ARGUMENT);
822  RCL_CHECK_ARGUMENT_FOR_NULL(service_name, RCL_RET_INVALID_ARGUMENT);
823  rmw_error_string_t error_string;
824  rmw_ret_t rmw_ret = rmw_service_endpoint_info_array_check_zero(info_array);
825  if (rmw_ret != RMW_RET_OK) {
826  error_string = rmw_get_error_string();
827  rmw_reset_error();
828  RCL_SET_ERROR_MSG_WITH_FORMAT_STRING(
829  "rmw_service_endpoint_info_array_t must be zero initialized: %s,\n"
830  "Use rmw_get_zero_initialized_service_endpoint_info_array",
831  error_string.str);
832  return rcl_convert_rmw_ret_to_rcl_ret(rmw_ret);
833  }
834  rmw_ret = get_service_endpoint_info(
836  allocator,
837  service_name,
838  no_mangle,
839  info_array);
840  if (rmw_ret != RMW_RET_OK) {
841  error_string = rmw_get_error_string();
842  rmw_reset_error();
843  RCL_SET_ERROR_MSG(error_string.str);
844  }
845  return rcl_convert_rmw_ret_to_rcl_ret(rmw_ret);
846 }
847 
848 rcl_ret_t
850  const rcl_node_t * node,
851  rcutils_allocator_t * allocator,
852  const char * service_name,
853  bool no_mangle,
854  rmw_service_endpoint_info_array_t * clients_info)
855 {
856  return __rcl_get_info_by_service(
857  node,
858  allocator,
859  service_name,
860  no_mangle,
861  clients_info,
862  rmw_get_clients_info_by_service);
863 }
864 
865 rcl_ret_t
867  const rcl_node_t * node,
868  rcutils_allocator_t * allocator,
869  const char * service_name,
870  bool no_mangle,
871  rmw_service_endpoint_info_array_t * servers_info)
872 {
873  return __rcl_get_info_by_service(
874  node,
875  allocator,
876  service_name,
877  no_mangle,
878  servers_info,
879  rmw_get_servers_info_by_service);
880 }
881 
882 rcl_ret_t
884  const rcl_node_t * node,
885  const rcl_client_t * client,
886  bool * is_available)
887 {
888  RCUTILS_CAN_RETURN_WITH_ERROR_OF(RCL_RET_INVALID_ARGUMENT);
889  RCUTILS_CAN_RETURN_WITH_ERROR_OF(RCL_RET_NODE_INVALID);
890 
891  if (!rcl_node_is_valid(node)) {
892  return RCL_RET_NODE_INVALID; // error already set
893  }
894  const rcl_node_options_t * node_options = rcl_node_get_options(node);
895  if (!node_options) {
896  return RCL_RET_NODE_INVALID; // shouldn't happen, but error is already set if so
897  }
898  RCL_CHECK_ARGUMENT_FOR_NULL(client, RCL_RET_INVALID_ARGUMENT);
899  RCL_CHECK_ARGUMENT_FOR_NULL(is_available, RCL_RET_INVALID_ARGUMENT);
900  rmw_ret_t rmw_ret = rmw_service_server_is_available(
903  is_available
904  );
905  return rcl_convert_rmw_ret_to_rcl_ret(rmw_ret);
906 }
907 
908 #ifdef __cplusplus
909 }
910 #endif
#define RCL_CHECK_ALLOCATOR(allocator, fail_statement)
Check that the given allocator is initialized.
Definition: allocator.h:49
#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 rmw_client_t * rcl_client_get_rmw_handle(const rcl_client_t *client)
Return the rmw client handle.
Definition: client.c:299
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_get_servers_info_by_service(const rcl_node_t *node, rcutils_allocator_t *allocator, const char *service_name, bool no_mangle, rcl_service_endpoint_info_array_t *servers_info)
Return a list of all servers to a service.
Definition: graph.c:866
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_wait_for_servers(const rcl_node_t *node, rcl_allocator_t *allocator, const char *service_name, const size_t count, rcutils_duration_value_t timeout, bool *success)
Wait for there to be a specified number of servers on a given service.
Definition: graph.c:696
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_count_services(const rcl_node_t *node, const char *service_name, size_t *count)
Return the number of servers on a given service.
Definition: graph.c:484
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_get_subscriptions_info_by_topic(const rcl_node_t *node, rcutils_allocator_t *allocator, const char *topic_name, bool no_mangle, rcl_topic_endpoint_info_array_t *subscriptions_info)
Return a list of all subscriptions to a topic.
Definition: graph.c:782
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_names_and_types_fini(rcl_names_and_types_t *names_and_types)
Finalize a rcl_names_and_types_t object.
Definition: graph.c:311
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_get_node_names_with_enclaves(const rcl_node_t *node, rcl_allocator_t allocator, rcutils_string_array_t *node_names, rcutils_string_array_t *node_namespaces, rcutils_string_array_t *enclaves)
Return a list of node names and their associated namespaces and enclaves in the ROS graph.
Definition: graph.c:380
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_wait_for_clients(const rcl_node_t *node, rcl_allocator_t *allocator, const char *service_name, const size_t count, rcutils_duration_value_t timeout, bool *success)
Wait for there to be a specified number of clients on a given service.
Definition: graph.c:677
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_get_service_names_and_types(const rcl_node_t *node, rcl_allocator_t *allocator, rcl_names_and_types_t *service_names_and_types)
Return a list of service names and their types.
Definition: graph.c:272
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_get_client_names_and_types_by_node(const rcl_node_t *node, rcl_allocator_t *allocator, const char *node_name, const char *node_namespace, rcl_names_and_types_t *service_names_and_types)
Return a list of service client names and types associated with a node.
Definition: graph.c:205
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_service_server_is_available(const rcl_node_t *node, const rcl_client_t *client, bool *is_available)
Check if a service server is available for the given service client.
Definition: graph.c:883
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_get_publishers_info_by_topic(const rcl_node_t *node, rcutils_allocator_t *allocator, const char *topic_name, bool no_mangle, rcl_topic_endpoint_info_array_t *publishers_info)
Return a list of all publishers to a topic.
Definition: graph.c:765
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_names_and_types_init(rcl_names_and_types_t *names_and_types, size_t size, rcl_allocator_t *allocator)
Initialize a rcl_names_and_types_t object.
Definition: graph.c:297
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_get_service_names_and_types_by_node(const rcl_node_t *node, rcl_allocator_t *allocator, const char *node_name, const char *node_namespace, rcl_names_and_types_t *service_names_and_types)
Return a list of service names and types associated with a node.
Definition: graph.c:165
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_get_subscriber_names_and_types_by_node(const rcl_node_t *node, rcl_allocator_t *allocator, bool no_demangle, const char *node_name, const char *node_namespace, rcl_names_and_types_t *topic_names_and_types)
Return a list of topic names and types for subscriptions associated with a node.
Definition: graph.c:123
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_count_subscribers(const rcl_node_t *node, const char *topic_name, size_t *count)
Return the number of subscriptions on a given topic.
Definition: graph.c:446
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_wait_for_publishers(const rcl_node_t *node, rcl_allocator_t *allocator, const char *topic_name, const size_t count, rcutils_duration_value_t timeout, bool *success)
Wait for there to be a specified number of publishers on a given topic.
Definition: graph.c:638
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 clients on a given service.
Definition: graph.c:465
rmw_names_and_types_t rcl_names_and_types_t
A structure that contains topic names and types.
Definition: graph.h:43
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_get_publisher_names_and_types_by_node(const rcl_node_t *node, rcl_allocator_t *allocator, bool no_demangle, const char *node_name, const char *node_namespace, rcl_names_and_types_t *topic_names_and_types)
Return a list of topic names and types for publishers associated with a node.
Definition: graph.c:82
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_wait_for_subscribers(const rcl_node_t *node, rcl_allocator_t *allocator, const char *topic_name, const size_t count, rcutils_duration_value_t timeout, bool *success)
Wait for there to be a specified number of subscribers on a given topic.
Definition: graph.c:657
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_get_topic_names_and_types(const rcl_node_t *node, rcl_allocator_t *allocator, bool no_demangle, rcl_names_and_types_t *topic_names_and_types)
Return a list of topic names and their types.
Definition: graph.c:245
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_get_node_names(const rcl_node_t *node, rcl_allocator_t allocator, rcutils_string_array_t *node_names, rcutils_string_array_t *node_namespaces)
Return a list of node names and their associated namespaces in the ROS graph.
Definition: graph.c:321
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_get_clients_info_by_service(const rcl_node_t *node, rcutils_allocator_t *allocator, const char *service_name, bool no_mangle, rcl_service_endpoint_info_array_t *clients_info)
Return a list of all clients to a service.
Definition: graph.c:849
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_count_publishers(const rcl_node_t *node, const char *topic_name, size_t *count)
Return the number of publishers on a given topic.
Definition: graph.c:427
RCL_PUBLIC RCL_WARN_UNUSED const rcl_node_options_t * rcl_node_get_options(const rcl_node_t *node)
Return the rcl node options.
Definition: node.c:443
RCL_PUBLIC bool rcl_node_is_valid(const rcl_node_t *node)
Return true if the node is valid, else false.
Definition: node.c:402
RCL_PUBLIC RCL_WARN_UNUSED const rcl_guard_condition_t * rcl_node_get_graph_guard_condition(const rcl_node_t *node)
Return a guard condition which is triggered when the ROS graph changes.
Definition: node.c:484
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:466
Structure which encapsulates a ROS Client.
Definition: client.h:43
Handle for a rcl guard condition.
Structure which encapsulates the options for creating a rcl_node_t.
Definition: node_options.h:35
Structure which encapsulates a ROS Node.
Definition: node.h:45
rcl_context_t * context
Context associated with this node.
Definition: node.h:47
Container for subscription's, guard condition's, etc to be waited on.
Definition: wait.h:42
#define RCL_RET_NODE_INVALID_NAMESPACE
Invalid node namespace return code.
Definition: types.h:63
#define RCL_RET_OK
Success return code.
Definition: types.h:27
#define RCL_RET_INVALID_ARGUMENT
Invalid argument return code.
Definition: types.h:35
#define RCL_RET_ERROR
Unspecified error return code.
Definition: types.h:29
#define RCL_RET_NODE_INVALID
Invalid rcl_node_t given return code.
Definition: types.h:59
#define RCL_RET_TIMEOUT
Timeout occurred return code.
Definition: types.h:31
#define RCL_RET_NODE_INVALID_NAME
Invalid node name return code.
Definition: types.h:61
rmw_ret_t rcl_ret_t
The type that holds an rcl return code.
Definition: types.h:24
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_wait_set_init(rcl_wait_set_t *wait_set, size_t number_of_subscriptions, size_t number_of_guard_conditions, size_t number_of_timers, size_t number_of_clients, size_t number_of_services, size_t number_of_events, rcl_context_t *context, rcl_allocator_t allocator)
Initialize a rcl wait set with space for items to be waited on.
Definition: wait.c:83
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_wait_set_clear(rcl_wait_set_t *wait_set)
Remove (sets to NULL) all entities in the wait set.
Definition: wait.c:350
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_wait_set_fini(rcl_wait_set_t *wait_set)
Finalize a rcl wait set.
Definition: wait.c:172
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_wait(rcl_wait_set_t *wait_set, int64_t timeout)
Block until the wait set is ready or until the timeout has been exceeded.
Definition: wait.c:538
RCL_PUBLIC RCL_WARN_UNUSED rcl_wait_set_t rcl_get_zero_initialized_wait_set(void)
Return a rcl_wait_set_t struct with members set to NULL.
Definition: wait.c:69
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_wait_set_add_guard_condition(rcl_wait_set_t *wait_set, const rcl_guard_condition_t *guard_condition, size_t *index)
Store a pointer to the guard condition in the next empty spot in the set.
Definition: wait.c:470