25 using data_callback_t = std::function<void(
const typename MessageType::ConstSharedPtr&)>;
33 : m_node(options.
node),
35 m_sub_opts(options.subscription_options),
42 m_latest_message_time(0),
43 m_latest_message(
nullptr),
44 m_message_callback(message_callback)
50 m_timeout_mgr_callback = std::bind(options.
timeout_callback, m_topic_name, std::placeholders::_1);
53 m_timeout_mgr_callback = std::bind(&Impl::default_timeout_callback,
this, m_topic_name, std::placeholders::_1);
60 if (!m_timeout_manager)
63 std::make_shared<mrs_lib::TimeoutManager>(m_node, rclcpp::Rate(1.0 / (options.
no_message_timeout.seconds() * 0.5), m_node->get_clock()));
67 m_timeout_id = m_timeout_manager->registerNew(options.
no_message_timeout, m_timeout_mgr_callback, m_node->get_clock()->now(),
false,
false);
72 virtual ~Impl() =
default;
76 virtual typename MessageType::ConstSharedPtr
getMsg()
85 virtual typename MessageType::ConstSharedPtr
peekMsg()
const
91 return m_latest_message;
96 virtual bool hasMsg()
const
103 virtual bool newMsg()
const
117 virtual typename MessageType::ConstSharedPtr
waitForNew(
const rclcpp::Duration& timeout)
120 const std::chrono::duration<float> chrono_timeout(timeout.seconds());
122 std::unique_lock lock(m_new_data_mtx);
126 else if (m_new_data_cv.wait_for(lock, chrono_timeout) == std::cv_status::no_timeout && m_new_data)
136 return m_latest_message_time;
143 if (m_sub ==
nullptr)
148 std::string ret = m_sub->get_topic_name();
152 ret = m_node->get_node_topics_interface()->resolve_topic_name(m_topic_name);
162 return m_sub->get_publisher_count();
169 if (timeout == mrs_lib::no_timeout)
172 if (m_timeout_manager !=
nullptr && m_timeout_id.has_value())
173 m_timeout_manager->pause(m_timeout_id.value());
178 if (m_timeout_manager ==
nullptr)
179 m_timeout_manager = std::make_shared<mrs_lib::TimeoutManager>(m_node, rclcpp::Rate(1.0 / (timeout.seconds() * 0.5), m_node->get_clock()));
182 const bool running = m_sub !=
nullptr;
185 if (m_timeout_id.has_value())
186 m_timeout_manager->change(m_timeout_id.value(), timeout, m_timeout_mgr_callback, m_node->get_clock()->now(),
false, running);
190 m_timeout_id = m_timeout_manager->registerNew(timeout, m_timeout_mgr_callback, m_node->get_clock()->now(),
false, running);
214 rclcpp::Node::SharedPtr m_node;
215 rclcpp::Subscription<MessageType>::SharedPtr m_sub;
216 rclcpp::QoS m_qos = rclcpp::SystemDefaultsQoS();
217 rclcpp::SubscriptionOptions m_sub_opts;
220 std::string m_topic_name;
221 std::string m_node_name;
226 mutable std::mutex m_new_data_mtx;
227 mutable std::condition_variable m_new_data_cv;
233 std::shared_ptr<mrs_lib::TimeoutManager> m_timeout_manager;
234 std::optional<mrs_lib::TimeoutManager::timeout_id_t> m_timeout_id;
235 mrs_lib::TimeoutManager::callback_t m_timeout_mgr_callback;
238 rclcpp::Time m_latest_message_time;
239 typename MessageType::ConstSharedPtr m_latest_message;
240 message_callback_t m_message_callback;
246 const std::function<void(
const typename MessageType::SharedPtr)> cbk = std::bind(&Impl::data_callback,
this, std::placeholders::_1);
248 m_sub = m_node->create_subscription<MessageType>(m_topic_name, m_qos, cbk, m_sub_opts);
249 RCLCPP_INFO_STREAM(m_node->get_logger(),
"Subscribed to topic '" << m_topic_name <<
"' -> '" << m_sub->get_topic_name() <<
"'");
256 if (m_timeout_manager && m_timeout_id.has_value())
257 m_timeout_manager->pause(m_timeout_id.value());
262 if (m_timeout_manager && m_timeout_id.has_value())
263 m_timeout_manager->start(m_timeout_id.value(), m_node->get_clock()->now());
268 void default_timeout_callback([[maybe_unused]]
const std::string& topic_name,
const rclcpp::Time& last_msg)
274 const rclcpp::Duration since_msg = (m_node->get_clock()->now() - last_msg);
275 const auto n_pubs = m_sub->get_publisher_count();
276 const std::string txt =
"Did not receive any message from topic '" + std::string(m_sub->get_topic_name()) +
"' for " + std::to_string(since_msg.seconds())
277 +
"s (" + std::to_string(n_pubs) +
" QoS-compatible publishers on this topic)";
279 RCLCPP_WARN_STREAM(m_node->get_logger(), txt);
284 void process_new_message(
const typename MessageType::ConstSharedPtr& msg)
286 m_latest_message_time = m_node->get_clock()->now();
287 m_latest_message = msg;
290 m_new_data = !m_message_callback;
292 m_new_data_cv.notify_one();
297 virtual void data_callback(
const typename MessageType::ConstSharedPtr& msg)
299 if (m_timeout_manager && m_timeout_id.has_value())
300 m_timeout_manager->reset(m_timeout_id.value(), m_node->get_clock()->now());
303 std::lock_guard lck(m_new_data_mtx);
304 process_new_message(msg);
308 if (m_message_callback)
309 m_message_callback(msg);
323 using timeout_callback_t =
typename impl_class_t::timeout_callback_t;
324 using message_callback_t =
typename impl_class_t::message_callback_t;
330 : impl_class_t::Impl(options, message_callback)
335 virtual bool hasMsg()
const override
337 std::lock_guard lck(m_mtx);
338 return impl_class_t::hasMsg();
340 virtual bool newMsg()
const override
342 std::lock_guard lck(m_mtx);
343 return impl_class_t::newMsg();
345 virtual bool usedMsg()
const override
347 std::lock_guard lck(m_mtx);
348 return impl_class_t::usedMsg();
350 virtual typename MessageType::ConstSharedPtr
getMsg()
override
352 std::lock_guard lck(m_mtx);
353 return impl_class_t::getMsg();
355 virtual typename MessageType::ConstSharedPtr
peekMsg()
const override
357 std::lock_guard lck(m_mtx);
358 return impl_class_t::peekMsg();
362 std::lock_guard lck(m_mtx);
363 return impl_class_t::lastMsgTime();
365 virtual std::string
topicName()
const override
367 std::lock_guard lck(m_mtx);
368 return impl_class_t::topicName();
371 virtual void start()
override
373 this->pauseTimeout();
375 std::lock_guard lck(m_mtx);
376 impl_class_t::subscribe();
378 this->startTimeout();
380 virtual void stop()
override
382 this->pauseTimeout();
383 std::lock_guard lck(m_mtx);
387 virtual ~ImplThreadsafe()
override =
default;
390 virtual void data_callback(
const typename MessageType::ConstSharedPtr& msg)
override
393 if (this->m_timeout_manager && this->m_timeout_id.has_value())
394 this->m_timeout_manager->reset(this->m_timeout_id.value(), this->m_node->get_clock()->now());
397 std::scoped_lock lck(m_mtx, this->m_new_data_mtx);
398 impl_class_t::process_new_message(msg);
402 if (this->m_message_callback)
403 impl_class_t::m_message_callback(msg);
407 mutable std::recursive_mutex m_mtx;