1#ifndef MRS_LIB_SUBSCRIBER_IMPL_HPP_
2#define MRS_LIB_SUBSCRIBER_IMPL_HPP_
4#include "mrs_lib/subscriber.hpp"
15#include <rclcpp/create_subscription.hpp>
16#include <rclcpp/duration.hpp>
17#include <rclcpp/subscription_options.hpp>
18#include <rclcpp/time.hpp>
20#include "mrs_lib/internal/coroutine_callback_helpers.hpp"
21#include "mrs_lib/logger.hpp"
22#include "mrs_lib/timeout_manager.hpp"
29 template <
typename MessageType>
34 std::shared_ptr<TimeoutManager2> timeout_manager{};
35 std::shared_ptr<TimeoutManagerHandle> timeout_handle{};
38 struct LatestMessageData
41 std::shared_ptr<const MessageType> message{};
46 explicit Impl(
const SubscriberOptions& options, std::string_view topic_name, std::optional<MessageCallback> callback)
47 : node_interfaces_(options.node_interfaces),
48 logger_(node_interfaces_),
49 unresolved_name_(topic_name),
50 timeout_data_(init_timeout_data(options, topic_name)),
51 user_callback_(check_nonempty_callback_function(std::move(callback))),
52 subscription_(rclcpp::create_subscription<MessageType>(
53 node_interfaces_, std::string(topic_name), options.qos,
54 std::function<void(std::shared_ptr<const MessageType>)>(std::bind_front(&Impl::message_callback, this)), std::invoke([&]() {
55 rclcpp::SubscriptionOptions opts{};
56 opts.callback_group = options.callback_group;
63 explicit Impl(
const SubscriberOptions& options, std::string_view topic_name,
MessageCoroCallback callback)
64 : Impl(options, topic_name, internal::get_detached_coro_callback_launcher(callback, options.callback_group))
69 Impl(
const Impl&) =
delete;
70 Impl& operator=(
const Impl&) =
delete;
71 Impl(Impl&&) =
delete;
72 Impl& operator=(Impl&&) =
delete;
74 [[nodiscard]] std::optional<std::shared_ptr<const MessageType>>
get_message()
76 auto guard = latest_message_data_.acquire();
77 guard->is_new =
false;
79 if (guard->message !=
nullptr)
81 return guard->message;
88 [[nodiscard]] std::optional<std::shared_ptr<const MessageType>>
get_new_message()
90 auto guard = latest_message_data_.acquire();
92 if (guard->is_new && guard->message !=
nullptr)
94 guard->is_new =
false;
95 return guard->message;
102 [[nodiscard]] std::optional<std::shared_ptr<const MessageType>>
peek_message()
const
104 auto guard = latest_message_data_.acquire();
105 if (guard->message !=
nullptr)
107 return guard->message;
114 [[nodiscard]] std::optional<std::shared_ptr<const MessageType>>
peek_new_message()
const
116 auto guard = latest_message_data_.acquire();
118 if (guard->is_new && guard->message !=
nullptr)
120 return guard->message;
129 auto guard = latest_message_data_.acquire();
135 return subscription_->get_topic_name();
140 return unresolved_name_;
145 return subscription_->get_publisher_count();
149 static std::optional<MessageCallback> check_nonempty_callback_function(std::optional<MessageCallback> callback)
151 if (callback.has_value() && !callback.value())
153 throw std::logic_error(
"Subscriber callback must not be empty.");
156 return std::move(callback);
160 TimeoutData init_timeout_data(
const SubscriberOptions& options, std::string_view topic_name)
162 if (!options.no_message_timeout.has_value())
167 auto no_message_timeout = options.no_message_timeout.value();
169 auto timeout_callback = std::invoke([&]() -> TimeoutManager2::Callback {
170 if (options.timeout_callback)
172 return [callback = options.timeout_callback, name = std::string(topic_name)](rclcpp::Time time) { callback(name, time); };
175 return std::bind_front(&Impl::default_timeout_callback,
this, std::string(topic_name));
180 std::shared_ptr<mrs_lib::TimeoutManager2> timeout_manager = options.timeout_manager;
182 if (timeout_manager ==
nullptr)
184 timeout_manager = std::make_shared<mrs_lib::TimeoutManager2>(node_interfaces_, no_message_timeout / 2);
188 auto timeout_handle = timeout_manager->register_new({
189 .timeout = no_message_timeout,
190 .callback = timeout_callback,
194 .timeout_manager = timeout_manager,
195 .timeout_handle = timeout_handle,
199 void default_timeout_callback(std::string_view, rclcpp::Time last_msg)
201 const rclcpp::Duration since_msg = (node_interfaces_.get_node_clock_interface()->get_clock()->now() - last_msg);
202 const auto n_pubs = subscription_->get_publisher_count();
203 logger_.warning(
"Did not receive any message from topic '{}' for {}s ({} publishers on this topic)",
get_topic_name(), since_msg.seconds(),
204 std::to_string(n_pubs));
207 void process_new_message(
const std::shared_ptr<const MessageType>& msg)
209 auto latest_message_data_guard = latest_message_data_.acquire();
210 latest_message_data_guard->time = node_interfaces_.get_node_clock_interface()->get_clock()->now();
211 latest_message_data_guard->message = msg;
214 latest_message_data_guard->is_new = !user_callback_.has_value();
217 void message_callback(
const std::shared_ptr<const MessageType>& msg)
220 auto timeout_data_guard = timeout_data_.acquire();
222 if (timeout_data_guard->timeout_manager && timeout_data_guard->timeout_handle)
224 timeout_data_guard->timeout_handle->reset_timeout();
228 process_new_message(msg);
231 if (user_callback_.has_value())
233 (*user_callback_)(msg);
241 std::string unresolved_name_;
243 OwningMutex<TimeoutData> timeout_data_;
244 OwningMutex<LatestMessageData> latest_message_data_;
246 std::optional<MessageCallback> user_callback_;
248 Pimpl<rclcpp::Subscription<MessageType>, std::shared_ptr<rclcpp::Subscription<MessageType>>> subscription_;
251 template <
typename MessageType>
253 : impl_(std::in_place_type_t<Impl>{}, options, topic_name, std::move(callback))
257 template <
typename MessageType>
259 : impl_(std::in_place_type_t<Impl>{}, options, topic_name, std::move(callback))
263 template <
typename MessageType>
265 : impl_(std::in_place_type_t<Impl>{}, options, topic_name, std::nullopt)
269 template <
typename MessageType>
272 return impl_->get_message();
275 template <
typename MessageType>
278 return impl_->get_new_message();
281 template <
typename MessageType>
284 return impl_->peek_message();
287 template <
typename MessageType>
290 return impl_->peek_message();
293 template <
typename MessageType>
296 return impl_->get_last_message_time();
299 template <
typename MessageType>
302 return impl_->get_topic_name();
305 template <
typename MessageType>
308 return impl_->get_unresolved_topic_name();
311 template <
typename MessageType>
314 return impl_->get_publisher_count();
Wrapper for rclcpp::Subscription with callback and polling interface.
Definition subscriber.hpp:78
Subscriber(const SubscriberOptions &options, std::string_view topic_name, MessageCallback callback)
Construct a Subscriber with the specified callback.
Definition subscriber.impl.hpp:252
uint32_t get_publisher_count() const
Returns number of publishers registered at the topic.
Definition subscriber.impl.hpp:312
std::optional< std::shared_ptr< const MessageType > > get_new_message()
Get the latest new message, if there is one.
Definition subscriber.impl.hpp:276
std::string get_unresolved_topic_name() const
Returns the unresolved name of the topic handled by this Subscriber.
Definition subscriber.impl.hpp:306
std::optional< std::shared_ptr< const MessageType > > peek_new_message() const
Get the latest new message, if there is one.
Definition subscriber.impl.hpp:288
std::optional< std::shared_ptr< const MessageType > > peek_message() const
Get the latest received message, if there is one.
Definition subscriber.impl.hpp:282
std::function< void(std::shared_ptr< const MessageType >)> MessageCallback
Type of the accepted callback.
Definition subscriber.hpp:84
CoroCallback< void(std::shared_ptr< const MessageType >)> MessageCoroCallback
Type of the accepted coroutine callback.
Definition subscriber.hpp:87
std::optional< std::shared_ptr< const MessageType > > get_message()
Get the latest received message, if there is one.
Definition subscriber.impl.hpp:270
rclcpp::Time get_last_message_time() const
Returns time of the last received message on the topic, handled by this Subscriber.
Definition subscriber.impl.hpp:294
std::string get_topic_name() const
Returns the resolved (full) name of the topic handled by this Subscriber.
Definition subscriber.impl.hpp:300
All mrs_lib functions, classes, variables and definitions are contained in this namespace.
Definition attitude_converter.h:24
rclcpp::node_interfaces::NodeInterfaces< rclcpp::node_interfaces::NodeBaseInterface, rclcpp::node_interfaces::NodeTimersInterface, rclcpp::node_interfaces::NodeClockInterface, rclcpp::node_interfaces::NodeLoggingInterface, rclcpp::node_interfaces::NodeParametersInterface, rclcpp::node_interfaces::NodeTopicsInterface > SubscriberNodeInterfaces
ROS node interfaces required by the Subscriber.
Definition subscriber.hpp:30
Tag for explicitly marking Subscriber that has no callback.
Definition subscriber.hpp:68
Options for configuring the Subscriber.
Definition subscriber.hpp:39