ROS 2 rclcpp + rcl - rolling  rolling-e7363f10
ROS 2 C++ Client Library with ROS Client Library
subscription.hpp
1 // Copyright 2014 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 #ifndef RCLCPP__SUBSCRIPTION_HPP_
16 #define RCLCPP__SUBSCRIPTION_HPP_
17 
18 #include <rmw/error_handling.h>
19 #include <rmw/rmw.h>
20 
21 #include <functional>
22 #include <memory>
23 #include <string>
24 #include <utility>
25 
26 #include "rcl/error_handling.h"
27 #include "rcl/subscription.h"
28 
29 #include "rclcpp/any_subscription_callback.hpp"
30 #include "rclcpp/detail/resolve_use_intra_process.hpp"
31 #include "rclcpp/detail/resolve_intra_process_buffer_type.hpp"
32 #include "rclcpp/exceptions.hpp"
33 #include "rclcpp/expand_topic_or_service_name.hpp"
34 #include "rclcpp/experimental/intra_process_manager.hpp"
35 #include "rclcpp/experimental/subscription_intra_process.hpp"
36 #include "rclcpp/logging.hpp"
37 #include "rclcpp/macros.hpp"
38 #include "rclcpp/message_info.hpp"
39 #include "rclcpp/message_memory_strategy.hpp"
40 #include "rclcpp/node_interfaces/node_base_interface.hpp"
41 #include "rclcpp/subscription_base.hpp"
42 #include "rclcpp/subscription_options.hpp"
43 #include "rclcpp/type_support_decl.hpp"
44 #include "rclcpp/visibility_control.hpp"
45 #include "rclcpp/waitable.hpp"
46 #include "tracetools/tracetools.h"
47 
48 namespace rclcpp
49 {
50 
51 namespace node_interfaces
52 {
53 class NodeTopicsInterface;
54 } // namespace node_interfaces
55 
57 template<
58  typename MessageT,
59  typename AllocatorT = std::allocator<void>,
62  typename SubscribedT = typename rclcpp::TypeAdapter<MessageT>::custom_type,
65  typename ROSMessageT = typename rclcpp::TypeAdapter<MessageT>::ros_message_type,
66  typename MessageMemoryStrategyT = rclcpp::message_memory_strategy::MessageMemoryStrategy<
67  ROSMessageT,
68  AllocatorT
69  >>
71 {
73 
74 public:
75  // Redeclare these here to use outside of the class.
76  using SubscribedType = SubscribedT;
77  using ROSMessageType = ROSMessageT;
78  using MessageMemoryStrategyType = MessageMemoryStrategyT;
79 
80  using SubscribedTypeAllocatorTraits = allocator::AllocRebind<SubscribedType, AllocatorT>;
81  using SubscribedTypeAllocator = typename SubscribedTypeAllocatorTraits::allocator_type;
82  using SubscribedTypeDeleter = allocator::Deleter<SubscribedTypeAllocator, SubscribedType>;
83 
84  using ROSMessageTypeAllocatorTraits = allocator::AllocRebind<ROSMessageType, AllocatorT>;
85  using ROSMessageTypeAllocator = typename ROSMessageTypeAllocatorTraits::allocator_type;
86  using ROSMessageTypeDeleter = allocator::Deleter<ROSMessageTypeAllocator, ROSMessageType>;
87 
88 public:
89  RCLCPP_SMART_PTR_DEFINITIONS(Subscription)
90 
91 
108  // *INDENT-OFF*
110  rclcpp::node_interfaces::NodeBaseInterface * node_base,
111  const rosidl_message_type_support_t & type_support_handle,
112  const std::string & topic_name,
113  const rclcpp::QoS & qos,
114  AnySubscriptionCallback<MessageT, AllocatorT> callback,
115  const rclcpp::SubscriptionOptionsWithAllocator<AllocatorT> & options,
116  typename MessageMemoryStrategyT::SharedPtr message_memory_strategy)
118  node_base,
119  type_support_handle,
120  topic_name,
121  options.to_rcl_subscription_options(qos),
122  // NOTE(methylDragon): Passing these args separately is necessary for event binding
123  options.event_callbacks,
124  options.use_default_callbacks,
125  callback.is_serialized_message_callback() ? DeliveredMessageKind::SERIALIZED_MESSAGE : DeliveredMessageKind::ROS_MESSAGE), // NOLINT
126  any_callback_(callback),
127  options_(options),
128  message_memory_strategy_(message_memory_strategy)
129  // *INDENT-ON*
130  {
131  // Setup intra process publishing if requested.
132  if (rclcpp::detail::resolve_use_intra_process(options_, *node_base)) {
133  using rclcpp::detail::resolve_intra_process_buffer_type;
134 
135  // Check if the QoS is compatible with intra-process.
136  auto qos_profile = get_actual_qos();
137  if (qos_profile.history() != rclcpp::HistoryPolicy::KeepLast) {
138  throw std::invalid_argument(
139  "intraprocess communication on topic '" + topic_name +
140  "' allowed only with keep last history qos policy");
141  }
142  if (qos_profile.depth() == 0) {
143  throw std::invalid_argument(
144  "intraprocess communication on topic '" + topic_name +
145  "' is not allowed with 0 depth qos policy");
146  }
147 
148  using SubscriptionIntraProcessT = rclcpp::experimental::SubscriptionIntraProcess<
149  MessageT,
150  SubscribedType,
151  SubscribedTypeAllocator,
152  SubscribedTypeDeleter,
153  ROSMessageT,
154  AllocatorT>;
155 
156  // First create a SubscriptionIntraProcess which will be given to the intra-process manager.
157  auto context = node_base->get_context();
158  subscription_intra_process_ = std::make_shared<SubscriptionIntraProcessT>(
159  callback,
160  options_.get_allocator(),
161  context,
162  this->get_topic_name(), // important to get like this, as it has the fully-qualified name
163  qos_profile,
164  resolve_intra_process_buffer_type(options_.intra_process_buffer_type, callback));
165  TRACETOOLS_TRACEPOINT(
166  rclcpp_subscription_init,
167  static_cast<const void *>(get_subscription_handle().get()),
168  static_cast<const void *>(subscription_intra_process_.get()));
169 
170  // Add it to the intra process manager.
172  auto ipm = context->get_sub_context<IntraProcessManager>();
173  uint64_t intra_process_subscription_id = ipm->template add_subscription<
174  ROSMessageType, ROSMessageTypeAllocator>(subscription_intra_process_);
175  this->setup_intra_process(intra_process_subscription_id, ipm);
176  }
177 
178  TRACETOOLS_TRACEPOINT(
179  rclcpp_subscription_init,
180  static_cast<const void *>(get_subscription_handle().get()),
181  static_cast<const void *>(this));
182  TRACETOOLS_TRACEPOINT(
183  rclcpp_subscription_callback_added,
184  static_cast<const void *>(this),
185  static_cast<const void *>(&any_callback_));
186  // The callback object gets copied, so if registration is done too early/before this point
187  // (e.g. in `AnySubscriptionCallback::set()`), its address won't match any address used later
188  // in subsequent tracepoints.
189 #ifndef TRACETOOLS_DISABLED
190  any_callback_.register_callback_for_tracing();
191 #endif
192  }
193 
195  void
197  [[maybe_unused]] rclcpp::node_interfaces::NodeBaseInterface * node_base,
198  [[maybe_unused]] const rclcpp::QoS & qos,
199  [[maybe_unused]] const rclcpp::SubscriptionOptionsWithAllocator<AllocatorT> & options)
200  {
201  // This function is intentionally left empty.
202  }
203 
205 
222  bool
223  take(ROSMessageType & message_out, rclcpp::MessageInfo & message_info_out)
224  {
225  return this->take_type_erased(static_cast<void *>(&message_out), message_info_out);
226  }
227 
229 
235  template<typename TakeT>
236  std::enable_if_t<
237  !rosidl_generator_traits::is_message<TakeT>::value &&
238  std::is_same_v<TakeT, SubscribedType>,
239  bool
240  >
241  take(TakeT & message_out, rclcpp::MessageInfo & message_info_out)
242  {
243  ROSMessageType local_message;
244  bool taken = this->take_type_erased(static_cast<void *>(&local_message), message_info_out);
245  if (taken) {
246  rclcpp::TypeAdapter<MessageT>::convert_to_custom(local_message, message_out);
247  }
248  return taken;
249  }
250 
251  std::shared_ptr<void>
252  create_message() override
253  {
254  /* The default message memory strategy provides a dynamically allocated message on each call to
255  * create_message, though alternative memory strategies that re-use a preallocated message may be
256  * used (see rclcpp/strategies/message_pool_memory_strategy.hpp).
257  */
258  return message_memory_strategy_->borrow_message();
259  }
260 
261  std::shared_ptr<rclcpp::SerializedMessage>
263  {
264  return message_memory_strategy_->borrow_serialized_message();
265  }
266 
268 
276  void
277  disable_callbacks() override
278  {
280  any_callback_.disable();
281  if (subscription_intra_process_) {
282  subscription_intra_process_->disable_callbacks();
283  }
284  for (const auto & [_, event_ptr] : event_handlers_) {
285  if (event_ptr) {
286  event_ptr->disable();
287  }
288  }
289  }
290 
292 
296  void
297  enable_callbacks() override
298  {
300  any_callback_.enable();
301  if (subscription_intra_process_) {
302  subscription_intra_process_->enable_callbacks();
303  }
304  for (const auto & [_, event_ptr] : event_handlers_) {
305  if (event_ptr) {
306  event_ptr->enable();
307  }
308  }
309  }
310 
311  void
313  std::shared_ptr<void> & message,
314  const rclcpp::MessageInfo & message_info) override
315  {
316  if (matches_any_intra_process_publishers(&message_info.get_rmw_message_info().publisher_gid)) {
317  // In this case, the message will be delivered via intra process and
318  // we should ignore this copy of the message.
319  return;
320  }
321  auto typed_message = std::static_pointer_cast<ROSMessageType>(message);
322  any_callback_.dispatch(typed_message, message_info);
323  }
324 
325  void
326  handle_serialized_message(
327  const std::shared_ptr<rclcpp::SerializedMessage> & serialized_message,
328  const rclcpp::MessageInfo & message_info) override
329  {
330  any_callback_.dispatch(serialized_message, message_info);
331  }
332 
333  void
334  handle_loaned_message(
335  void * loaned_message,
336  const rclcpp::MessageInfo & message_info) override
337  {
338  if (matches_any_intra_process_publishers(&message_info.get_rmw_message_info().publisher_gid)) {
339  // In this case, the message will be delivered via intra process and
340  // we should ignore this copy of the message.
341  return;
342  }
343 
344  auto typed_message = static_cast<ROSMessageType *>(loaned_message);
345  // message is loaned, so we have to make sure that the deleter does not deallocate the message
346  auto sptr = std::shared_ptr<ROSMessageType>(
347  typed_message, [](ROSMessageType * msg) {(void) msg;});
348  any_callback_.dispatch(sptr, message_info);
349  }
350 
352 
355  void
356  return_message(std::shared_ptr<void> & message) override
357  {
358  auto typed_message = std::static_pointer_cast<ROSMessageType>(message);
359  message_memory_strategy_->return_message(typed_message);
360  }
361 
363 
366  void
367  return_serialized_message(std::shared_ptr<rclcpp::SerializedMessage> & message) override
368  {
369  message_memory_strategy_->return_serialized_message(message);
370  }
371 
372  bool
373  use_take_shared_method() const
374  {
375  return any_callback_.use_take_shared_method();
376  }
377 
378  // DYNAMIC TYPE ==================================================================================
379  // TODO(methylDragon): Reorder later
380  // TODO(methylDragon): Implement later...
381  rclcpp::dynamic_typesupport::DynamicMessageType::SharedPtr
382  get_shared_dynamic_message_type() override
383  {
385  "get_shared_dynamic_message_type is not implemented for Subscription");
386  }
387 
388  rclcpp::dynamic_typesupport::DynamicMessage::SharedPtr
389  get_shared_dynamic_message() override
390  {
392  "get_shared_dynamic_message is not implemented for Subscription");
393  }
394 
395  rclcpp::dynamic_typesupport::DynamicSerializationSupport::SharedPtr
396  get_shared_dynamic_serialization_support() override
397  {
399  "get_shared_dynamic_serialization_support is not implemented for Subscription");
400  }
401 
402  rclcpp::dynamic_typesupport::DynamicMessage::SharedPtr
404  {
406  "create_dynamic_message is not implemented for Subscription");
407  }
408 
409  void
410  return_dynamic_message(
411  [[maybe_unused]] rclcpp::dynamic_typesupport::DynamicMessage::SharedPtr & message) override
412  {
414  "return_dynamic_message is not implemented for Subscription");
415  }
416 
417  void
418  handle_dynamic_message(
419  [[maybe_unused]] const rclcpp::dynamic_typesupport::DynamicMessage::SharedPtr & message,
420  [[maybe_unused]] const rclcpp::MessageInfo & message_info) override
421  {
423  "handle_dynamic_message is not implemented for Subscription");
424  }
425 
426 private:
427  RCLCPP_DISABLE_COPY(Subscription)
428 
429  AnySubscriptionCallback<MessageT, AllocatorT> any_callback_;
431 
436  typename message_memory_strategy::MessageMemoryStrategy<ROSMessageType, AllocatorT>::SharedPtr
437  message_memory_strategy_;
438 };
439 
440 } // namespace rclcpp
441 
442 #endif // RCLCPP__SUBSCRIPTION_HPP_
Additional meta data about messages taken from subscriptions.
const rmw_message_info_t & get_rmw_message_info() const
Return the message info as the underlying rmw message info type.
Encapsulation of Quality of Service settings.
Definition: qos.hpp:114
RCLCPP_PUBLIC rclcpp::QoS get_actual_qos() const
Get the actual QoS settings, after the defaults have been determined.
virtual RCLCPP_PUBLIC void enable_callbacks()
Enable the callbacks to be called.
RCLCPP_PUBLIC void setup_intra_process(uint64_t intra_process_subscription_id, IntraProcessManagerWeakPtr weak_ipm)
Implemenation detail.
virtual RCLCPP_PUBLIC void disable_callbacks()
Disable callbacks from being called.
RCLCPP_PUBLIC bool take_type_erased(void *message_out, rclcpp::MessageInfo &message_info_out)
Take the next inter-process message from the subscription as a type erased pointer.
Subscription implementation, templated on the type of message this subscription receives.
std::enable_if_t< !rosidl_generator_traits::is_message< TakeT >::value &&std::is_same_v< TakeT, SubscribedType >, bool > take(TakeT &message_out, rclcpp::MessageInfo &message_info_out)
Take the next message from the inter-process subscription.
void enable_callbacks() override
Enable the callbacks to be called.
void disable_callbacks() override
Disable callbacks from being called.
void return_serialized_message(std::shared_ptr< rclcpp::SerializedMessage > &message) override
Return the borrowed serialized message.
std::shared_ptr< rclcpp::SerializedMessage > create_serialized_message() override
Borrow a new serialized message.
Subscription(rclcpp::node_interfaces::NodeBaseInterface *node_base, const rosidl_message_type_support_t &type_support_handle, const std::string &topic_name, const rclcpp::QoS &qos, AnySubscriptionCallback< MessageT, AllocatorT > callback, const rclcpp::SubscriptionOptionsWithAllocator< AllocatorT > &options, typename MessageMemoryStrategyT::SharedPtr message_memory_strategy)
Default constructor.
void return_message(std::shared_ptr< void > &message) override
Return the borrowed message.
rclcpp::dynamic_typesupport::DynamicMessage::SharedPtr create_dynamic_message() override
Borrow a new serialized message (this clones!)
void post_init_setup([[maybe_unused]] rclcpp::node_interfaces::NodeBaseInterface *node_base, [[maybe_unused]] const rclcpp::QoS &qos, [[maybe_unused]] const rclcpp::SubscriptionOptionsWithAllocator< AllocatorT > &options)
Called after construction to continue setup that requires shared_from_this().
void handle_message(std::shared_ptr< void > &message, const rclcpp::MessageInfo &message_info) override
Check if we need to handle the message, and execute the callback if we do.
std::shared_ptr< void > create_message() override
Borrow a new message.
bool take(ROSMessageType &message_out, rclcpp::MessageInfo &message_info_out)
Take the next message from the inter-process subscription.
This class performs intra process communication between nodes.
Default allocation strategy for messages received by subscriptions.
Pure virtual interface class for the NodeBase part of the Node API.
Pure virtual interface class for the NodeTopics part of the Node API.
Versions of rosidl_typesupport_cpp::get_message_type_support_handle that handle adapted types.
DeliveredMessageKind
The kind of message that the subscription delivers in its callback, used by the executor.
Structure containing optional configuration for Subscriptions.
Template structure used to adapt custom, user-defined types to ROS types.