ROS 2 rclcpp + rcl - jazzy  jazzy
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  }
136  }
137  }
138  if (rcl_node_fini(node) != RCL_RET_OK) {
139  RCUTILS_LOG_ERROR_NAMED(
140  "rclcpp",
141  "Error in destruction of rcl node handle: %s", rcl_get_error_string().str);
142  }
143  delete node;
144  });
145 
146  // Create the default callback group, if needed.
147  if (nullptr == default_callback_group_) {
148  using rclcpp::CallbackGroupType;
149  // Default callback group is mutually exclusive and automatically associated with
150  // any executors that this node is added to.
151  default_callback_group_ =
152  NodeBase::create_callback_group(CallbackGroupType::MutuallyExclusive, true);
153  }
154 
155  // Indicate the notify_guard_condition is now valid.
156  notify_guard_condition_is_valid_ = true;
157 }
158 
159 NodeBase::~NodeBase()
160 {
161  // Finalize the interrupt guard condition after removing self from graph listener.
162  {
163  std::lock_guard<std::recursive_mutex> notify_condition_lock(notify_guard_condition_mutex_);
164  notify_guard_condition_is_valid_ = false;
165  }
166 }
167 
168 const char *
170 {
171  return rcl_node_get_name(node_handle_.get());
172 }
173 
174 const char *
176 {
177  return rcl_node_get_namespace(node_handle_.get());
178 }
179 
180 const char *
182 {
183  return rcl_node_get_fully_qualified_name(node_handle_.get());
184 }
185 
186 rclcpp::Context::SharedPtr
188 {
189  return context_;
190 }
191 
192 rcl_node_t *
194 {
195  return node_handle_.get();
196 }
197 
198 const rcl_node_t *
200 {
201  return node_handle_.get();
202 }
203 
204 std::shared_ptr<rcl_node_t>
206 {
207  return std::shared_ptr<rcl_node_t>(shared_from_this(), node_handle_.get());
208 }
209 
210 std::shared_ptr<const rcl_node_t>
212 {
213  return std::shared_ptr<const rcl_node_t>(shared_from_this(), node_handle_.get());
214 }
215 
216 rclcpp::CallbackGroup::SharedPtr
218  rclcpp::CallbackGroupType group_type,
219  bool automatically_add_to_executor_with_node)
220 {
221  auto group = std::make_shared<rclcpp::CallbackGroup>(
222  group_type,
223  context_->weak_from_this(),
224  automatically_add_to_executor_with_node);
225  std::lock_guard<std::mutex> lock(callback_groups_mutex_);
226  callback_groups_.push_back(group);
227 
228  // This guard condition is generally used to signal to this node's executor that a callback
229  // group has been added that should be considered for new entities.
230  // If this is creating the default callback group, then the notify guard condition won't be
231  // ready or needed yet, as the node is not done being constructed and therefore cannot be added.
232  // If the callback group is not automatically associated with this node's executors, then
233  // triggering the guard condition is also unnecessary, it will be manually added to an exector.
234  if (notify_guard_condition_is_valid_ && automatically_add_to_executor_with_node) {
236  }
237  return group;
238 }
239 
240 rclcpp::CallbackGroup::SharedPtr
242 {
243  return default_callback_group_;
244 }
245 
246 bool
247 NodeBase::callback_group_in_node(rclcpp::CallbackGroup::SharedPtr group)
248 {
249  std::lock_guard<std::mutex> lock(callback_groups_mutex_);
250  for (auto & weak_group : this->callback_groups_) {
251  auto cur_group = weak_group.lock();
252  if (cur_group && (cur_group == group)) {
253  return true;
254  }
255  }
256  return false;
257 }
258 
259 void NodeBase::for_each_callback_group(const CallbackGroupFunction & func)
260 {
261  std::lock_guard<std::mutex> lock(callback_groups_mutex_);
262  for (rclcpp::CallbackGroup::WeakPtr & weak_group : this->callback_groups_) {
263  rclcpp::CallbackGroup::SharedPtr group = weak_group.lock();
264  if (group) {
265  func(group);
266  }
267  }
268 }
269 
270 std::atomic_bool &
272 {
273  return associated_with_executor_;
274 }
275 
278 {
279  std::lock_guard<std::recursive_mutex> notify_condition_lock(notify_guard_condition_mutex_);
280  if (!notify_guard_condition_is_valid_) {
281  throw std::runtime_error("failed to get notify guard condition because it is invalid");
282  }
283  return *notify_guard_condition_;
284 }
285 
286 rclcpp::GuardCondition::SharedPtr
288 {
289  std::lock_guard<std::recursive_mutex> notify_condition_lock(notify_guard_condition_mutex_);
290  if (!notify_guard_condition_is_valid_) {
291  return nullptr;
292  }
293  return notify_guard_condition_;
294 }
295 
296 void
298 {
299  std::lock_guard<std::recursive_mutex> notify_condition_lock(notify_guard_condition_mutex_);
300  if (!notify_guard_condition_is_valid_) {
301  throw std::runtime_error("failed to trigger notify guard condition because it is invalid");
302  }
303  notify_guard_condition_->trigger();
304 }
305 
306 bool
308 {
309  return use_intra_process_default_;
310 }
311 
312 bool
314 {
315  return enable_topic_statistics_default_;
316 }
317 
318 std::string
320  const std::string & name, bool is_service, bool only_expand) const
321 {
322  char * output_cstr = NULL;
323  auto allocator = rcl_get_default_allocator();
325  node_handle_.get(),
326  name.c_str(),
327  allocator,
328  is_service,
329  only_expand,
330  &output_cstr);
331  if (RCL_RET_OK != ret) {
332  throw_from_rcl_error(ret, "failed to resolve name", rcl_get_error_state());
333  }
334  std::string output{output_cstr};
335  allocator.deallocate(output_cstr, allocator.state);
336  return output;
337 }
#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:169
bool get_enable_topic_statistics_default() const override
Return the default preference for enabling topic statistics collection.
Definition: node_base.cpp:313
RCLCPP_PUBLIC bool get_use_intra_process_default() const override
Return the default preference for using intra process communication.
Definition: node_base.cpp:307
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:287
RCLCPP_PUBLIC rclcpp::CallbackGroup::SharedPtr get_default_callback_group() override
Return the default callback group.
Definition: node_base.cpp:241
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:217
RCLCPP_PUBLIC rcl_node_t * get_rcl_node_handle() override
Return the rcl_node_t node handle (non-const version).
Definition: node_base.cpp:193
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:319
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:277
RCLCPP_PUBLIC const char * get_namespace() const override
Return the namespace of the node.
Definition: node_base.cpp:175
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:205
RCLCPP_PUBLIC void trigger_notify_guard_condition() override
Trigger the guard condition that notifies of internal node state changes.
Definition: node_base.cpp:297
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:259
RCLCPP_PUBLIC rclcpp::Context::SharedPtr get_context() override
Return the context of the node.
Definition: node_base.cpp:187
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:247
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:271
RCLCPP_PUBLIC const char * get_fully_qualified_name() const override
Return the fully qualified name of the node.
Definition: node_base.cpp:181
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:99
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:411
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_node_fini(rcl_node_t *node)
Finalize a rcl_node_t.
Definition: node.c:344
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:420
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:429
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:109
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