ROS 2 rclcpp + rcl - rolling  rolling-a919a6e5
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 "rcl/graph.h"
21 
22 #include "rcl/error_handling.h"
23 #include "rcl/guard_condition.h"
24 #include "rcl/wait.h"
25 #include "rcutils/allocator.h"
26 #include "rcutils/error_handling.h"
27 #include "rcutils/macros.h"
28 #include "rcutils/time.h"
29 #include "rcutils/types.h"
30 #include "rmw/error_handling.h"
31 #include "rmw/get_node_info_and_types.h"
32 #include "rmw/get_service_names_and_types.h"
33 #include "rmw/get_topic_endpoint_info.h"
34 #include "rmw/get_topic_names_and_types.h"
35 #include "rmw/names_and_types.h"
36 #include "rmw/rmw.h"
37 #include "rmw/topic_endpoint_info_array.h"
38 #include "rmw/validate_namespace.h"
39 #include "rmw/validate_node_name.h"
40 
41 #include "./common.h"
42 
44 __validate_node_name_and_namespace(
45  const char * node_name,
46  const char * node_namespace)
47 {
48  int validation_result = 0;
49  rmw_ret_t rmw_ret = rmw_validate_namespace(node_namespace, &validation_result, NULL);
50 
51  if (RMW_RET_OK != rmw_ret) {
52  RCL_SET_ERROR_MSG(rmw_get_error_string().str);
53  return rcl_convert_rmw_ret_to_rcl_ret(rmw_ret);
54  }
55  if (validation_result != RMW_NAMESPACE_VALID) {
56  const char * msg = rmw_namespace_validation_result_string(validation_result);
57  RCL_SET_ERROR_MSG_WITH_FORMAT_STRING("%s, result: %d", msg, validation_result);
59  }
60 
61  validation_result = 0;
62  rmw_ret = rmw_validate_node_name(node_name, &validation_result, NULL);
63  if (RMW_RET_OK != rmw_ret) {
64  RCL_SET_ERROR_MSG(rmw_get_error_string().str);
65  return rcl_convert_rmw_ret_to_rcl_ret(rmw_ret);
66  }
67  if (RMW_NODE_NAME_VALID != validation_result) {
68  const char * msg = rmw_node_name_validation_result_string(validation_result);
69  RCL_SET_ERROR_MSG_WITH_FORMAT_STRING("%s, result: %d", msg, validation_result);
71  }
72 
73  return RCL_RET_OK;
74 }
75 
78  const rcl_node_t * node,
79  rcl_allocator_t * allocator,
80  bool no_demangle,
81  const char * node_name,
82  const char * node_namespace,
83  rcl_names_and_types_t * topic_names_and_types)
84 {
85  if (!rcl_node_is_valid(node)) {
86  return RCL_RET_NODE_INVALID;
87  }
88  RCL_CHECK_ALLOCATOR_WITH_MSG(allocator, "invalid allocator", return RCL_RET_INVALID_ARGUMENT);
89  RCL_CHECK_ARGUMENT_FOR_NULL(node_name, RCL_RET_INVALID_ARGUMENT);
90  RCL_CHECK_ARGUMENT_FOR_NULL(node_namespace, RCL_RET_INVALID_ARGUMENT);
91  RCL_CHECK_ARGUMENT_FOR_NULL(topic_names_and_types, RCL_RET_INVALID_ARGUMENT);
92 
93  const char * valid_namespace = "/";
94  if (strlen(node_namespace) > 0) {
95  valid_namespace = node_namespace;
96  }
97  rmw_ret_t rmw_ret = rmw_names_and_types_check_zero(topic_names_and_types);
98  if (RMW_RET_OK != rmw_ret) {
99  return rcl_convert_rmw_ret_to_rcl_ret(rmw_ret);
100  }
101  rcl_ret_t rcl_ret = __validate_node_name_and_namespace(node_name, valid_namespace);
102  if (RCL_RET_OK != rcl_ret) {
103  return rcl_ret;
104  }
105  rcutils_allocator_t rcutils_allocator = *allocator;
106  rmw_ret = rmw_get_publisher_names_and_types_by_node(
108  &rcutils_allocator,
109  node_name,
110  valid_namespace,
111  no_demangle,
112  topic_names_and_types
113  );
114  return rcl_convert_rmw_ret_to_rcl_ret(rmw_ret);
115 }
116 
117 rcl_ret_t
119  const rcl_node_t * node,
120  rcl_allocator_t * allocator,
121  bool no_demangle,
122  const char * node_name,
123  const char * node_namespace,
124  rcl_names_and_types_t * topic_names_and_types)
125 {
126  if (!rcl_node_is_valid(node)) {
127  return RCL_RET_NODE_INVALID;
128  }
129  RCL_CHECK_ALLOCATOR_WITH_MSG(allocator, "invalid allocator", return RCL_RET_INVALID_ARGUMENT);
130  RCL_CHECK_ARGUMENT_FOR_NULL(node_name, RCL_RET_INVALID_ARGUMENT);
131  RCL_CHECK_ARGUMENT_FOR_NULL(node_namespace, RCL_RET_INVALID_ARGUMENT);
132  RCL_CHECK_ARGUMENT_FOR_NULL(topic_names_and_types, RCL_RET_INVALID_ARGUMENT);
133 
134  const char * valid_namespace = "/";
135  if (strlen(node_namespace) > 0) {
136  valid_namespace = node_namespace;
137  }
138  rmw_ret_t rmw_ret;
139  rmw_ret = rmw_names_and_types_check_zero(topic_names_and_types);
140  if (rmw_ret != RMW_RET_OK) {
141  return rcl_convert_rmw_ret_to_rcl_ret(rmw_ret);
142  }
143  rcutils_allocator_t rcutils_allocator = *allocator;
144  rcl_ret_t rcl_ret = __validate_node_name_and_namespace(node_name, valid_namespace);
145  if (RCL_RET_OK != rcl_ret) {
146  return rcl_ret;
147  }
148  rmw_ret = rmw_get_subscriber_names_and_types_by_node(
150  &rcutils_allocator,
151  node_name,
152  valid_namespace,
153  no_demangle,
154  topic_names_and_types
155  );
156  return rcl_convert_rmw_ret_to_rcl_ret(rmw_ret);
157 }
158 
159 rcl_ret_t
161  const rcl_node_t * node,
162  rcl_allocator_t * allocator,
163  const char * node_name,
164  const char * node_namespace,
165  rcl_names_and_types_t * service_names_and_types)
166 {
167  if (!rcl_node_is_valid(node)) {
168  return RCL_RET_NODE_INVALID;
169  }
170  RCL_CHECK_ALLOCATOR_WITH_MSG(allocator, "invalid allocator", return RCL_RET_INVALID_ARGUMENT);
171  RCL_CHECK_ARGUMENT_FOR_NULL(node_name, RCL_RET_INVALID_ARGUMENT);
172  RCL_CHECK_ARGUMENT_FOR_NULL(node_namespace, RCL_RET_INVALID_ARGUMENT);
173  RCL_CHECK_ARGUMENT_FOR_NULL(service_names_and_types, RCL_RET_INVALID_ARGUMENT);
174 
175  const char * valid_namespace = "/";
176  if (strlen(node_namespace) > 0) {
177  valid_namespace = node_namespace;
178  }
179  rmw_ret_t rmw_ret;
180  rmw_ret = rmw_names_and_types_check_zero(service_names_and_types);
181  if (rmw_ret != RMW_RET_OK) {
182  return rcl_convert_rmw_ret_to_rcl_ret(rmw_ret);
183  }
184  rcl_ret_t rcl_ret = __validate_node_name_and_namespace(node_name, valid_namespace);
185  if (RCL_RET_OK != rcl_ret) {
186  return rcl_ret;
187  }
188  rcutils_allocator_t rcutils_allocator = *allocator;
189  rmw_ret = rmw_get_service_names_and_types_by_node(
191  &rcutils_allocator,
192  node_name,
193  valid_namespace,
194  service_names_and_types
195  );
196  return rcl_convert_rmw_ret_to_rcl_ret(rmw_ret);
197 }
198 
199 rcl_ret_t
201  const rcl_node_t * node,
202  rcl_allocator_t * allocator,
203  const char * node_name,
204  const char * node_namespace,
205  rcl_names_and_types_t * service_names_and_types)
206 {
207  if (!rcl_node_is_valid(node)) {
208  return RCL_RET_NODE_INVALID;
209  }
210  RCL_CHECK_ALLOCATOR_WITH_MSG(allocator, "invalid allocator", return RCL_RET_INVALID_ARGUMENT);
211  RCL_CHECK_ARGUMENT_FOR_NULL(node_name, RCL_RET_INVALID_ARGUMENT);
212  RCL_CHECK_ARGUMENT_FOR_NULL(node_namespace, RCL_RET_INVALID_ARGUMENT);
213  RCL_CHECK_ARGUMENT_FOR_NULL(service_names_and_types, RCL_RET_INVALID_ARGUMENT);
214 
215  const char * valid_namespace = "/";
216  if (strlen(node_namespace) > 0) {
217  valid_namespace = node_namespace;
218  }
219  rmw_ret_t rmw_ret;
220  rmw_ret = rmw_names_and_types_check_zero(service_names_and_types);
221  if (rmw_ret != RMW_RET_OK) {
222  return rcl_convert_rmw_ret_to_rcl_ret(rmw_ret);
223  }
224  rcl_ret_t rcl_ret = __validate_node_name_and_namespace(node_name, valid_namespace);
225  if (RCL_RET_OK != rcl_ret) {
226  return rcl_ret;
227  }
228  rcutils_allocator_t rcutils_allocator = *allocator;
229  rmw_ret = rmw_get_client_names_and_types_by_node(
231  &rcutils_allocator,
232  node_name,
233  valid_namespace,
234  service_names_and_types
235  );
236  return rcl_convert_rmw_ret_to_rcl_ret(rmw_ret);
237 }
238 
239 rcl_ret_t
241  const rcl_node_t * node,
242  rcl_allocator_t * allocator,
243  bool no_demangle,
244  rcl_names_and_types_t * topic_names_and_types)
245 {
246  if (!rcl_node_is_valid(node)) {
247  return RCL_RET_NODE_INVALID; // error already set
248  }
249  RCL_CHECK_ALLOCATOR_WITH_MSG(allocator, "invalid allocator", return RCL_RET_INVALID_ARGUMENT);
250  RCL_CHECK_ARGUMENT_FOR_NULL(topic_names_and_types, RCL_RET_INVALID_ARGUMENT);
251  rmw_ret_t rmw_ret;
252  rmw_ret = rmw_names_and_types_check_zero(topic_names_and_types);
253  if (rmw_ret != RMW_RET_OK) {
254  return rcl_convert_rmw_ret_to_rcl_ret(rmw_ret);
255  }
256  rcutils_allocator_t rcutils_allocator = *allocator;
257  rmw_ret = rmw_get_topic_names_and_types(
259  &rcutils_allocator,
260  no_demangle,
261  topic_names_and_types
262  );
263  return rcl_convert_rmw_ret_to_rcl_ret(rmw_ret);
264 }
265 
266 rcl_ret_t
268  const rcl_node_t * node,
269  rcl_allocator_t * allocator,
270  rcl_names_and_types_t * service_names_and_types)
271 {
272  if (!rcl_node_is_valid(node)) {
273  return RCL_RET_NODE_INVALID; // error already set
274  }
275  RCL_CHECK_ALLOCATOR_WITH_MSG(allocator, "invalid allocator", return RCL_RET_INVALID_ARGUMENT);
276  RCL_CHECK_ARGUMENT_FOR_NULL(service_names_and_types, RCL_RET_INVALID_ARGUMENT);
277  rmw_ret_t rmw_ret;
278  rmw_ret = rmw_names_and_types_check_zero(service_names_and_types);
279  if (rmw_ret != RMW_RET_OK) {
280  return rcl_convert_rmw_ret_to_rcl_ret(rmw_ret);
281  }
282  rcutils_allocator_t rcutils_allocator = *allocator;
283  rmw_ret = rmw_get_service_names_and_types(
285  &rcutils_allocator,
286  service_names_and_types
287  );
288  return rcl_convert_rmw_ret_to_rcl_ret(rmw_ret);
289 }
290 
291 rcl_ret_t
293  rcl_names_and_types_t * names_and_types,
294  size_t size,
295  rcl_allocator_t * allocator)
296 {
297  RCUTILS_CAN_RETURN_WITH_ERROR_OF(RCL_RET_INVALID_ARGUMENT);
298 
299  RCL_CHECK_ARGUMENT_FOR_NULL(names_and_types, RCL_RET_INVALID_ARGUMENT);
301  rmw_ret_t rmw_ret = rmw_names_and_types_init(names_and_types, size, allocator);
302  return rcl_convert_rmw_ret_to_rcl_ret(rmw_ret);
303 }
304 
305 rcl_ret_t
307 {
308  RCUTILS_CAN_RETURN_WITH_ERROR_OF(RCL_RET_INVALID_ARGUMENT);
309 
310  RCL_CHECK_ARGUMENT_FOR_NULL(topic_names_and_types, RCL_RET_INVALID_ARGUMENT);
311  rmw_ret_t rmw_ret = rmw_names_and_types_fini(topic_names_and_types);
312  return rcl_convert_rmw_ret_to_rcl_ret(rmw_ret);
313 }
314 
315 rcl_ret_t
317  const rcl_node_t * node,
318  rcl_allocator_t allocator,
319  rcutils_string_array_t * node_names,
320  rcutils_string_array_t * node_namespaces)
321 {
322  if (!rcl_node_is_valid(node)) {
323  return RCL_RET_NODE_INVALID; // error already set
324  }
325  RCL_CHECK_ARGUMENT_FOR_NULL(node_names, RCL_RET_INVALID_ARGUMENT);
326  if (node_names->size != 0) {
327  RCL_SET_ERROR_MSG("node_names size is not zero");
329  }
330  if (node_names->data) {
331  RCL_SET_ERROR_MSG("node_names is not null");
333  }
334  RCL_CHECK_ARGUMENT_FOR_NULL(node_namespaces, RCL_RET_INVALID_ARGUMENT);
335  if (node_namespaces->size != 0) {
336  RCL_SET_ERROR_MSG("node_namespaces size is not zero");
338  }
339  if (node_namespaces->data) {
340  RCL_SET_ERROR_MSG("node_namespaces is not null");
342  }
343  (void)allocator; // to be used in rmw_get_node_names in the future
344  rmw_ret_t rmw_ret = rmw_get_node_names(
346  node_names,
347  node_namespaces);
348 
349  if (RMW_RET_OK != rmw_ret) {
350  return rcl_convert_rmw_ret_to_rcl_ret(rmw_ret);
351  }
352 
353  // Check that none of the node names are NULL or empty
354  for (size_t i = 0u; i < node_names->size; ++i) {
355  if (!node_names->data[i]) {
356  RCL_SET_ERROR_MSG("NULL node name returned by the RMW layer");
358  }
359  if (!strcmp(node_names->data[i], "")) {
360  RCL_SET_ERROR_MSG("empty node name returned by the RMW layer");
362  }
363  }
364  // Check that none of the node namespaces are NULL
365  for (size_t i = 0u; i < node_namespaces->size; ++i) {
366  if (!node_namespaces->data[i]) {
367  RCL_SET_ERROR_MSG("NULL node namespace returned by the RMW layer");
369  }
370  }
371  return RCL_RET_OK;
372 }
373 
374 rcl_ret_t
376  const rcl_node_t * node,
377  rcl_allocator_t allocator,
378  rcutils_string_array_t * node_names,
379  rcutils_string_array_t * node_namespaces,
380  rcutils_string_array_t * enclaves)
381 {
382  if (!rcl_node_is_valid(node)) {
383  return RCL_RET_NODE_INVALID; // error already set
384  }
385  RCL_CHECK_ARGUMENT_FOR_NULL(node_names, RCL_RET_INVALID_ARGUMENT);
386  if (node_names->size != 0) {
387  RCL_SET_ERROR_MSG("node_names size is not zero");
389  }
390  if (node_names->data) {
391  RCL_SET_ERROR_MSG("node_names is not null");
393  }
394  RCL_CHECK_ARGUMENT_FOR_NULL(node_namespaces, RCL_RET_INVALID_ARGUMENT);
395  if (node_namespaces->size != 0) {
396  RCL_SET_ERROR_MSG("node_namespaces size is not zero");
398  }
399  if (node_namespaces->data) {
400  RCL_SET_ERROR_MSG("node_namespaces is not null");
402  }
403  RCL_CHECK_ARGUMENT_FOR_NULL(enclaves, RCL_RET_INVALID_ARGUMENT);
404  if (enclaves->size != 0) {
405  RCL_SET_ERROR_MSG("enclaves size is not zero");
407  }
408  if (enclaves->data) {
409  RCL_SET_ERROR_MSG("enclaves is not null");
411  }
412  (void)allocator; // to be used in rmw_get_node_names in the future
413  rmw_ret_t rmw_ret = rmw_get_node_names_with_enclaves(
415  node_names,
416  node_namespaces,
417  enclaves);
418  return rcl_convert_rmw_ret_to_rcl_ret(rmw_ret);
419 }
420 
421 rcl_ret_t
423  const rcl_node_t * node,
424  const char * topic_name,
425  size_t * count)
426 {
427  if (!rcl_node_is_valid(node)) {
428  return RCL_RET_NODE_INVALID; // error already set
429  }
430  const rcl_node_options_t * node_options = rcl_node_get_options(node);
431  if (!node_options) {
432  return RCL_RET_NODE_INVALID; // shouldn't happen, but error is already set if so
433  }
434  RCL_CHECK_ARGUMENT_FOR_NULL(topic_name, RCL_RET_INVALID_ARGUMENT);
435  RCL_CHECK_ARGUMENT_FOR_NULL(count, RCL_RET_INVALID_ARGUMENT);
436  rmw_ret_t rmw_ret = rmw_count_publishers(rcl_node_get_rmw_handle(node), topic_name, count);
437  return rcl_convert_rmw_ret_to_rcl_ret(rmw_ret);
438 }
439 
440 rcl_ret_t
442  const rcl_node_t * node,
443  const char * topic_name,
444  size_t * count)
445 {
446  if (!rcl_node_is_valid(node)) {
447  return RCL_RET_NODE_INVALID; // error already set
448  }
449  const rcl_node_options_t * node_options = rcl_node_get_options(node);
450  if (!node_options) {
451  return RCL_RET_NODE_INVALID; // shouldn't happen, but error is already set if so
452  }
453  RCL_CHECK_ARGUMENT_FOR_NULL(topic_name, RCL_RET_INVALID_ARGUMENT);
454  RCL_CHECK_ARGUMENT_FOR_NULL(count, RCL_RET_INVALID_ARGUMENT);
455  rmw_ret_t rmw_ret = rmw_count_subscribers(rcl_node_get_rmw_handle(node), topic_name, count);
456  return rcl_convert_rmw_ret_to_rcl_ret(rmw_ret);
457 }
458 
459 rcl_ret_t
461  const rcl_node_t * node,
462  const char * service_name,
463  size_t * count)
464 {
465  if (!rcl_node_is_valid(node)) {
466  return RCL_RET_NODE_INVALID; // error already set
467  }
468  const rcl_node_options_t * node_options = rcl_node_get_options(node);
469  if (!node_options) {
470  return RCL_RET_NODE_INVALID; // shouldn't happen, but error is already set if so
471  }
472  RCL_CHECK_ARGUMENT_FOR_NULL(service_name, RCL_RET_INVALID_ARGUMENT);
473  RCL_CHECK_ARGUMENT_FOR_NULL(count, RCL_RET_INVALID_ARGUMENT);
474  rmw_ret_t rmw_ret = rmw_count_clients(rcl_node_get_rmw_handle(node), service_name, count);
475  return rcl_convert_rmw_ret_to_rcl_ret(rmw_ret);
476 }
477 
478 rcl_ret_t
480  const rcl_node_t * node,
481  const char * service_name,
482  size_t * count)
483 {
484  if (!rcl_node_is_valid(node)) {
485  return RCL_RET_NODE_INVALID; // error already set
486  }
487  const rcl_node_options_t * node_options = rcl_node_get_options(node);
488  if (!node_options) {
489  return RCL_RET_NODE_INVALID; // shouldn't happen, but error is already set if so
490  }
491  RCL_CHECK_ARGUMENT_FOR_NULL(service_name, RCL_RET_INVALID_ARGUMENT);
492  RCL_CHECK_ARGUMENT_FOR_NULL(count, RCL_RET_INVALID_ARGUMENT);
493  rmw_ret_t rmw_ret = rmw_count_services(rcl_node_get_rmw_handle(node), service_name, count);
494  return rcl_convert_rmw_ret_to_rcl_ret(rmw_ret);
495 }
496 
497 typedef rcl_ret_t (* count_entities_func_t)(
498  const rcl_node_t * node,
499  const char * topic_name,
500  size_t * count);
501 
502 rcl_ret_t
503 _rcl_wait_for_entities(
504  const rcl_node_t * node,
505  rcl_allocator_t * allocator,
506  const char * topic_name,
507  const size_t expected_count,
508  rcutils_duration_value_t timeout,
509  bool * success,
510  count_entities_func_t count_entities_func)
511 {
512  if (!rcl_node_is_valid(node)) {
513  return RCL_RET_NODE_INVALID;
514  }
515  RCL_CHECK_ALLOCATOR_WITH_MSG(allocator, "invalid allocator", return RCL_RET_INVALID_ARGUMENT);
516  RCL_CHECK_ARGUMENT_FOR_NULL(topic_name, RCL_RET_INVALID_ARGUMENT);
517  RCL_CHECK_ARGUMENT_FOR_NULL(success, RCL_RET_INVALID_ARGUMENT);
518 
519  rcl_ret_t ret = RCL_RET_OK;
520  *success = false;
521 
522  // We can avoid waiting if there are already the expected number of entities
523  size_t count = 0u;
524  ret = count_entities_func(node, topic_name, &count);
525  if (ret != RCL_RET_OK) {
526  // Error message already set
527  return ret;
528  }
529  if (expected_count <= count) {
530  *success = true;
531  return RCL_RET_OK;
532  }
533 
534  // Create a wait set and add the node graph guard condition to it
536  ret = rcl_wait_set_init(
537  &wait_set, 0, 1, 0, 0, 0, 0, node->context, *allocator);
538  if (ret != RCL_RET_OK) {
539  // Error message already set
540  return ret;
541  }
542 
543  const rcl_guard_condition_t * guard_condition = rcl_node_get_graph_guard_condition(node);
544  if (!guard_condition) {
545  // Error message already set
546  ret = RCL_RET_ERROR;
547  goto cleanup;
548  }
549 
550  // Add it to the wait set
551  ret = rcl_wait_set_add_guard_condition(&wait_set, guard_condition, NULL);
552  if (ret != RCL_RET_OK) {
553  // Error message already set
554  goto cleanup;
555  }
556 
557  // Get current time
558  // We use system time to be consistent with the clock used by rcl_wait()
559  rcutils_time_point_value_t start;
560  rcutils_ret_t time_ret = rcutils_system_time_now(&start);
561  if (time_ret != RCUTILS_RET_OK) {
562  rcutils_error_string_t error = rcutils_get_error_string();
563  rcutils_reset_error();
564  RCL_SET_ERROR_MSG(error.str);
565  ret = RCL_RET_ERROR;
566  goto cleanup;
567  }
568 
569  // Wait for expected count or timeout
570  rcl_ret_t wait_ret;
571  while (true) {
572  // Use separate 'wait_ret' code to avoid returning spurious TIMEOUT value
573  wait_ret = rcl_wait(&wait_set, timeout);
574  if (wait_ret != RCL_RET_OK && wait_ret != RCL_RET_TIMEOUT) {
575  // Error message already set
576  ret = wait_ret;
577  break;
578  }
579 
580  // Check count
581  ret = count_entities_func(node, topic_name, &count);
582  if (ret != RCL_RET_OK) {
583  // Error already set
584  break;
585  }
586  if (expected_count <= count) {
587  *success = true;
588  break;
589  }
590 
591  // If we're not waiting indefinitely, compute time remaining
592  if (timeout >= 0) {
593  rcutils_time_point_value_t now;
594  time_ret = rcutils_system_time_now(&now);
595  if (time_ret != RCUTILS_RET_OK) {
596  rcutils_error_string_t error = rcutils_get_error_string();
597  rcutils_reset_error();
598  RCL_SET_ERROR_MSG(error.str);
599  ret = RCL_RET_ERROR;
600  break;
601  }
602  timeout = timeout - (now - start);
603  if (timeout <= 0) {
604  ret = RCL_RET_TIMEOUT;
605  break;
606  }
607  }
608 
609  // Clear wait set for next iteration
610  ret = rcl_wait_set_clear(&wait_set);
611  if (ret != RCL_RET_OK) {
612  // Error message already set
613  break;
614  }
615  }
616 
617  rcl_ret_t cleanup_ret;
618 cleanup:
619  // Cleanup
620  cleanup_ret = rcl_wait_set_fini(&wait_set);
621  if (cleanup_ret != RCL_RET_OK) {
622  // If we got two unexpected errors, return the earlier error
623  if (ret != RCL_RET_OK && ret != RCL_RET_TIMEOUT) {
624  // Error message already set
625  ret = cleanup_ret;
626  }
627  }
628 
629  return ret;
630 }
631 
632 rcl_ret_t
634  const rcl_node_t * node,
635  rcl_allocator_t * allocator,
636  const char * topic_name,
637  const size_t expected_count,
638  rcutils_duration_value_t timeout,
639  bool * success)
640 {
641  return _rcl_wait_for_entities(
642  node,
643  allocator,
644  topic_name,
645  expected_count,
646  timeout,
647  success,
649 }
650 
651 rcl_ret_t
653  const rcl_node_t * node,
654  rcl_allocator_t * allocator,
655  const char * topic_name,
656  const size_t expected_count,
657  rcutils_duration_value_t timeout,
658  bool * success)
659 {
660  return _rcl_wait_for_entities(
661  node,
662  allocator,
663  topic_name,
664  expected_count,
665  timeout,
666  success,
668 }
669 
670 typedef rmw_ret_t (* get_topic_endpoint_info_func_t)(
671  const rmw_node_t * node,
672  rcutils_allocator_t * allocator,
673  const char * topic_name,
674  bool no_mangle,
675  rmw_topic_endpoint_info_array_t * info_array);
676 
677 rcl_ret_t
678 __rcl_get_info_by_topic(
679  const rcl_node_t * node,
680  rcutils_allocator_t * allocator,
681  const char * topic_name,
682  bool no_mangle,
683  rmw_topic_endpoint_info_array_t * info_array,
684  get_topic_endpoint_info_func_t get_topic_endpoint_info)
685 {
686  if (!rcl_node_is_valid(node)) {
687  return RCL_RET_NODE_INVALID; // error already set.
688  }
689  const rcl_node_options_t * node_options = rcl_node_get_options(node);
690  if (!node_options) {
691  return RCL_RET_NODE_INVALID; // shouldn't happen, but error is already set if so
692  }
693  RCL_CHECK_ALLOCATOR_WITH_MSG(allocator, "invalid allocator", return RCL_RET_INVALID_ARGUMENT);
694  RCL_CHECK_ARGUMENT_FOR_NULL(topic_name, RCL_RET_INVALID_ARGUMENT);
695  rmw_error_string_t error_string;
696  rmw_ret_t rmw_ret = rmw_topic_endpoint_info_array_check_zero(info_array);
697  if (rmw_ret != RMW_RET_OK) {
698  error_string = rmw_get_error_string();
699  rmw_reset_error();
700  RCL_SET_ERROR_MSG_WITH_FORMAT_STRING(
701  "rmw_topic_endpoint_info_array_t must be zero initialized: %s,\n"
702  "Use rmw_get_zero_initialized_topic_endpoint_info_array",
703  error_string.str);
704  return rcl_convert_rmw_ret_to_rcl_ret(rmw_ret);
705  }
706  rmw_ret = get_topic_endpoint_info(
708  allocator,
709  topic_name,
710  no_mangle,
711  info_array);
712  if (rmw_ret != RMW_RET_OK) {
713  error_string = rmw_get_error_string();
714  rmw_reset_error();
715  RCL_SET_ERROR_MSG(error_string.str);
716  }
717  return rcl_convert_rmw_ret_to_rcl_ret(rmw_ret);
718 }
719 
720 rcl_ret_t
722  const rcl_node_t * node,
723  rcutils_allocator_t * allocator,
724  const char * topic_name,
725  bool no_mangle,
726  rmw_topic_endpoint_info_array_t * publishers_info)
727 {
728  return __rcl_get_info_by_topic(
729  node,
730  allocator,
731  topic_name,
732  no_mangle,
733  publishers_info,
734  rmw_get_publishers_info_by_topic);
735 }
736 
737 rcl_ret_t
739  const rcl_node_t * node,
740  rcutils_allocator_t * allocator,
741  const char * topic_name,
742  bool no_mangle,
743  rmw_topic_endpoint_info_array_t * subscriptions_info)
744 {
745  return __rcl_get_info_by_topic(
746  node,
747  allocator,
748  topic_name,
749  no_mangle,
750  subscriptions_info,
751  rmw_get_subscriptions_info_by_topic);
752 }
753 
754 rcl_ret_t
756  const rcl_node_t * node,
757  const rcl_client_t * client,
758  bool * is_available)
759 {
760  RCUTILS_CAN_RETURN_WITH_ERROR_OF(RCL_RET_INVALID_ARGUMENT);
761  RCUTILS_CAN_RETURN_WITH_ERROR_OF(RCL_RET_NODE_INVALID);
762 
763  if (!rcl_node_is_valid(node)) {
764  return RCL_RET_NODE_INVALID; // error already set
765  }
766  const rcl_node_options_t * node_options = rcl_node_get_options(node);
767  if (!node_options) {
768  return RCL_RET_NODE_INVALID; // shouldn't happen, but error is already set if so
769  }
770  RCL_CHECK_ARGUMENT_FOR_NULL(client, RCL_RET_INVALID_ARGUMENT);
771  RCL_CHECK_ARGUMENT_FOR_NULL(is_available, RCL_RET_INVALID_ARGUMENT);
772  rmw_ret_t rmw_ret = rmw_service_server_is_available(
775  is_available
776  );
777  return rcl_convert_rmw_ret_to_rcl_ret(rmw_ret);
778 }
779 
780 #ifdef __cplusplus
781 }
782 #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:309
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:479
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:738
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:306
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:375
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:267
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:200
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:755
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:721
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:292
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:160
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:118
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:441
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:633
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:460
rmw_names_and_types_t rcl_names_and_types_t
A structure that contains topic names and types.
Definition: graph.h:40
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:77
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:652
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:240
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:316
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:422
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:435
RCL_PUBLIC bool rcl_node_is_valid(const rcl_node_t *node)
Return true if the node is valid, else false.
Definition: node.c:394
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:476
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:458
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:78
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:321
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:167
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:509
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:64
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:441