ROS 2 rclcpp + rcl - kilted  kilted
ROS 2 C++ Client Library with ROS Client Library
node_graph.cpp
1 // Copyright 2016 Open Source Robotics Foundation, Inc.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "rclcpp/node_interfaces/node_graph.hpp"
16 
17 #include <algorithm>
18 #include <map>
19 #include <string>
20 #include <tuple>
21 #include <utility>
22 #include <vector>
23 
24 #include "rcl/graph.h"
25 #include "rcl/remap.h"
26 #include "rclcpp/event.hpp"
27 #include "rclcpp/exceptions.hpp"
28 #include "rclcpp/expand_topic_or_service_name.hpp"
29 #include "rclcpp/graph_listener.hpp"
30 #include "rclcpp/node_interfaces/node_graph_interface.hpp"
31 #include "rcpputils/scope_exit.hpp"
32 
34 using rclcpp::exceptions::throw_from_rcl_error;
36 
37 NodeGraph::NodeGraph(rclcpp::node_interfaces::NodeBaseInterface * node_base)
38 : node_base_(node_base),
39  graph_listener_(
40  node_base->get_context()->get_sub_context<GraphListener>(node_base->get_context())
41  ),
42  should_add_to_graph_listener_(true),
43  graph_users_count_(0)
44 {}
45 
46 NodeGraph::~NodeGraph()
47 {
48  // Remove self from graph listener.
49  // Exchange with false to prevent others from trying to add this node to the
50  // graph listener after checking that it was not here.
51  if (!should_add_to_graph_listener_.exchange(false)) {
52  // If it was already false, then it needs to now be removed.
53  graph_listener_->remove_node(this);
54  }
55 }
56 
57 std::map<std::string, std::vector<std::string>>
58 NodeGraph::get_topic_names_and_types(bool no_demangle) const
59 {
61 
64  node_base_->get_rcl_node_handle(),
65  &allocator,
66  no_demangle,
67  &topic_names_and_types);
68  if (ret != RCL_RET_OK) {
69  auto error_msg = std::string("failed to get topic names and types: ") +
70  rcl_get_error_string().str;
71  rcl_reset_error();
72  if (rcl_names_and_types_fini(&topic_names_and_types) != RCL_RET_OK) {
73  error_msg += std::string(", failed also to cleanup topic names and types, leaking memory: ") +
74  rcl_get_error_string().str;
75  rcl_reset_error();
76  }
77  throw std::runtime_error(error_msg);
78  }
79 
80  std::map<std::string, std::vector<std::string>> topics_and_types;
81  for (size_t i = 0; i < topic_names_and_types.names.size; ++i) {
82  std::string topic_name = topic_names_and_types.names.data[i];
83  for (size_t j = 0; j < topic_names_and_types.types[i].size; ++j) {
84  topics_and_types[topic_name].emplace_back(topic_names_and_types.types[i].data[j]);
85  }
86  }
87 
88  ret = rcl_names_and_types_fini(&topic_names_and_types);
89  if (ret != RCL_RET_OK) {
90  // *INDENT-OFF*
91  throw std::runtime_error(
92  std::string("could not destroy topic names and types: ") + rcl_get_error_string().str);
93  // *INDENT-ON*
94  }
95 
96  return topics_and_types;
97 }
98 
99 std::map<std::string, std::vector<std::string>>
101 {
103 
106  node_base_->get_rcl_node_handle(),
107  &allocator,
108  &service_names_and_types);
109  if (ret != RCL_RET_OK) {
110  auto error_msg = std::string("failed to get service names and types: ") +
111  rcl_get_error_string().str;
112  rcl_reset_error();
113  if (rcl_names_and_types_fini(&service_names_and_types) != RCL_RET_OK) {
114  error_msg +=
115  std::string(", failed also to cleanup service names and types, leaking memory: ") +
116  rcl_get_error_string().str;
117  rcl_reset_error();
118  }
119  throw std::runtime_error(error_msg);
120  }
121 
122  std::map<std::string, std::vector<std::string>> services_and_types;
123  for (size_t i = 0; i < service_names_and_types.names.size; ++i) {
124  std::string service_name = service_names_and_types.names.data[i];
125  for (size_t j = 0; j < service_names_and_types.types[i].size; ++j) {
126  services_and_types[service_name].emplace_back(service_names_and_types.types[i].data[j]);
127  }
128  }
129 
130  ret = rcl_names_and_types_fini(&service_names_and_types);
131  if (ret != RCL_RET_OK) {
132  // *INDENT-OFF*
133  throw std::runtime_error(
134  std::string("could not destroy service names and types: ") + rcl_get_error_string().str);
135  // *INDENT-ON*
136  }
137 
138  return services_and_types;
139 }
140 
141 std::map<std::string, std::vector<std::string>>
143  const std::string & node_name,
144  const std::string & namespace_) const
145 {
149  node_base_->get_rcl_node_handle(),
150  &allocator,
151  node_name.c_str(),
152  namespace_.c_str(),
153  &service_names_and_types);
154  if (ret != RCL_RET_OK) {
155  auto error_msg = std::string("failed to get service names and types by node: ") +
156  rcl_get_error_string().str;
157  rcl_reset_error();
158  if (rcl_names_and_types_fini(&service_names_and_types) != RCL_RET_OK) {
159  error_msg +=
160  std::string(", failed also to cleanup service names and types, leaking memory: ") +
161  rcl_get_error_string().str;
162  rcl_reset_error();
163  }
164  throw std::runtime_error(error_msg);
165  }
166 
167  std::map<std::string, std::vector<std::string>> services_and_types;
168  for (size_t i = 0; i < service_names_and_types.names.size; ++i) {
169  std::string service_name = service_names_and_types.names.data[i];
170  for (size_t j = 0; j < service_names_and_types.types[i].size; ++j) {
171  services_and_types[service_name].emplace_back(service_names_and_types.types[i].data[j]);
172  }
173  }
174 
175  ret = rcl_names_and_types_fini(&service_names_and_types);
176  if (ret != RCL_RET_OK) {
177  throw_from_rcl_error(ret, "could not destroy service names and types");
178  }
179 
180  return services_and_types;
181 }
182 
183 std::map<std::string, std::vector<std::string>>
185  const std::string & node_name,
186  const std::string & namespace_) const
187 {
189  auto service_names_and_types_finalizer = rcpputils::make_scope_exit(
190  [&service_names_and_types]() {
191  if (rcl_names_and_types_fini(&service_names_and_types) != RCL_RET_OK) {
192  RCLCPP_ERROR(
193  rclcpp::get_logger("rclcpp"), "could not destroy service names and types");
194  }
195  });
198  node_base_->get_rcl_node_handle(),
199  &allocator,
200  node_name.c_str(),
201  namespace_.c_str(),
202  &service_names_and_types);
203  if (ret != RCL_RET_OK) {
204  throw_from_rcl_error(ret, "failed to get service names and types by node");
205  }
206 
207  std::map<std::string, std::vector<std::string>> services_and_types;
208  for (size_t i = 0; i < service_names_and_types.names.size; ++i) {
209  std::string service_name = service_names_and_types.names.data[i];
210  for (size_t j = 0; j < service_names_and_types.types[i].size; ++j) {
211  services_and_types[service_name].emplace_back(service_names_and_types.types[i].data[j]);
212  }
213  }
214 
215  return services_and_types;
216 }
217 
218 std::map<std::string, std::vector<std::string>>
220  const std::string & node_name,
221  const std::string & namespace_,
222  bool no_demangle) const
223 {
225  auto topic_names_and_types_finalizer = rcpputils::make_scope_exit(
226  [&topic_names_and_types]() {
227  if (rcl_names_and_types_fini(&topic_names_and_types) != RCL_RET_OK) {
228  RCLCPP_ERROR(
229  rclcpp::get_logger("rclcpp"), "could not destroy topic names and types");
230  }
231  });
234  node_base_->get_rcl_node_handle(),
235  &allocator,
236  no_demangle,
237  node_name.c_str(),
238  namespace_.c_str(),
239  &topic_names_and_types);
240  if (ret != RCL_RET_OK) {
241  throw_from_rcl_error(ret, "failed to get topic names and types by node");
242  }
243 
244  std::map<std::string, std::vector<std::string>> topics_and_types;
245  for (size_t i = 0; i < topic_names_and_types.names.size; ++i) {
246  std::string topic_name = topic_names_and_types.names.data[i];
247  for (size_t j = 0; j < topic_names_and_types.types[i].size; ++j) {
248  topics_and_types[topic_name].emplace_back(topic_names_and_types.types[i].data[j]);
249  }
250  }
251 
252  return topics_and_types;
253 }
254 
255 std::map<std::string, std::vector<std::string>>
257  const std::string & node_name,
258  const std::string & namespace_,
259  bool no_demangle) const
260 {
262  auto topic_names_and_types_finalizer = rcpputils::make_scope_exit(
263  [&topic_names_and_types]() {
264  if (rcl_names_and_types_fini(&topic_names_and_types) != RCL_RET_OK) {
265  RCLCPP_ERROR(
266  rclcpp::get_logger("rclcpp"), "could not destroy topic names and types");
267  }
268  });
271  node_base_->get_rcl_node_handle(),
272  &allocator,
273  no_demangle,
274  node_name.c_str(),
275  namespace_.c_str(),
276  &topic_names_and_types);
277  if (ret != RCL_RET_OK) {
278  throw_from_rcl_error(ret, "failed to get topic names and types by node");
279  }
280 
281  std::map<std::string, std::vector<std::string>> topics_and_types;
282  for (size_t i = 0; i < topic_names_and_types.names.size; ++i) {
283  std::string topic_name = topic_names_and_types.names.data[i];
284  for (size_t j = 0; j < topic_names_and_types.types[i].size; ++j) {
285  topics_and_types[topic_name].emplace_back(topic_names_and_types.types[i].data[j]);
286  }
287  }
288 
289  return topics_and_types;
290 }
291 
292 std::vector<std::string>
294 {
295  std::vector<std::string> nodes;
296  auto names_and_namespaces = get_node_names_and_namespaces();
297 
298  std::transform(
299  names_and_namespaces.begin(),
300  names_and_namespaces.end(),
301  std::back_inserter(nodes),
302  [](std::pair<std::string, std::string> nns) {
303  std::string return_string;
304  if (nns.second.back() == '/') {
305  return_string = nns.second + nns.first;
306  } else {
307  return_string = nns.second + '/' + nns.first;
308  }
309  // Quick check to make sure that we start with a slash
310  // Since fully-qualified strings need to
311  if (return_string.front() != '/') {
312  return_string = "/" + return_string;
313  }
314  return return_string;
315  }
316  );
317  return nodes;
318 }
319 
320 std::vector<std::tuple<std::string, std::string, std::string>>
322 {
323  rcutils_string_array_t node_names_c =
324  rcutils_get_zero_initialized_string_array();
325  rcutils_string_array_t node_namespaces_c =
326  rcutils_get_zero_initialized_string_array();
327  rcutils_string_array_t node_enclaves_c =
328  rcutils_get_zero_initialized_string_array();
329 
330  auto allocator = rcl_get_default_allocator();
332  node_base_->get_rcl_node_handle(),
333  allocator,
334  &node_names_c,
335  &node_namespaces_c,
336  &node_enclaves_c);
337  if (ret != RCL_RET_OK) {
338  auto error_msg =
339  std::string("failed to get node names with enclaves: ") + rcl_get_error_string().str;
340  rcl_reset_error();
341  if (rcutils_string_array_fini(&node_names_c) != RCUTILS_RET_OK) {
342  error_msg += std::string(", failed also to cleanup node names, leaking memory: ") +
343  rcl_get_error_string().str;
344  rcl_reset_error();
345  }
346  if (rcutils_string_array_fini(&node_namespaces_c) != RCUTILS_RET_OK) {
347  error_msg += std::string(", failed also to cleanup node namespaces, leaking memory: ") +
348  rcl_get_error_string().str;
349  rcl_reset_error();
350  }
351  if (rcutils_string_array_fini(&node_enclaves_c) != RCUTILS_RET_OK) {
352  error_msg += std::string(", failed also to cleanup node enclaves, leaking memory: ") +
353  rcl_get_error_string().str;
354  rcl_reset_error();
355  }
356  throw std::runtime_error(error_msg);
357  }
358 
359  std::vector<std::tuple<std::string, std::string, std::string>> node_tuples;
360  for (size_t i = 0; i < node_names_c.size; ++i) {
361  if (node_names_c.data[i] && node_namespaces_c.data[i] && node_enclaves_c.data[i]) {
362  node_tuples.emplace_back(
363  std::make_tuple(node_names_c.data[i], node_namespaces_c.data[i], node_enclaves_c.data[i]));
364  }
365  }
366 
367  std::string error("failed to finalize array");
368  rcl_ret_t ret_names = rcutils_string_array_fini(&node_names_c);
369  if (ret_names != RCUTILS_RET_OK) {
370  error += std::string(", could not destroy node names, leaking memory: ") +
371  rcl_get_error_string().str;
372  rcl_reset_error();
373  }
374  rcl_ret_t ret_ns = rcutils_string_array_fini(&node_namespaces_c);
375  if (ret_ns != RCUTILS_RET_OK) {
376  error += std::string(", could not destroy node namespaces, leaking memory: ") +
377  rcl_get_error_string().str;
378  rcl_reset_error();
379  }
380 
381  rcl_ret_t ret_ecv = rcutils_string_array_fini(&node_enclaves_c);
382  if (ret_ecv != RCUTILS_RET_OK) {
383  error += std::string(", could not destroy node enclaves, leaking memory: ") +
384  rcl_get_error_string().str;
385  rcl_reset_error();
386  }
387 
388  if (ret_names != RCUTILS_RET_OK || ret_ns != RCUTILS_RET_OK || ret_ecv != RCUTILS_RET_OK) {
389  throw std::runtime_error(error);
390  }
391 
392  return node_tuples;
393 }
394 
395 std::vector<std::pair<std::string, std::string>>
397 {
398  rcutils_string_array_t node_names_c =
399  rcutils_get_zero_initialized_string_array();
400  rcutils_string_array_t node_namespaces_c =
401  rcutils_get_zero_initialized_string_array();
402 
403  auto allocator = rcl_get_default_allocator();
404  auto ret = rcl_get_node_names(
405  node_base_->get_rcl_node_handle(),
406  allocator,
407  &node_names_c,
408  &node_namespaces_c);
409  if (ret != RCL_RET_OK) {
410  auto error_msg = std::string("failed to get node names: ") + rcl_get_error_string().str;
411  rcl_reset_error();
412  if (rcutils_string_array_fini(&node_names_c) != RCUTILS_RET_OK) {
413  error_msg += std::string(", failed also to cleanup node names, leaking memory: ") +
414  rcl_get_error_string().str;
415  rcl_reset_error();
416  }
417  if (rcutils_string_array_fini(&node_namespaces_c) != RCUTILS_RET_OK) {
418  error_msg += std::string(", failed also to cleanup node namespaces, leaking memory: ") +
419  rcl_get_error_string().str;
420  rcl_reset_error();
421  }
422  // TODO(karsten1987): Append rcutils_error_message once it's in master
423  throw std::runtime_error(error_msg);
424  }
425 
426  std::vector<std::pair<std::string, std::string>> node_names;
427  node_names.reserve(node_names_c.size);
428  for (size_t i = 0; i < node_names_c.size; ++i) {
429  if (node_names_c.data[i] && node_namespaces_c.data[i]) {
430  node_names.emplace_back(node_names_c.data[i], node_namespaces_c.data[i]);
431  }
432  }
433 
434  std::string error;
435  rcl_ret_t ret_names = rcutils_string_array_fini(&node_names_c);
436  if (ret_names != RCUTILS_RET_OK) {
437  // *INDENT-OFF*
438  // TODO(karsten1987): Append rcutils_error_message once it's in master
439  error = "could not destroy node names";
440  // *INDENT-ON*
441  }
442  rcl_ret_t ret_ns = rcutils_string_array_fini(&node_namespaces_c);
443  if (ret_ns != RCUTILS_RET_OK) {
444  // *INDENT-OFF*
445  // TODO(karsten1987): Append rcutils_error_message once it's in master
446  error += ", could not destroy node namespaces";
447  // *INDENT-ON*
448  }
449 
450  if (ret_names != RCUTILS_RET_OK || ret_ns != RCUTILS_RET_OK) {
451  throw std::runtime_error(error);
452  }
453 
454  return node_names;
455 }
456 
457 size_t
458 NodeGraph::count_publishers(const std::string & topic_name) const
459 {
460  auto rcl_node_handle = node_base_->get_rcl_node_handle();
461 
463  topic_name,
464  rcl_node_get_name(rcl_node_handle),
465  rcl_node_get_namespace(rcl_node_handle),
466  false); // false = not a service
467 
468  size_t count;
469  auto ret = rcl_count_publishers(rcl_node_handle, fqdn.c_str(), &count);
470  if (ret != RMW_RET_OK) {
471  // *INDENT-OFF*
472  throw std::runtime_error(
473  std::string("could not count publishers: ") + rmw_get_error_string().str);
474  // *INDENT-ON*
475  }
476  return count;
477 }
478 
479 size_t
480 NodeGraph::count_subscribers(const std::string & topic_name) const
481 {
482  auto rcl_node_handle = node_base_->get_rcl_node_handle();
483 
485  topic_name,
486  rcl_node_get_name(rcl_node_handle),
487  rcl_node_get_namespace(rcl_node_handle),
488  false); // false = not a service
489 
490  size_t count;
491  auto ret = rcl_count_subscribers(rcl_node_handle, fqdn.c_str(), &count);
492  if (ret != RMW_RET_OK) {
493  // *INDENT-OFF*
494  throw std::runtime_error(
495  std::string("could not count subscribers: ") + rmw_get_error_string().str);
496  // *INDENT-ON*
497  }
498  return count;
499 }
500 
501 size_t
502 NodeGraph::count_clients(const std::string & service_name) const
503 {
504  auto rcl_node_handle = node_base_->get_rcl_node_handle();
505 
507  service_name,
508  rcl_node_get_name(rcl_node_handle),
509  rcl_node_get_namespace(rcl_node_handle),
510  true);
511 
512  size_t count;
513  auto ret = rcl_count_clients(rcl_node_handle, fqdn.c_str(), &count);
514  if (ret != RMW_RET_OK) {
515  // *INDENT-OFF*
516  throw std::runtime_error(
517  std::string("could not count clients: ") + rmw_get_error_string().str);
518  // *INDENT-ON*
519  }
520  return count;
521 }
522 
523 size_t
524 NodeGraph::count_services(const std::string & service_name) const
525 {
526  auto rcl_node_handle = node_base_->get_rcl_node_handle();
527 
529  service_name,
530  rcl_node_get_name(rcl_node_handle),
531  rcl_node_get_namespace(rcl_node_handle),
532  true);
533 
534  size_t count;
535  auto ret = rcl_count_services(rcl_node_handle, fqdn.c_str(), &count);
536  if (ret != RMW_RET_OK) {
537  // *INDENT-OFF*
538  throw std::runtime_error(
539  std::string("could not count services: ") + rmw_get_error_string().str);
540  // *INDENT-ON*
541  }
542  return count;
543 }
544 
545 const rcl_guard_condition_t *
547 {
549 }
550 
551 void
553 {
554  {
555  std::lock_guard<std::mutex> graph_changed_lock(graph_mutex_);
556  bool bad_ptr_encountered = false;
557  for (auto & event_wptr : graph_events_) {
558  auto event_ptr = event_wptr.lock();
559  if (event_ptr) {
560  event_ptr->set();
561  } else {
562  bad_ptr_encountered = true;
563  }
564  }
565  if (bad_ptr_encountered) {
566  // remove invalid pointers with the erase-remove idiom
567  graph_events_.erase(
568  std::remove_if(
569  graph_events_.begin(),
570  graph_events_.end(),
571  [](const rclcpp::Event::WeakPtr & wptr) {
572  return wptr.expired();
573  }),
574  graph_events_.end());
575  // update graph_users_count_
576  graph_users_count_.store(graph_events_.size());
577  }
578  }
579  graph_cv_.notify_all();
580  try {
581  node_base_->trigger_notify_guard_condition();
582  } catch (const rclcpp::exceptions::RCLError & ex) {
583  throw std::runtime_error(
584  std::string("failed to notify wait set on graph change: ") + ex.what());
585  }
586 }
587 
588 void
590 {
591  // notify here anything that will not be woken up by ctrl-c or rclcpp::shutdown().
592  graph_cv_.notify_all();
593 }
594 
595 rclcpp::Event::SharedPtr
597 {
598  auto event = rclcpp::Event::make_shared();
599  {
600  std::lock_guard<std::mutex> graph_changed_lock(graph_mutex_);
601  graph_events_.push_back(event);
602  graph_users_count_++;
603  }
604  // on first call, add node to graph_listener_
605  if (should_add_to_graph_listener_.exchange(false)) {
606  graph_listener_->add_node(this);
607  graph_listener_->start_if_not_started();
608  }
609  return event;
610 }
611 
612 void
614  rclcpp::Event::SharedPtr event,
615  std::chrono::nanoseconds timeout)
616 {
619  if (!event) {
620  throw InvalidEventError();
621  }
622  {
623  std::lock_guard<std::mutex> graph_changed_lock(graph_mutex_);
624  bool event_in_graph_events = false;
625  for (const auto & event_wptr : graph_events_) {
626  if (event == event_wptr.lock()) {
627  event_in_graph_events = true;
628  break;
629  }
630  }
631  if (!event_in_graph_events) {
632  throw EventNotRegisteredError();
633  }
634  }
635  auto pred = [&event, context = node_base_->get_context()]() {
636  return event->check() || !rclcpp::ok(context);
637  };
638  std::unique_lock<std::mutex> graph_lock(graph_mutex_);
639  if (!pred()) {
640  graph_cv_.wait_for(graph_lock, timeout, pred);
641  }
642 }
643 
644 size_t
646 {
647  return graph_users_count_.load();
648 }
649 
650 static
651 std::vector<rclcpp::TopicEndpointInfo>
652 convert_to_topic_info_list(const rcl_topic_endpoint_info_array_t & info_array)
653 {
654  std::vector<rclcpp::TopicEndpointInfo> topic_info_list;
655  for (size_t i = 0; i < info_array.size; ++i) {
656  topic_info_list.push_back(rclcpp::TopicEndpointInfo(info_array.info_array[i]));
657  }
658  return topic_info_list;
659 }
660 
661 template<const char * EndpointType, typename FunctionT>
662 static std::vector<rclcpp::TopicEndpointInfo>
663 get_info_by_topic(
665  const std::string & topic_name,
666  bool no_mangle,
667  FunctionT rcl_get_info_by_topic)
668 {
669  std::string fqdn;
670  auto rcl_node_handle = node_base->get_rcl_node_handle();
671 
672  if (no_mangle) {
673  fqdn = topic_name;
674  } else {
676  topic_name,
677  rcl_node_get_name(rcl_node_handle),
678  rcl_node_get_namespace(rcl_node_handle),
679  false); // false = not a service
680 
681  // Get the node options
682  const rcl_node_options_t * node_options = rcl_node_get_options(rcl_node_handle);
683  if (nullptr == node_options) {
684  throw std::runtime_error("Need valid node options in get_info_by_topic()");
685  }
686  const rcl_arguments_t * global_args = nullptr;
687  if (node_options->use_global_arguments) {
688  global_args = &(rcl_node_handle->context->global_arguments);
689  }
690 
691  char * remapped_topic_name = nullptr;
693  &(node_options->arguments),
694  global_args,
695  fqdn.c_str(),
696  rcl_node_get_name(rcl_node_handle),
697  rcl_node_get_namespace(rcl_node_handle),
698  node_options->allocator,
699  &remapped_topic_name);
700  if (RCL_RET_OK != ret) {
701  throw_from_rcl_error(ret, std::string("Failed to remap topic name ") + fqdn);
702  } else if (nullptr != remapped_topic_name) {
703  fqdn = remapped_topic_name;
704  node_options->allocator.deallocate(remapped_topic_name, node_options->allocator.state);
705  }
706  }
707 
708  rcutils_allocator_t allocator = rcutils_get_default_allocator();
710  rcl_ret_t ret =
711  rcl_get_info_by_topic(rcl_node_handle, &allocator, fqdn.c_str(), no_mangle, &info_array);
712  if (RCL_RET_OK != ret) {
713  auto error_msg =
714  std::string("Failed to get information by topic for ") + EndpointType + std::string(":");
715  if (RCL_RET_UNSUPPORTED == ret) {
716  error_msg += std::string("function not supported by RMW_IMPLEMENTATION");
717  } else {
718  error_msg += rcl_get_error_string().str;
719  }
720  rcl_reset_error();
721  if (RCL_RET_OK != rcl_topic_endpoint_info_array_fini(&info_array, &allocator)) {
722  error_msg += std::string(", failed also to cleanup topic info array, leaking memory: ") +
723  rcl_get_error_string().str;
724  rcl_reset_error();
725  }
726  throw_from_rcl_error(ret, error_msg);
727  }
728 
729  std::vector<rclcpp::TopicEndpointInfo> topic_info_list = convert_to_topic_info_list(info_array);
730  ret = rcl_topic_endpoint_info_array_fini(&info_array, &allocator);
731  if (RCL_RET_OK != ret) {
732  throw_from_rcl_error(ret, "rcl_topic_info_array_fini failed.");
733  }
734 
735  return topic_info_list;
736 }
737 
738 static constexpr char kPublisherEndpointTypeName[] = "publishers";
739 std::vector<rclcpp::TopicEndpointInfo>
741  const std::string & topic_name,
742  bool no_mangle) const
743 {
744  return get_info_by_topic<kPublisherEndpointTypeName>(
745  node_base_,
746  topic_name,
747  no_mangle,
749 }
750 
751 static constexpr char kSubscriptionEndpointTypeName[] = "subscriptions";
752 std::vector<rclcpp::TopicEndpointInfo>
754  const std::string & topic_name,
755  bool no_mangle) const
756 {
757  return get_info_by_topic<kSubscriptionEndpointTypeName>(
758  node_base_,
759  topic_name,
760  no_mangle,
762 }
763 
764 std::string &
766 {
767  return node_name_;
768 }
769 
770 const std::string &
772 {
773  return node_name_;
774 }
775 
776 std::string &
778 {
779  return node_namespace_;
780 }
781 
782 const std::string &
784 {
785  return node_namespace_;
786 }
787 
788 std::string &
790 {
791  return topic_type_;
792 }
793 
794 const std::string &
796 {
797  return topic_type_;
798 }
799 
800 rclcpp::EndpointType &
802 {
803  return endpoint_type_;
804 }
805 
806 const rclcpp::EndpointType &
808 {
809  return endpoint_type_;
810 }
811 
812 std::array<uint8_t, RMW_GID_STORAGE_SIZE> &
814 {
815  return endpoint_gid_;
816 }
817 
818 const std::array<uint8_t, RMW_GID_STORAGE_SIZE> &
820 {
821  return endpoint_gid_;
822 }
823 
824 rclcpp::QoS &
826 {
827  return qos_profile_;
828 }
829 
830 const rclcpp::QoS &
832 {
833  return qos_profile_;
834 }
835 
836 rosidl_type_hash_t &
838 {
839  return topic_type_hash_;
840 }
841 
842 const rosidl_type_hash_t &
844 {
845  return topic_type_hash_;
846 }
#define rcl_get_default_allocator
Return a properly initialized rcl_allocator_t with default values.
Definition: allocator.h:37
rcutils_allocator_t rcl_allocator_t
Encapsulation of an allocator.
Definition: allocator.h:31
Encapsulation of Quality of Service settings.
Definition: qos.hpp:116
RCLCPP_PUBLIC std::string & node_name()
Get a mutable reference to the node name.
Definition: node_graph.cpp:765
RCLCPP_PUBLIC std::string & node_namespace()
Get a mutable reference to the node namespace.
Definition: node_graph.cpp:777
RCLCPP_PUBLIC rosidl_type_hash_t & topic_type_hash()
Get a mutable reference to the type hash of the topic endpoint.
Definition: node_graph.cpp:837
RCLCPP_PUBLIC std::string & topic_type()
Get a mutable reference to the topic type string.
Definition: node_graph.cpp:789
RCLCPP_PUBLIC rclcpp::QoS & qos_profile()
Get a mutable reference to the QoS profile of the topic endpoint.
Definition: node_graph.cpp:825
RCLCPP_PUBLIC rclcpp::EndpointType & endpoint_type()
Get a mutable reference to the topic endpoint type.
Definition: node_graph.cpp:801
RCLCPP_PUBLIC std::array< uint8_t, RMW_GID_STORAGE_SIZE > & endpoint_gid()
Get a mutable reference to the GID of the topic endpoint.
Definition: node_graph.cpp:813
Thrown when an unregistered rclcpp::Event is encountered where a registered one was expected.
Definition: exceptions.hpp:236
Thrown when an invalid rclcpp::Event object or SharedPtr is encountered.
Definition: exceptions.hpp:228
Created when the return code does not match one of the other specialized exceptions.
Definition: exceptions.hpp:162
Notifies many nodes of graph changes by listening in a thread.
Pure virtual interface class for the NodeBase part of the Node API.
virtual RCLCPP_PUBLIC void trigger_notify_guard_condition()=0
Trigger the guard condition that notifies of internal node state changes.
virtual RCLCPP_PUBLIC rcl_node_t * get_rcl_node_handle()=0
Return the rcl_node_t node handle (non-const version).
virtual RCLCPP_PUBLIC rclcpp::Context::SharedPtr get_context()=0
Return the context of the node.
Implementation the NodeGraph part of the Node API.
Definition: node_graph.hpp:51
RCLCPP_PUBLIC std::vector< std::string > get_node_names() const override
Return a vector of existing node names (string).
Definition: node_graph.cpp:293
RCLCPP_PUBLIC std::map< std::string, std::vector< std::string > > get_service_names_and_types() const override
Return a map of existing service names to list of service types.
Definition: node_graph.cpp:100
RCLCPP_PUBLIC std::map< std::string, std::vector< std::string > > get_publisher_names_and_types_by_node(const std::string &node_name, const std::string &namespace_, bool no_demangle=false) const override
Return a map of existing topic names to list of topic types for a specific node.
Definition: node_graph.cpp:219
RCLCPP_PUBLIC std::vector< rclcpp::TopicEndpointInfo > get_publishers_info_by_topic(const std::string &topic_name, bool no_mangle=false) const override
Return the topic endpoint information about publishers on a given topic.
Definition: node_graph.cpp:740
RCLCPP_PUBLIC std::vector< std::pair< std::string, std::string > > get_node_names_and_namespaces() const override
Return a vector of existing node names and namespaces (pair of string).
Definition: node_graph.cpp:396
RCLCPP_PUBLIC size_t count_services(const std::string &service_name) const override
Return the number of services created for a given service.
Definition: node_graph.cpp:524
RCLCPP_PUBLIC std::map< std::string, std::vector< std::string > > get_subscriber_names_and_types_by_node(const std::string &node_name, const std::string &namespace_, bool no_demangle=false) const override
Return a map of existing topic names to list of topic types for a specific node.
Definition: node_graph.cpp:256
RCLCPP_PUBLIC size_t count_subscribers(const std::string &topic_name) const override
Return the number of subscribers who have created a subscription for a given topic.
Definition: node_graph.cpp:480
RCLCPP_PUBLIC std::map< std::string, std::vector< std::string > > get_client_names_and_types_by_node(const std::string &node_name, const std::string &namespace_) const override
Return a map of existing service names and types with a specific node.
Definition: node_graph.cpp:184
RCLCPP_PUBLIC std::vector< rclcpp::TopicEndpointInfo > get_subscriptions_info_by_topic(const std::string &topic_name, bool no_mangle=false) const override
Return the topic endpoint information about subscriptions on a given topic.
Definition: node_graph.cpp:753
RCLCPP_PUBLIC size_t count_graph_users() const override
Return the number of on loan graph events, see get_graph_event().
Definition: node_graph.cpp:645
RCLCPP_PUBLIC std::map< std::string, std::vector< std::string > > get_topic_names_and_types(bool no_demangle=false) const override
Return a map of existing topic names to list of topic types.
Definition: node_graph.cpp:58
RCLCPP_PUBLIC void wait_for_graph_change(rclcpp::Event::SharedPtr event, std::chrono::nanoseconds timeout) override
Wait for a graph event to occur by waiting on an Event to become set.
Definition: node_graph.cpp:613
RCLCPP_PUBLIC std::map< std::string, std::vector< std::string > > get_service_names_and_types_by_node(const std::string &node_name, const std::string &namespace_) const override
Return a map of existing service names to list of service types for a specific node.
Definition: node_graph.cpp:142
RCLCPP_PUBLIC rclcpp::Event::SharedPtr get_graph_event() override
Return a graph event, which will be set anytime a graph change occurs.
Definition: node_graph.cpp:596
RCLCPP_PUBLIC void notify_shutdown() override
Notify any and all blocking node actions that shutdown has occurred.
Definition: node_graph.cpp:589
RCLCPP_PUBLIC std::vector< std::tuple< std::string, std::string, std::string > > get_node_names_with_enclaves() const override
Return a vector of existing node names, namespaces and enclaves (tuple of string).
Definition: node_graph.cpp:321
RCLCPP_PUBLIC size_t count_publishers(const std::string &topic_name) const override
Return the number of publishers that are advertised on a given topic.
Definition: node_graph.cpp:458
RCLCPP_PUBLIC size_t count_clients(const std::string &service_name) const override
Return the number of clients created for a given service.
Definition: node_graph.cpp:502
RCLCPP_PUBLIC void notify_graph_change() override
Notify threads waiting on graph changes.
Definition: node_graph.cpp:552
RCLCPP_PUBLIC const rcl_guard_condition_t * get_graph_guard_condition() const override
Return the rcl guard condition which is triggered when the ROS graph changes.
Definition: node_graph.cpp:546
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_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
rmw_topic_endpoint_info_array_t rcl_topic_endpoint_info_array_t
An array of topic endpoint information.
Definition: graph.h:48
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_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
#define rcl_topic_endpoint_info_array_fini
Finalize a topic_endpoint_info_array_t structure.
Definition: graph.h:58
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
#define rcl_get_zero_initialized_names_and_types
Return a zero-initialized rcl_names_and_types_t structure.
Definition: graph.h:51
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
#define rcl_get_zero_initialized_topic_endpoint_info_array
Return a zero-initialized rcl_topic_endpoint_info_t structure.
Definition: graph.h:54
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
RCLCPP_PUBLIC std::string expand_topic_or_service_name(const std::string &name, const std::string &node_name, const std::string &namespace_, bool is_service=false)
Expand a topic or service name and throw if it is not valid.
RCLCPP_PUBLIC bool ok(rclcpp::Context::SharedPtr context=nullptr)
Check rclcpp's status.
RCLCPP_PUBLIC Logger get_logger(const std::string &name)
Return a named logger.
Definition: logger.cpp:34
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 RCL_WARN_UNUSED const char * rcl_node_get_name(const rcl_node_t *node)
Return the name of the node.
Definition: node.c:408
RCL_PUBLIC RCL_WARN_UNUSED const char * rcl_node_get_namespace(const rcl_node_t *node)
Return the namespace of the node.
Definition: node.c:417
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 rcl_ret_t rcl_remap_topic_name(const rcl_arguments_t *local_arguments, const rcl_arguments_t *global_arguments, const char *topic_name, const char *node_name, const char *node_namespace, rcl_allocator_t allocator, char **output_name)
Remap a topic name based on given rules.
Definition: remap.c:233
Hold output of parsing command line arguments.
Definition: arguments.h:36
Handle for a rcl guard condition.
Structure which encapsulates the options for creating a rcl_node_t.
Definition: node_options.h:35
bool use_global_arguments
If false then only use arguments in this struct, otherwise use global arguments also.
Definition: node_options.h:47
rcl_arguments_t arguments
Command line arguments that apply only to this node.
Definition: node_options.h:50
rcl_allocator_t allocator
If true, no parameter infrastructure will be setup.
Definition: node_options.h:44
#define RCL_RET_UNSUPPORTED
Unsupported return code.
Definition: types.h:37
#define RCL_RET_OK
Success return code.
Definition: types.h:27
rmw_ret_t rcl_ret_t
The type that holds an rcl return code.
Definition: types.h:24