ROS 2 rclcpp + rcl - kilted  kilted
ROS 2 C++ Client Library with ROS Client Library
node_base.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 <string>
16 #include <limits>
17 #include <memory>
18 #include <vector>
19 
20 #include "rclcpp/node_interfaces/node_base.hpp"
21 
22 #include "rcl/arguments.h"
23 #include "rcl/node_type_cache.h"
24 #include "rcl/logging.h"
25 #include "rcl/logging_rosout.h"
26 #include "rclcpp/exceptions.hpp"
27 #include "rcutils/logging_macros.h"
28 #include "rmw/validate_namespace.h"
29 #include "rmw/validate_node_name.h"
30 
31 #include "../logging_mutex.hpp"
32 
33 using rclcpp::exceptions::throw_from_rcl_error;
34 
36 
37 NodeBase::NodeBase(
38  const std::string & node_name,
39  const std::string & namespace_,
40  rclcpp::Context::SharedPtr context,
41  const rcl_node_options_t & rcl_node_options,
42  bool use_intra_process_default,
43  bool enable_topic_statistics_default,
44  rclcpp::CallbackGroup::SharedPtr default_callback_group)
45 : context_(context),
46  use_intra_process_default_(use_intra_process_default),
47  enable_topic_statistics_default_(enable_topic_statistics_default),
48  node_handle_(nullptr),
49  default_callback_group_(default_callback_group),
50  associated_with_executor_(false),
51  notify_guard_condition_(std::make_shared<rclcpp::GuardCondition>(context)),
52  notify_guard_condition_is_valid_(false)
53 {
54  // Create the rcl node and store it in a shared_ptr with a custom destructor.
55  std::unique_ptr<rcl_node_t> rcl_node(new rcl_node_t(rcl_get_zero_initialized_node()));
56 
57  std::shared_ptr<std::recursive_mutex> logging_mutex = get_global_logging_mutex();
58 
59  rcl_ret_t ret;
60 
61  // TODO(ivanpauno): /rosout Qos should be reconfigurable.
62  ret = rcl_node_init(
63  rcl_node.get(),
64  node_name.c_str(), namespace_.c_str(),
65  context_->get_rcl_context().get(), &rcl_node_options);
66  if (ret != RCL_RET_OK) {
67  if (ret == RCL_RET_NODE_INVALID_NAME) {
68  rcl_reset_error(); // discard rcl_node_init error
69  int validation_result;
70  size_t invalid_index;
71  rmw_ret_t rmw_ret =
72  rmw_validate_node_name(node_name.c_str(), &validation_result, &invalid_index);
73  if (rmw_ret != RMW_RET_OK) {
74  if (rmw_ret == RMW_RET_INVALID_ARGUMENT) {
75  throw_from_rcl_error(RCL_RET_INVALID_ARGUMENT, "failed to validate node name");
76  }
77  throw_from_rcl_error(RCL_RET_ERROR, "failed to validate node name");
78  }
79 
80  if (validation_result != RMW_NODE_NAME_VALID) {
82  node_name.c_str(),
83  rmw_node_name_validation_result_string(validation_result),
84  invalid_index);
85  } else {
86  throw std::runtime_error("valid rmw node name but invalid rcl node name");
87  }
88  }
89 
90  if (ret == RCL_RET_NODE_INVALID_NAMESPACE) {
91  rcl_reset_error(); // discard rcl_node_init error
92  int validation_result;
93  size_t invalid_index;
94  rmw_ret_t rmw_ret =
95  rmw_validate_namespace(namespace_.c_str(), &validation_result, &invalid_index);
96  if (rmw_ret != RMW_RET_OK) {
97  if (rmw_ret == RMW_RET_INVALID_ARGUMENT) {
98  throw_from_rcl_error(RCL_RET_INVALID_ARGUMENT, "failed to validate namespace");
99  }
100  throw_from_rcl_error(RCL_RET_ERROR, "failed to validate namespace");
101  }
102 
103  if (validation_result != RMW_NAMESPACE_VALID) {
105  namespace_.c_str(),
106  rmw_namespace_validation_result_string(validation_result),
107  invalid_index);
108  } else {
109  throw std::runtime_error("valid rmw node namespace but invalid rcl node namespace");
110  }
111  }
112  throw_from_rcl_error(ret, "failed to initialize rcl node");
113  }
114 
115  // The initialization for the rosout publisher
116  if (rcl_logging_rosout_enabled() && rcl_node_options.enable_rosout) {
117  std::lock_guard<std::recursive_mutex> guard(*logging_mutex);
118  ret = rcl_logging_rosout_init_publisher_for_node(rcl_node.get());
119  if (ret != RCL_RET_OK) {
120  throw_from_rcl_error(ret, "failed to initialize rosout publisher");
121  }
122  }
123 
124  node_handle_.reset(
125  rcl_node.release(),
126  [logging_mutex, rcl_node_options](rcl_node_t * node) -> void {
127  {
128  std::lock_guard<std::recursive_mutex> guard(*logging_mutex);
129  if (rcl_logging_rosout_enabled() && rcl_node_options.enable_rosout) {
130  rcl_ret_t ret = rcl_logging_rosout_fini_publisher_for_node(node);
131  if (ret != RCL_RET_OK) {
132  RCUTILS_LOG_ERROR_NAMED(
133  "rclcpp",
134  "Error in destruction of rosout publisher: %s", rcl_get_error_string().str);
135  rcl_reset_error();
136  }
137  }
138  }
139  if (rcl_node_fini(node) != RCL_RET_OK) {
140  RCUTILS_LOG_ERROR_NAMED(
141  "rclcpp",
142  "Error in destruction of rcl node handle: %s", rcl_get_error_string().str);
143  rcl_reset_error();
144  }
145  delete node;
146  });
147 
148  // Create the default callback group, if needed.
149  if (nullptr == default_callback_group_) {
150  using rclcpp::CallbackGroupType;
151  // Default callback group is mutually exclusive and automatically associated with
152  // any executors that this node is added to.
153  default_callback_group_ =
154  NodeBase::create_callback_group(CallbackGroupType::MutuallyExclusive, true);
155  }
156 
157  // Indicate the notify_guard_condition is now valid.
158  notify_guard_condition_is_valid_ = true;
159 }
160 
161 NodeBase::~NodeBase()
162 {
163  // Finalize the interrupt guard condition after removing self from graph listener.
164  {
165  std::lock_guard<std::recursive_mutex> notify_condition_lock(notify_guard_condition_mutex_);
166  notify_guard_condition_is_valid_ = false;
167  }
168 }
169 
170 const char *
172 {
173  return rcl_node_get_name(node_handle_.get());
174 }
175 
176 const char *
178 {
179  return rcl_node_get_namespace(node_handle_.get());
180 }
181 
182 const char *
184 {
185  return rcl_node_get_fully_qualified_name(node_handle_.get());
186 }
187 
188 rclcpp::Context::SharedPtr
190 {
191  return context_;
192 }
193 
194 rcl_node_t *
196 {
197  return node_handle_.get();
198 }
199 
200 const rcl_node_t *
202 {
203  return node_handle_.get();
204 }
205 
206 std::shared_ptr<rcl_node_t>
208 {
209  return std::shared_ptr<rcl_node_t>(shared_from_this(), node_handle_.get());
210 }
211 
212 std::shared_ptr<const rcl_node_t>
214 {
215  return std::shared_ptr<const rcl_node_t>(shared_from_this(), node_handle_.get());
216 }
217 
218 rclcpp::CallbackGroup::SharedPtr
220  rclcpp::CallbackGroupType group_type,
221  bool automatically_add_to_executor_with_node)
222 {
223  auto group = std::make_shared<rclcpp::CallbackGroup>(
224  group_type,
225  context_->weak_from_this(),
226  automatically_add_to_executor_with_node);
227  std::lock_guard<std::mutex> lock(callback_groups_mutex_);
228  callback_groups_.push_back(group);
229 
230  // This guard condition is generally used to signal to this node's executor that a callback
231  // group has been added that should be considered for new entities.
232  // If this is creating the default callback group, then the notify guard condition won't be
233  // ready or needed yet, as the node is not done being constructed and therefore cannot be added.
234  // If the callback group is not automatically associated with this node's executors, then
235  // triggering the guard condition is also unnecessary, it will be manually added to an exector.
236  if (notify_guard_condition_is_valid_ && automatically_add_to_executor_with_node) {
238  }
239  return group;
240 }
241 
242 rclcpp::CallbackGroup::SharedPtr
244 {
245  return default_callback_group_;
246 }
247 
248 bool
249 NodeBase::callback_group_in_node(rclcpp::CallbackGroup::SharedPtr group)
250 {
251  std::lock_guard<std::mutex> lock(callback_groups_mutex_);
252  for (auto & weak_group : this->callback_groups_) {
253  auto cur_group = weak_group.lock();
254  if (cur_group && (cur_group == group)) {
255  return true;
256  }
257  }
258  return false;
259 }
260 
261 void NodeBase::for_each_callback_group(const CallbackGroupFunction & func)
262 {
263  std::lock_guard<std::mutex> lock(callback_groups_mutex_);
264  for (rclcpp::CallbackGroup::WeakPtr & weak_group : this->callback_groups_) {
265  rclcpp::CallbackGroup::SharedPtr group = weak_group.lock();
266  if (group) {
267  func(group);
268  }
269  }
270 }
271 
272 std::atomic_bool &
274 {
275  return associated_with_executor_;
276 }
277 
280 {
281  std::lock_guard<std::recursive_mutex> notify_condition_lock(notify_guard_condition_mutex_);
282  if (!notify_guard_condition_is_valid_) {
283  throw std::runtime_error("failed to get notify guard condition because it is invalid");
284  }
285  return *notify_guard_condition_;
286 }
287 
288 rclcpp::GuardCondition::SharedPtr
290 {
291  std::lock_guard<std::recursive_mutex> notify_condition_lock(notify_guard_condition_mutex_);
292  if (!notify_guard_condition_is_valid_) {
293  return nullptr;
294  }
295  return notify_guard_condition_;
296 }
297 
298 void
300 {
301  std::lock_guard<std::recursive_mutex> notify_condition_lock(notify_guard_condition_mutex_);
302  if (!notify_guard_condition_is_valid_) {
303  throw std::runtime_error("failed to trigger notify guard condition because it is invalid");
304  }
305  notify_guard_condition_->trigger();
306 }
307 
308 bool
310 {
311  return use_intra_process_default_;
312 }
313 
314 bool
316 {
317  return enable_topic_statistics_default_;
318 }
319 
320 std::string
322  const std::string & name, bool is_service, bool only_expand) const
323 {
324  char * output_cstr = NULL;
325  auto allocator = rcl_get_default_allocator();
327  node_handle_.get(),
328  name.c_str(),
329  allocator,
330  is_service,
331  only_expand,
332  &output_cstr);
333  if (RCL_RET_OK != ret) {
334  throw_from_rcl_error(ret, "failed to resolve name", rcl_get_error_state());
335  }
336  std::string output{output_cstr};
337  allocator.deallocate(output_cstr, allocator.state);
338  return output;
339 }
#define rcl_get_default_allocator
Return a properly initialized rcl_allocator_t with default values.
Definition: allocator.h:37
A condition that can be waited on in a single wait set and asynchronously triggered.
Thrown when a node namespace is invalid.
Definition: exceptions.hpp:78
Thrown when a node name is invalid.
Definition: exceptions.hpp:69
Implementation of the NodeBase part of the Node API.
Definition: node_base.hpp:38
RCLCPP_PUBLIC const char * get_name() const override
Return the name of the node.
Definition: node_base.cpp:171
bool get_enable_topic_statistics_default() const override
Return the default preference for enabling topic statistics collection.
Definition: node_base.cpp:315
RCLCPP_PUBLIC bool get_use_intra_process_default() const override
Return the default preference for using intra process communication.
Definition: node_base.cpp:309
RCLCPP_PUBLIC rclcpp::GuardCondition::SharedPtr get_shared_notify_guard_condition() override
Return a guard condition that should be notified when the internal node state changes.
Definition: node_base.cpp:289
RCLCPP_PUBLIC rclcpp::CallbackGroup::SharedPtr get_default_callback_group() override
Return the default callback group.
Definition: node_base.cpp:243
RCLCPP_PUBLIC rclcpp::CallbackGroup::SharedPtr create_callback_group(rclcpp::CallbackGroupType group_type, bool automatically_add_to_executor_with_node=true) override
Create and return a callback group.
Definition: node_base.cpp:219
RCLCPP_PUBLIC rcl_node_t * get_rcl_node_handle() override
Return the rcl_node_t node handle (non-const version).
Definition: node_base.cpp:195
std::string resolve_topic_or_service_name(const std::string &name, bool is_service, bool only_expand=false) const override
Expand and remap a given topic or service name.
Definition: node_base.cpp:321
RCLCPP_PUBLIC rclcpp::GuardCondition & get_notify_guard_condition() override
Return a guard condition that should be notified when the internal node state changes.
Definition: node_base.cpp:279
RCLCPP_PUBLIC const char * get_namespace() const override
Return the namespace of the node.
Definition: node_base.cpp:177
RCLCPP_PUBLIC std::shared_ptr< rcl_node_t > get_shared_rcl_node_handle() override
Return the rcl_node_t node handle in a std::shared_ptr.
Definition: node_base.cpp:207
RCLCPP_PUBLIC void trigger_notify_guard_condition() override
Trigger the guard condition that notifies of internal node state changes.
Definition: node_base.cpp:299
RCLCPP_PUBLIC void for_each_callback_group(const CallbackGroupFunction &func) override
Iterate over the stored callback groups, calling the given function on each valid one.
Definition: node_base.cpp:261
RCLCPP_PUBLIC rclcpp::Context::SharedPtr get_context() override
Return the context of the node.
Definition: node_base.cpp:189
RCLCPP_PUBLIC bool callback_group_in_node(rclcpp::CallbackGroup::SharedPtr group) override
Return true if the given callback group is associated with this node.
Definition: node_base.cpp:249
RCLCPP_PUBLIC std::atomic_bool & get_associated_with_executor_atomic() override
Return the atomic bool which is used to ensure only one executor is used.
Definition: node_base.cpp:273
RCLCPP_PUBLIC const char * get_fully_qualified_name() const override
Return the fully qualified name of the node.
Definition: node_base.cpp:183
RCL_PUBLIC RCL_WARN_UNUSED bool rcl_logging_rosout_enabled(void)
See if logging rosout is enabled.
Definition: logging.c:148
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_logging_rosout_init_publisher_for_node(rcl_node_t *node)
Creates a rosout publisher for a node and registers it to be used by the logging system.
Versions of rosidl_typesupport_cpp::get_message_type_support_handle that handle adapted types.
RCL_PUBLIC RCL_WARN_UNUSED rcl_node_t rcl_get_zero_initialized_node(void)
Return a rcl_node_t struct with members initialized to NULL.
Definition: node.c:98
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 rcl_ret_t rcl_node_fini(rcl_node_t *node)
Finalize a rcl_node_t.
Definition: node.c:341
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
struct rcl_node_s rcl_node_t
Structure which encapsulates a ROS Node.
RCL_PUBLIC RCL_WARN_UNUSED const char * rcl_node_get_fully_qualified_name(const rcl_node_t *node)
Return the fully qualified name of the node.
Definition: node.c:426
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_node_init(rcl_node_t *node, const char *name, const char *namespace_, rcl_context_t *context, const rcl_node_options_t *options)
Initialize a ROS node.
Definition: node.c:106
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_node_resolve_name(const rcl_node_t *node, const char *input_name, rcl_allocator_t allocator, bool is_service, bool only_expand, char **output_name)
Expand a given name into a fully-qualified topic name and apply remapping rules.
Structure which encapsulates the options for creating a rcl_node_t.
Definition: node_options.h:35
bool enable_rosout
Flag to enable rosout for this node.
Definition: node_options.h:53
Structure which encapsulates a ROS Node.
Definition: node.h:45
#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_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