ROS 2 rclcpp + rcl - lyrical  lyrical
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 <chrono>
22 #include <functional>
23 #include <iostream>
24 #include <memory>
25 #include <sstream>
26 #include <string>
27 #include <utility>
28 
29 #include "rcl/error_handling.h"
30 #include "rcl/subscription.h"
31 
32 #include "rclcpp/any_subscription_callback.hpp"
33 #include "rclcpp/detail/resolve_use_intra_process.hpp"
34 #include "rclcpp/detail/resolve_intra_process_buffer_type.hpp"
35 #include "rclcpp/exceptions.hpp"
36 #include "rclcpp/expand_topic_or_service_name.hpp"
37 #include "rclcpp/experimental/intra_process_manager.hpp"
38 #include "rclcpp/experimental/subscription_intra_process.hpp"
39 #include "rclcpp/logging.hpp"
40 #include "rclcpp/macros.hpp"
41 #include "rclcpp/message_info.hpp"
42 #include "rclcpp/message_memory_strategy.hpp"
43 #include "rclcpp/node_interfaces/node_base_interface.hpp"
44 #include "rclcpp/subscription_base.hpp"
45 #include "rclcpp/subscription_options.hpp"
46 #include "rclcpp/subscription_traits.hpp"
47 #include "rclcpp/type_support_decl.hpp"
48 #include "rclcpp/visibility_control.hpp"
49 #include "rclcpp/waitable.hpp"
50 #include "rclcpp/topic_statistics/subscription_topic_statistics.hpp"
51 #include "tracetools/tracetools.h"
52 
53 namespace rclcpp
54 {
55 
56 namespace node_interfaces
57 {
58 class NodeTopicsInterface;
59 } // namespace node_interfaces
60 
62 template<
63  typename MessageT,
64  typename AllocatorT = std::allocator<void>,
67  typename SubscribedT = typename rclcpp::TypeAdapter<MessageT>::custom_type,
70  typename ROSMessageT = typename rclcpp::TypeAdapter<MessageT>::ros_message_type,
71  typename MessageMemoryStrategyT = rclcpp::message_memory_strategy::MessageMemoryStrategy<
72  ROSMessageT,
73  AllocatorT
74  >>
76 {
78 
79 public:
80  // Redeclare these here to use outside of the class.
81  using SubscribedType = SubscribedT;
82  using ROSMessageType = ROSMessageT;
83  using MessageMemoryStrategyType = MessageMemoryStrategyT;
84 
85  using SubscribedTypeAllocatorTraits = allocator::AllocRebind<SubscribedType, AllocatorT>;
86  using SubscribedTypeAllocator = typename SubscribedTypeAllocatorTraits::allocator_type;
87  using SubscribedTypeDeleter = allocator::Deleter<SubscribedTypeAllocator, SubscribedType>;
88 
89  using ROSMessageTypeAllocatorTraits = allocator::AllocRebind<ROSMessageType, AllocatorT>;
90  using ROSMessageTypeAllocator = typename ROSMessageTypeAllocatorTraits::allocator_type;
91  using ROSMessageTypeDeleter = allocator::Deleter<ROSMessageTypeAllocator, ROSMessageType>;
92 
93 private:
94  using SubscriptionTopicStatisticsSharedPtr =
95  std::shared_ptr<rclcpp::topic_statistics::SubscriptionTopicStatistics>;
96 
97 public:
98  RCLCPP_SMART_PTR_DEFINITIONS(Subscription)
99 
100 
118  // *INDENT-OFF*
120  rclcpp::node_interfaces::NodeBaseInterface * node_base,
121  const rosidl_message_type_support_t & type_support_handle,
122  const std::string & topic_name,
123  const rclcpp::QoS & qos,
124  AnySubscriptionCallback<MessageT, AllocatorT> callback,
125  const rclcpp::SubscriptionOptionsWithAllocator<AllocatorT> & options,
126  typename MessageMemoryStrategyT::SharedPtr message_memory_strategy,
127  SubscriptionTopicStatisticsSharedPtr subscription_topic_statistics = nullptr)
129  node_base,
130  type_support_handle,
131  topic_name,
132  options.to_rcl_subscription_options(qos),
133  // NOTE(methylDragon): Passing these args separately is necessary for event binding
134  options.event_callbacks,
135  options.use_default_callbacks,
136  callback.is_serialized_message_callback() ? DeliveredMessageKind::SERIALIZED_MESSAGE : DeliveredMessageKind::ROS_MESSAGE), // NOLINT
137  any_callback_(callback),
138  options_(options),
139  message_memory_strategy_(message_memory_strategy)
140  // *INDENT-ON*
141  {
142  // Setup intra process publishing if requested.
143  if (rclcpp::detail::resolve_use_intra_process(options_, *node_base)) {
144  using rclcpp::detail::resolve_intra_process_buffer_type;
145 
146  // Check if the QoS is compatible with intra-process.
147  auto qos_profile = get_actual_qos();
148  if (qos_profile.history() != rclcpp::HistoryPolicy::KeepLast) {
149  throw std::invalid_argument(
150  "intraprocess communication on topic '" + topic_name +
151  "' allowed only with keep last history qos policy");
152  }
153  if (qos_profile.depth() == 0) {
154  throw std::invalid_argument(
155  "intraprocess communication on topic '" + topic_name +
156  "' is not allowed with 0 depth qos policy");
157  }
158 
159  using SubscriptionIntraProcessT = rclcpp::experimental::SubscriptionIntraProcess<
160  MessageT,
161  SubscribedType,
162  SubscribedTypeAllocator,
163  SubscribedTypeDeleter,
164  ROSMessageT,
165  AllocatorT>;
166 
167  // Build a type-erased stats handler to avoid a circular include chain
168  // via publisher.hpp and callback_group.hpp
169  typename SubscriptionIntraProcessT::StatsHandlerFn stats_handler = nullptr;
170  if (subscription_topic_statistics) {
171  stats_handler =
172  [subscription_topic_statistics](
173  const rmw_message_info_t & info, const rclcpp::Time & time)
174  {
175  subscription_topic_statistics->handle_message(info, time);
176  };
177  }
178 
179  // First create a SubscriptionIntraProcess which will be given to the intra-process manager.
180  auto context = node_base->get_context();
181  subscription_intra_process_ = std::make_shared<SubscriptionIntraProcessT>(
182  callback,
183  options_.get_allocator(),
184  context,
185  this->get_topic_name(), // important to get like this, as it has the fully-qualified name
186  qos_profile,
187  resolve_intra_process_buffer_type(options_.intra_process_buffer_type, callback),
188  std::move(stats_handler));
189  TRACETOOLS_TRACEPOINT(
190  rclcpp_subscription_init,
191  static_cast<const void *>(get_subscription_handle().get()),
192  static_cast<const void *>(subscription_intra_process_.get()));
193 
194  // Add it to the intra process manager.
196  auto ipm = context->get_sub_context<IntraProcessManager>();
197  uint64_t intra_process_subscription_id = ipm->template add_subscription<
198  ROSMessageType, ROSMessageTypeAllocator>(subscription_intra_process_);
199  this->setup_intra_process(intra_process_subscription_id, ipm);
200  }
201 
202  if (subscription_topic_statistics != nullptr) {
203  this->subscription_topic_statistics_ = std::move(subscription_topic_statistics);
204  }
205 
206  TRACETOOLS_TRACEPOINT(
207  rclcpp_subscription_init,
208  static_cast<const void *>(get_subscription_handle().get()),
209  static_cast<const void *>(this));
210  TRACETOOLS_TRACEPOINT(
211  rclcpp_subscription_callback_added,
212  static_cast<const void *>(this),
213  static_cast<const void *>(&any_callback_));
214  // The callback object gets copied, so if registration is done too early/before this point
215  // (e.g. in `AnySubscriptionCallback::set()`), its address won't match any address used later
216  // in subsequent tracepoints.
217 #ifndef TRACETOOLS_DISABLED
218  any_callback_.register_callback_for_tracing();
219 #endif
220  }
221 
223  void
225  [[maybe_unused]] rclcpp::node_interfaces::NodeBaseInterface * node_base,
226  [[maybe_unused]] const rclcpp::QoS & qos,
227  [[maybe_unused]] const rclcpp::SubscriptionOptionsWithAllocator<AllocatorT> & options)
228  {
229  // This function is intentionally left empty.
230  }
231 
233 
250  bool
251  take(ROSMessageType & message_out, rclcpp::MessageInfo & message_info_out)
252  {
253  return this->take_type_erased(static_cast<void *>(&message_out), message_info_out);
254  }
255 
257 
263  template<typename TakeT>
264  std::enable_if_t<
265  !rosidl_generator_traits::is_message<TakeT>::value &&
266  std::is_same_v<TakeT, SubscribedType>,
267  bool
268  >
269  take(TakeT & message_out, rclcpp::MessageInfo & message_info_out)
270  {
271  ROSMessageType local_message;
272  bool taken = this->take_type_erased(static_cast<void *>(&local_message), message_info_out);
273  if (taken) {
274  rclcpp::TypeAdapter<MessageT>::convert_to_custom(local_message, message_out);
275  }
276  return taken;
277  }
278 
279  std::shared_ptr<void>
280  create_message() override
281  {
282  /* The default message memory strategy provides a dynamically allocated message on each call to
283  * create_message, though alternative memory strategies that re-use a preallocated message may be
284  * used (see rclcpp/strategies/message_pool_memory_strategy.hpp).
285  */
286  return message_memory_strategy_->borrow_message();
287  }
288 
289  std::shared_ptr<rclcpp::SerializedMessage>
291  {
292  return message_memory_strategy_->borrow_serialized_message();
293  }
294 
296 
304  void
305  disable_callbacks() override
306  {
308  any_callback_.disable();
309  if (subscription_intra_process_) {
310  subscription_intra_process_->disable_callbacks();
311  }
312  for (const auto & [_, event_ptr] : event_handlers_) {
313  if (event_ptr) {
314  event_ptr->disable();
315  }
316  }
317  }
318 
320 
324  void
325  enable_callbacks() override
326  {
328  any_callback_.enable();
329  if (subscription_intra_process_) {
330  subscription_intra_process_->enable_callbacks();
331  }
332  for (const auto & [_, event_ptr] : event_handlers_) {
333  if (event_ptr) {
334  event_ptr->enable();
335  }
336  }
337  }
338 
339  void
341  std::shared_ptr<void> & message,
342  const rclcpp::MessageInfo & message_info) override
343  {
344  if (matches_any_intra_process_publishers(&message_info.get_rmw_message_info().publisher_gid)) {
345  // In this case, the message will be delivered via intra process and
346  // we should ignore this copy of the message.
347  return;
348  }
349  auto typed_message = std::static_pointer_cast<ROSMessageType>(message);
350 
351  std::chrono::time_point<std::chrono::system_clock> now;
352  if (subscription_topic_statistics_) {
353  // get current time before executing callback to
354  // exclude callback duration from topic statistics result.
355  now = std::chrono::system_clock::now();
356  }
357 
358  any_callback_.dispatch(typed_message, message_info);
359 
360  if (subscription_topic_statistics_) {
361  const auto nanos = std::chrono::time_point_cast<std::chrono::nanoseconds>(now);
362  const auto time = rclcpp::Time(nanos.time_since_epoch().count());
363  subscription_topic_statistics_->handle_message(message_info.get_rmw_message_info(), time);
364  }
365  }
366 
367  void
368  handle_serialized_message(
369  const std::shared_ptr<rclcpp::SerializedMessage> & serialized_message,
370  const rclcpp::MessageInfo & message_info) override
371  {
372  std::chrono::time_point<std::chrono::system_clock> now;
373  if (subscription_topic_statistics_) {
374  // get current time before executing callback to
375  // exclude callback duration from topic statistics result.
376  now = std::chrono::system_clock::now();
377  }
378 
379  any_callback_.dispatch(serialized_message, message_info);
380 
381  if (subscription_topic_statistics_) {
382  const auto nanos = std::chrono::time_point_cast<std::chrono::nanoseconds>(now);
383  const auto time = rclcpp::Time(nanos.time_since_epoch().count());
384  subscription_topic_statistics_->handle_message(message_info.get_rmw_message_info(), time);
385  }
386  }
387 
388  void
389  handle_loaned_message(
390  void * loaned_message,
391  const rclcpp::MessageInfo & message_info) override
392  {
393  if (matches_any_intra_process_publishers(&message_info.get_rmw_message_info().publisher_gid)) {
394  // In this case, the message will be delivered via intra process and
395  // we should ignore this copy of the message.
396  return;
397  }
398 
399  auto typed_message = static_cast<ROSMessageType *>(loaned_message);
400  // message is loaned, so we have to make sure that the deleter does not deallocate the message
401  auto sptr = std::shared_ptr<ROSMessageType>(
402  typed_message, [](ROSMessageType * msg) {(void) msg;});
403 
404  std::chrono::time_point<std::chrono::system_clock> now;
405  if (subscription_topic_statistics_) {
406  // get current time before executing callback to
407  // exclude callback duration from topic statistics result.
408  now = std::chrono::system_clock::now();
409  }
410 
411  any_callback_.dispatch(sptr, message_info);
412 
413  if (subscription_topic_statistics_) {
414  const auto nanos = std::chrono::time_point_cast<std::chrono::nanoseconds>(now);
415  const auto time = rclcpp::Time(nanos.time_since_epoch().count());
416  subscription_topic_statistics_->handle_message(message_info.get_rmw_message_info(), time);
417  }
418  }
419 
421 
424  void
425  return_message(std::shared_ptr<void> & message) override
426  {
427  auto typed_message = std::static_pointer_cast<ROSMessageType>(message);
428  message_memory_strategy_->return_message(typed_message);
429  }
430 
432 
435  void
436  return_serialized_message(std::shared_ptr<rclcpp::SerializedMessage> & message) override
437  {
438  message_memory_strategy_->return_serialized_message(message);
439  }
440 
441  bool
442  use_take_shared_method() const
443  {
444  return any_callback_.use_take_shared_method();
445  }
446 
447  // DYNAMIC TYPE ==================================================================================
448  // TODO(methylDragon): Reorder later
449  // TODO(methylDragon): Implement later...
450  rclcpp::dynamic_typesupport::DynamicMessageType::SharedPtr
451  get_shared_dynamic_message_type() override
452  {
454  "get_shared_dynamic_message_type is not implemented for Subscription");
455  }
456 
457  rclcpp::dynamic_typesupport::DynamicMessage::SharedPtr
458  get_shared_dynamic_message() override
459  {
461  "get_shared_dynamic_message is not implemented for Subscription");
462  }
463 
464  rclcpp::dynamic_typesupport::DynamicSerializationSupport::SharedPtr
465  get_shared_dynamic_serialization_support() override
466  {
468  "get_shared_dynamic_serialization_support is not implemented for Subscription");
469  }
470 
471  rclcpp::dynamic_typesupport::DynamicMessage::SharedPtr
473  {
475  "create_dynamic_message is not implemented for Subscription");
476  }
477 
478  void
479  return_dynamic_message(
480  [[maybe_unused]] rclcpp::dynamic_typesupport::DynamicMessage::SharedPtr & message) override
481  {
483  "return_dynamic_message is not implemented for Subscription");
484  }
485 
486  void
487  handle_dynamic_message(
488  [[maybe_unused]] const rclcpp::dynamic_typesupport::DynamicMessage::SharedPtr & message,
489  [[maybe_unused]] const rclcpp::MessageInfo & message_info) override
490  {
492  "handle_dynamic_message is not implemented for Subscription");
493  }
494 
495 private:
496  RCLCPP_DISABLE_COPY(Subscription)
497 
498  AnySubscriptionCallback<MessageT, AllocatorT> any_callback_;
500 
505  typename message_memory_strategy::MessageMemoryStrategy<ROSMessageType, AllocatorT>::SharedPtr
506  message_memory_strategy_;
507 
509  SubscriptionTopicStatisticsSharedPtr subscription_topic_statistics_{nullptr};
510 };
511 
512 } // namespace rclcpp
513 
514 #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:116
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.
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, SubscriptionTopicStatisticsSharedPtr subscription_topic_statistics=nullptr)
Default constructor.
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.
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.