ROS 2 rclcpp + rcl - lyrical  lyrical
ROS 2 C++ Client Library with ROS Client Library
subscription_base.hpp
1 // Copyright 2019 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_BASE_HPP_
16 #define RCLCPP__SUBSCRIPTION_BASE_HPP_
17 
18 #include <atomic>
19 #include <memory>
20 #include <mutex>
21 #include <string>
22 #include <unordered_map>
23 #include <utility>
24 #include <vector>
25 
26 #include "rcl/event_callback.h"
27 #include "rcl/subscription.h"
28 
29 #include "rmw/impl/cpp/demangle.hpp"
30 #include "rmw/rmw.h"
31 
32 #include "rclcpp/any_subscription_callback.hpp"
33 #include "rclcpp/detail/cpp_callback_trampoline.hpp"
34 #include "rclcpp/dynamic_typesupport/dynamic_message.hpp"
35 #include "rclcpp/dynamic_typesupport/dynamic_message_type.hpp"
36 #include "rclcpp/dynamic_typesupport/dynamic_serialization_support.hpp"
37 #include "rclcpp/experimental/intra_process_manager.hpp"
38 #include "rclcpp/experimental/subscription_intra_process_base.hpp"
39 #include "rclcpp/macros.hpp"
40 #include "rclcpp/message_info.hpp"
41 #include "rclcpp/network_flow_endpoint.hpp"
42 #include "rclcpp/qos.hpp"
43 #include "rclcpp/event_handler.hpp"
44 #include "rclcpp/serialized_message.hpp"
45 #include "rclcpp/subscription_content_filter_options.hpp"
46 #include "rclcpp/type_support_decl.hpp"
47 #include "rclcpp/visibility_control.hpp"
48 
49 namespace rclcpp
50 {
51 
52 namespace node_interfaces
53 {
54 class NodeBaseInterface;
55 } // namespace node_interfaces
56 
57 namespace experimental
58 {
63 class IntraProcessManager;
64 } // namespace experimental
65 
67 
79 enum class DeliveredMessageKind : uint8_t
80 {
81  INVALID = 0,
82  ROS_MESSAGE = 1, // The subscription delivers a ROS message to its callback
83  SERIALIZED_MESSAGE = 2, // The subscription delivers a serialized message to its callback
84  DYNAMIC_MESSAGE = 3, // The subscription delivers a dynamic message to its callback
85 };
86 
89 class SubscriptionBase : public std::enable_shared_from_this<SubscriptionBase>
90 {
91 public:
92  RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(SubscriptionBase)
93 
94 
106  RCLCPP_PUBLIC
109  const rosidl_message_type_support_t & type_support_handle,
110  const std::string & topic_name,
111  const rcl_subscription_options_t & subscription_options,
112  const SubscriptionEventCallbacks & event_callbacks,
113  bool use_default_callbacks,
114  DeliveredMessageKind delivered_message_kind = DeliveredMessageKind::ROS_MESSAGE);
115 
117  RCLCPP_PUBLIC
118  virtual ~SubscriptionBase();
119 
121  RCLCPP_PUBLIC
122  void
124  const SubscriptionEventCallbacks & event_callbacks, bool use_default_callbacks);
125 
127  RCLCPP_PUBLIC
128  const char *
129  get_topic_name() const;
130 
131  RCLCPP_PUBLIC
132  std::shared_ptr<rcl_subscription_t>
133  get_subscription_handle();
134 
135  RCLCPP_PUBLIC
136  std::shared_ptr<const rcl_subscription_t>
137  get_subscription_handle() const;
138 
140 
141  RCLCPP_PUBLIC
142  const
143  std::unordered_map<rcl_subscription_event_type_t, std::shared_ptr<rclcpp::EventHandlerBase>> &
144  get_event_handlers() const;
145 
147 
158  RCLCPP_PUBLIC
160  get_actual_qos() const;
161 
163 
179  RCLCPP_PUBLIC
180  bool
181  take_type_erased(void * message_out, rclcpp::MessageInfo & message_info_out);
182 
184 
197  RCLCPP_PUBLIC
198  bool
199  take_serialized(rclcpp::SerializedMessage & message_out, rclcpp::MessageInfo & message_info_out);
200 
202 
203  RCLCPP_PUBLIC
204  virtual
205  std::shared_ptr<void>
207 
209 
210  RCLCPP_PUBLIC
211  virtual
212  std::shared_ptr<rclcpp::SerializedMessage>
214 
216 
219  RCLCPP_PUBLIC
220  virtual
221  void disable_callbacks();
222 
224 
228  RCLCPP_PUBLIC
229  virtual
230  void enable_callbacks();
231 
233 
237  RCLCPP_PUBLIC
238  virtual
239  void
240  handle_message(std::shared_ptr<void> & message, const rclcpp::MessageInfo & message_info) = 0;
241 
242  RCLCPP_PUBLIC
243  virtual
244  void
245  handle_serialized_message(
246  const std::shared_ptr<rclcpp::SerializedMessage> & serialized_message,
247  const rclcpp::MessageInfo & message_info) = 0;
248 
249  RCLCPP_PUBLIC
250  virtual
251  void
252  handle_loaned_message(void * loaned_message, const rclcpp::MessageInfo & message_info) = 0;
253 
255 
256  RCLCPP_PUBLIC
257  virtual
258  void
259  return_message(std::shared_ptr<void> & message) = 0;
260 
262 
263  RCLCPP_PUBLIC
264  virtual
265  void
266  return_serialized_message(std::shared_ptr<rclcpp::SerializedMessage> & message) = 0;
267 
268  RCLCPP_PUBLIC
269  const rosidl_message_type_support_t &
270  get_message_type_support_handle() const;
271 
273 
276  RCLCPP_PUBLIC
277  bool
278  is_serialized() const;
279 
281 
284  RCLCPP_PUBLIC
287 
289 
290  RCLCPP_PUBLIC
291  size_t
292  get_publisher_count() const;
293 
295 
301  RCLCPP_PUBLIC
302  bool
303  can_loan_messages() const;
304 
305  using IntraProcessManagerWeakPtr =
306  std::weak_ptr<rclcpp::experimental::IntraProcessManager>;
307 
309  RCLCPP_PUBLIC
310  void
312  uint64_t intra_process_subscription_id,
313  IntraProcessManagerWeakPtr weak_ipm);
314 
316 
320  RCLCPP_PUBLIC
321  rclcpp::Waitable::SharedPtr
323 
325 
337  RCLCPP_PUBLIC
338  bool
339  exchange_in_use_by_wait_set_state(void * pointer_to_subscription_part, bool in_use_state);
340 
342 
346  RCLCPP_PUBLIC
347  std::vector<rclcpp::NetworkFlowEndpoint>
349 
351 
374  void
375  set_on_new_message_callback(const std::function<void(size_t)> & callback)
376  {
377  if (!callback) {
378  throw std::invalid_argument(
379  "The callback passed to set_on_new_message_callback "
380  "is not callable.");
381  }
382 
383  auto new_callback =
384  [callback, this](size_t number_of_messages) {
385  try {
386  callback(number_of_messages);
387  } catch (const std::exception & exception) {
388  RCLCPP_ERROR_STREAM(
389  node_logger_,
390  "rclcpp::SubscriptionBase@" << this <<
391  " caught " << rmw::impl::cpp::demangle(exception) <<
392  " exception in user-provided callback for the 'on new message' callback: " <<
393  exception.what());
394  } catch (...) {
395  RCLCPP_ERROR_STREAM(
396  node_logger_,
397  "rclcpp::SubscriptionBase@" << this <<
398  " caught unhandled exception in user-provided callback " <<
399  "for the 'on new message' callback");
400  }
401  };
402 
403  std::lock_guard<std::recursive_mutex> lock(on_new_message_callback_mutex_);
404 
405  // Set it temporarily to the new callback, while we replace the old one.
406  // This two-step setting, prevents a gap where the old std::function has
407  // been replaced but the middleware hasn't been told about the new one yet.
409  rclcpp::detail::cpp_callback_trampoline<decltype(new_callback), const void *, size_t>,
410  static_cast<const void *>(&new_callback));
411 
412  // Store the std::function to keep it in scope, also overwrites the existing one.
413  on_new_message_callback_ = new_callback;
414 
415  // Set it again, now using the permanent storage.
417  rclcpp::detail::cpp_callback_trampoline<
418  decltype(on_new_message_callback_), const void *, size_t>,
419  static_cast<const void *>(&on_new_message_callback_));
420  }
421 
423  void
425  {
426  std::lock_guard<std::recursive_mutex> lock(on_new_message_callback_mutex_);
427 
428  if (on_new_message_callback_) {
429  set_on_new_message_callback(nullptr, nullptr);
430  on_new_message_callback_ = nullptr;
431  }
432  }
433 
435 
452  void
453  set_on_new_intra_process_message_callback(const std::function<void(size_t)> & callback)
454  {
455  if (!use_intra_process_) {
456  RCLCPP_WARN(
457  rclcpp::get_logger("rclcpp"),
458  "Calling set_on_new_intra_process_message_callback for subscription with IPC disabled");
459  return;
460  }
461 
462  if (!callback) {
463  throw std::invalid_argument(
464  "The callback passed to set_on_new_intra_process_message_callback "
465  "is not callable.");
466  }
467 
468  // The on_ready_callback signature has an extra `int` argument used to disambiguate between
469  // possible different entities within a generic waitable.
470  // We hide that detail to users of this method.
471  std::function<void(size_t, int)> new_callback = [callback] (size_t nr, int) {callback(nr);};
472  subscription_intra_process_->set_on_ready_callback(new_callback);
473  }
474 
476  void
478  {
479  if (!use_intra_process_) {
480  RCLCPP_WARN(
481  rclcpp::get_logger("rclcpp"),
482  "Calling clear_on_new_intra_process_message_callback for subscription with IPC disabled");
483  return;
484  }
485 
486  subscription_intra_process_->clear_on_ready_callback();
487  }
488 
490 
515  void
517  const std::function<void(size_t)> & callback,
519  {
520  if (event_handlers_.count(event_type) == 0) {
521  RCLCPP_WARN(
522  rclcpp::get_logger("rclcpp"),
523  "Calling set_on_new_qos_event_callback for non registered subscription event_type");
524  return;
525  }
526 
527  if (!callback) {
528  throw std::invalid_argument(
529  "The callback passed to set_on_new_qos_event_callback "
530  "is not callable.");
531  }
532 
533  // The on_ready_callback signature has an extra `int` argument used to disambiguate between
534  // possible different entities within a generic waitable.
535  // We hide that detail to users of this method.
536  std::function<void(size_t, int)> new_callback = [callback] (size_t nr, int) {callback(nr);};
537  event_handlers_[event_type]->set_on_ready_callback(new_callback);
538  }
539 
541  void
543  {
544  if (event_handlers_.count(event_type) == 0) {
545  RCLCPP_WARN(
546  rclcpp::get_logger("rclcpp"),
547  "Calling clear_on_new_qos_event_callback for non registered event_type");
548  return;
549  }
550 
551  event_handlers_[event_type]->clear_on_ready_callback();
552  }
553 
555 
559  RCLCPP_PUBLIC
560  bool
561  is_cft_supported() const;
562 
564 
567  RCLCPP_PUBLIC
568  bool
569  is_cft_enabled() const;
570 
572 
581  RCLCPP_PUBLIC
582  void
584  const std::string & filter_expression,
585  const std::vector<std::string> & expression_parameters = {});
586 
588 
593  RCLCPP_PUBLIC
595  get_content_filter() const;
596 
597  // DYNAMIC TYPE ==================================================================================
598  // TODO(methylDragon): Reorder later
599  RCLCPP_PUBLIC
600  virtual
601  rclcpp::dynamic_typesupport::DynamicMessageType::SharedPtr
602  get_shared_dynamic_message_type() = 0;
603 
604  RCLCPP_PUBLIC
605  virtual
606  rclcpp::dynamic_typesupport::DynamicMessage::SharedPtr
607  get_shared_dynamic_message() = 0;
608 
609  RCLCPP_PUBLIC
610  virtual
611  rclcpp::dynamic_typesupport::DynamicSerializationSupport::SharedPtr
612  get_shared_dynamic_serialization_support() = 0;
613 
615 
616  RCLCPP_PUBLIC
617  virtual
618  rclcpp::dynamic_typesupport::DynamicMessage::SharedPtr
620 
621  RCLCPP_PUBLIC
622  virtual
623  void
624  return_dynamic_message(rclcpp::dynamic_typesupport::DynamicMessage::SharedPtr & message) = 0;
625 
626  RCLCPP_PUBLIC
627  virtual
628  void
629  handle_dynamic_message(
630  const rclcpp::dynamic_typesupport::DynamicMessage::SharedPtr & message,
631  const rclcpp::MessageInfo & message_info) = 0;
632 
633  RCLCPP_PUBLIC
634  bool
635  take_dynamic_message(
637  rclcpp::MessageInfo & message_info_out);
638  // ===============================================================================================
639 
640 protected:
641  template<typename EventCallbackT>
642  void
643  add_event_handler(
644  const EventCallbackT & callback,
645  const rcl_subscription_event_type_t event_type)
646  {
647  auto handler = std::make_shared<EventHandler<EventCallbackT,
648  std::shared_ptr<rcl_subscription_t>>>(
649  callback,
651  get_subscription_handle(),
652  event_type);
653  qos_events_in_use_by_wait_set_.insert(std::make_pair(handler.get(), false));
654  event_handlers_.insert(std::make_pair(event_type, handler));
655  }
656 
657  RCLCPP_PUBLIC
658  void default_incompatible_qos_callback(QOSRequestedIncompatibleQoSInfo & info) const;
659 
660  RCLCPP_PUBLIC
661  void default_incompatible_type_callback(IncompatibleTypeInfo & info) const;
662 
663  RCLCPP_PUBLIC
664  bool
665  matches_any_intra_process_publishers(const rmw_gid_t * sender_gid) const;
666 
667  RCLCPP_PUBLIC
668  void
669  set_on_new_message_callback(rcl_event_callback_t callback, const void * user_data);
670 
672 
673  std::shared_ptr<rcl_node_t> node_handle_;
674 
675  std::recursive_mutex on_new_message_callback_mutex_;
676  // It is important to declare on_new_message_callback_ before
677  // subscription_handle_, so on destruction the subscription is
678  // destroyed first. Otherwise, the rmw subscription callback
679  // would point briefly to a destroyed function.
680  std::function<void(size_t)> on_new_message_callback_{nullptr};
681  // Declare subscription_handle_ after callback
682  std::shared_ptr<rcl_subscription_t> subscription_handle_;
683  std::shared_ptr<rcl_subscription_t> intra_process_subscription_handle_;
684  rclcpp::Logger node_logger_;
685 
686  std::unordered_map<rcl_subscription_event_type_t,
687  std::shared_ptr<rclcpp::EventHandlerBase>> event_handlers_;
688 
689  bool use_intra_process_;
690  IntraProcessManagerWeakPtr weak_ipm_;
691  uint64_t intra_process_subscription_id_;
692  std::shared_ptr<rclcpp::experimental::SubscriptionIntraProcessBase> subscription_intra_process_;
693 
694  const SubscriptionEventCallbacks event_callbacks_;
695 
696 private:
697  RCLCPP_DISABLE_COPY(SubscriptionBase)
698 
699  rosidl_message_type_support_t type_support_;
700  DeliveredMessageKind delivered_message_kind_;
701 
702  std::atomic<bool> subscription_in_use_by_wait_set_{false};
703  std::atomic<bool> intra_process_subscription_waitable_in_use_by_wait_set_{false};
704  std::unordered_map<rclcpp::EventHandlerBase *,
705  std::atomic<bool>> qos_events_in_use_by_wait_set_;
706 };
707 
708 } // namespace rclcpp
709 
710 #endif // RCLCPP__SUBSCRIPTION_BASE_HPP_
Additional meta data about messages taken from subscriptions.
Encapsulation of Quality of Service settings.
Definition: qos.hpp:116
Object oriented version of rcl_serialized_message_t with destructor to avoid memory leaks.
virtual RCLCPP_PUBLIC std::shared_ptr< void > create_message()=0
Borrow a new message.
RCLCPP_PUBLIC size_t get_publisher_count() const
Get matching publisher count.
virtual RCLCPP_PUBLIC rclcpp::dynamic_typesupport::DynamicMessage::SharedPtr create_dynamic_message()=0
Borrow a new serialized message (this clones!)
RCLCPP_PUBLIC rclcpp::QoS get_actual_qos() const
Get the actual QoS settings, after the defaults have been determined.
virtual RCLCPP_PUBLIC void handle_message(std::shared_ptr< void > &message, const rclcpp::MessageInfo &message_info)=0
Check if we need to handle the message, and execute the callback if we do.
virtual RCLCPP_PUBLIC void enable_callbacks()
Enable the callbacks to be called.
RCLCPP_PUBLIC bool can_loan_messages() const
Check if subscription instance can loan messages.
RCLCPP_PUBLIC bool is_cft_supported() const
Check if content filtered topic feature of the subscription instance is supported.
RCLCPP_PUBLIC rclcpp::Waitable::SharedPtr get_intra_process_waitable() const
Return the waitable for intra-process.
void set_on_new_message_callback(const std::function< void(size_t)> &callback)
Set a callback to be called when each new message is received.
RCLCPP_PUBLIC std::vector< rclcpp::NetworkFlowEndpoint > get_network_flow_endpoints() const
Get network flow endpoints.
RCLCPP_PUBLIC void setup_intra_process(uint64_t intra_process_subscription_id, IntraProcessManagerWeakPtr weak_ipm)
Implemenation detail.
RCLCPP_PUBLIC DeliveredMessageKind get_delivered_message_kind() const
Return the delivered message kind.
virtual RCLCPP_PUBLIC void return_message(std::shared_ptr< void > &message)=0
Return the message borrowed in create_message.
void clear_on_new_message_callback()
Unset the callback registered for new messages, if any.
void set_on_new_qos_event_callback(const std::function< void(size_t)> &callback, rcl_subscription_event_type_t event_type)
Set a callback to be called when each new qos event instance occurs.
virtual RCLCPP_PUBLIC void disable_callbacks()
Disable callbacks from being called.
virtual RCLCPP_PUBLIC std::shared_ptr< rclcpp::SerializedMessage > create_serialized_message()=0
Borrow a new serialized message.
void set_on_new_intra_process_message_callback(const std::function< void(size_t)> &callback)
Set a callback to be called when each new intra-process message is received.
RCLCPP_PUBLIC bool take_serialized(rclcpp::SerializedMessage &message_out, rclcpp::MessageInfo &message_info_out)
Take the next inter-process message, in its serialized form, from the subscription.
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.
RCLCPP_PUBLIC bool exchange_in_use_by_wait_set_state(void *pointer_to_subscription_part, bool in_use_state)
Exchange state of whether or not a part of the subscription is used by a wait set.
void clear_on_new_qos_event_callback(rcl_subscription_event_type_t event_type)
Unset the callback registered for new qos events, if any.
RCLCPP_PUBLIC void set_content_filter(const std::string &filter_expression, const std::vector< std::string > &expression_parameters={})
Set the filter expression and expression parameters for the subscription.
virtual RCLCPP_PUBLIC ~SubscriptionBase()
Destructor.
RCLCPP_PUBLIC SubscriptionBase(rclcpp::node_interfaces::NodeBaseInterface *node_base, const rosidl_message_type_support_t &type_support_handle, const std::string &topic_name, const rcl_subscription_options_t &subscription_options, const SubscriptionEventCallbacks &event_callbacks, bool use_default_callbacks, DeliveredMessageKind delivered_message_kind=DeliveredMessageKind::ROS_MESSAGE)
Constructor.
virtual RCLCPP_PUBLIC void return_serialized_message(std::shared_ptr< rclcpp::SerializedMessage > &message)=0
Return the message borrowed in create_serialized_message.
RCLCPP_PUBLIC bool is_cft_enabled() const
Check if content filtered topic feature of the subscription instance is enabled.
void clear_on_new_intra_process_message_callback()
Unset the callback registered for new intra-process messages, if any.
RCLCPP_PUBLIC void bind_event_callbacks(const SubscriptionEventCallbacks &event_callbacks, bool use_default_callbacks)
Add event handlers for passed in event_callbacks.
RCLCPP_PUBLIC const std::unordered_map< rcl_subscription_event_type_t, std::shared_ptr< rclcpp::EventHandlerBase > > & get_event_handlers() const
Get all the QoS event handlers associated with this subscription.
RCLCPP_PUBLIC rclcpp::ContentFilterOptions get_content_filter() const
Get the filter expression and expression parameters for the subscription.
RCLCPP_PUBLIC bool is_serialized() const
Return if the subscription is serialized.
RCLCPP_PUBLIC const char * get_topic_name() const
Get the topic that this subscription is subscribed on.
Pure virtual interface class for the NodeBase part of the Node API.
enum rcl_subscription_event_type_e rcl_subscription_event_type_t
Enumeration of all of the subscription events that may fire.
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_subscription_event_init(rcl_event_t *event, const rcl_subscription_t *subscription, const rcl_subscription_event_type_t event_type)
Initialize an rcl_event_t with a subscription.
Definition: event.c:105
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.
RCLCPP_PUBLIC Logger get_logger(const std::string &name)
Return a named logger.
Definition: logger.cpp:34
Options available for a rcl subscription.
Definition: subscription.h:47
Options to configure content filtered topic in the subscription.
Contains callbacks for non-message events that a Subscription can receive from the middleware.