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