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