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