Nav2 Navigation Stack - lyrical  lyrical
ROS 2 Navigation Stack
interface_factories.hpp
1 // Copyright (c) 2025 Open Navigation LLC
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 NAV2_ROS_COMMON__INTERFACE_FACTORIES_HPP_
16 #define NAV2_ROS_COMMON__INTERFACE_FACTORIES_HPP_
17 
18 #include <utility>
19 #include <string>
20 #include <memory>
21 #include "nav2_ros_common/qos_profiles.hpp"
22 #include "nav2_ros_common/service_client.hpp"
23 #include "nav2_ros_common/service_server.hpp"
24 #include "nav2_ros_common/simple_action_server.hpp"
25 #include "nav2_ros_common/node_utils.hpp"
26 #include "nav2_ros_common/publisher.hpp"
27 #include "nav2_ros_common/subscription.hpp"
28 #include "nav2_ros_common/action_client.hpp"
29 #include "rclcpp_action/client.hpp"
30 #include "nav2_ros_common/rate.hpp"
31 #include "nav2_ros_common/tf2_factories.hpp"
32 
33 namespace nav2
34 {
35 
36 namespace interfaces
37 {
38 
52 inline rclcpp::SubscriptionOptions createSubscriptionOptions(
53  const std::string & topic_name,
54  const bool allow_parameter_qos_overrides = true,
55  const rclcpp::CallbackGroup::SharedPtr callback_group_ptr = nullptr,
56  rclcpp::QOSMessageLostCallbackType qos_message_lost_callback = nullptr,
57  rclcpp::SubscriptionMatchedCallbackType subscription_matched_callback = nullptr,
58  rclcpp::IncompatibleTypeCallbackType incompatible_qos_type_callback = nullptr,
59  rclcpp::QOSRequestedIncompatibleQoSCallbackType requested_incompatible_qos_callback = nullptr,
60  rclcpp::QOSDeadlineRequestedCallbackType qos_deadline_requested_callback = nullptr,
61  rclcpp::QOSLivelinessChangedCallbackType qos_liveliness_changed_callback = nullptr)
62 {
63  rclcpp::SubscriptionOptions options;
64  // Allow for all topics to have QoS overrides
65  if (allow_parameter_qos_overrides) {
66  options.qos_overriding_options = rclcpp::QosOverridingOptions(
67  {rclcpp::QosPolicyKind::Depth, rclcpp::QosPolicyKind::Durability,
68  rclcpp::QosPolicyKind::Reliability, rclcpp::QosPolicyKind::History});
69  }
70 
71  // Set the callback group to use for this subscription, if given
72  options.callback_group = callback_group_ptr;
73 
74  // ROS 2 default logs this already
75  options.event_callbacks.incompatible_qos_callback = requested_incompatible_qos_callback;
76  options.event_callbacks.incompatible_type_callback = incompatible_qos_type_callback;
77 
78  // Set the event callbacks if given, else log
79  if (qos_message_lost_callback) {
80  options.event_callbacks.message_lost_callback =
81  qos_message_lost_callback;
82  } else {
83  options.event_callbacks.message_lost_callback =
84  [topic_name](rclcpp::QOSMessageLostInfo & info) {
85  RCLCPP_WARN(
86  rclcpp::get_logger("nav2::interfaces"),
87  "[%lu] Message was dropped on topic [%s] due to queue size or network constraints.",
88  info.total_count_change,
89  topic_name.c_str());
90  };
91  }
92 
93  if (subscription_matched_callback) {
94  options.event_callbacks.matched_callback = subscription_matched_callback;
95  } else {
96  options.event_callbacks.matched_callback =
97  [topic_name](rclcpp::MatchedInfo & status) {
98  if (status.current_count_change > 0) {
99  RCLCPP_DEBUG(
100  rclcpp::get_logger("nav2::interfaces"),
101  "Connected: %d new publisher(s) to [%s]. Total active: %zu.",
102  status.current_count_change,
103  topic_name.c_str(),
104  status.current_count);
105  } else if (status.current_count_change < 0) {
106  RCLCPP_DEBUG(
107  rclcpp::get_logger("nav2::interfaces"),
108  "Disconnected: %d publisher(s) from [%s]. Total active: %zu.",
109  -status.current_count_change,
110  topic_name.c_str(),
111  status.current_count);
112  }
113  };
114  }
115 
116  options.event_callbacks.deadline_callback = qos_deadline_requested_callback;
117  options.event_callbacks.liveliness_callback = qos_liveliness_changed_callback;
118  return options;
119 }
120 
133 inline rclcpp::PublisherOptions createPublisherOptions(
134  const std::string & topic_name,
135  const bool allow_parameter_qos_overrides = true,
136  const rclcpp::CallbackGroup::SharedPtr callback_group_ptr = nullptr,
137  rclcpp::PublisherMatchedCallbackType publisher_matched_callback = nullptr,
138  rclcpp::IncompatibleTypeCallbackType incompatible_qos_type_callback = nullptr,
139  rclcpp::QOSOfferedIncompatibleQoSCallbackType offered_incompatible_qos_cb = nullptr,
140  rclcpp::QOSDeadlineOfferedCallbackType qos_deadline_offered_callback = nullptr,
141  rclcpp::QOSLivelinessLostCallbackType qos_liveliness_lost_callback = nullptr)
142 {
143  rclcpp::PublisherOptions options;
144  // Allow for all topics to have QoS overrides
145  if (allow_parameter_qos_overrides) {
146  options.qos_overriding_options = rclcpp::QosOverridingOptions(
147  {rclcpp::QosPolicyKind::Depth, rclcpp::QosPolicyKind::Durability,
148  rclcpp::QosPolicyKind::Reliability, rclcpp::QosPolicyKind::History});
149  }
150 
151  // Set the callback group to use for this publisher, if given
152  options.callback_group = callback_group_ptr;
153 
154  // ROS 2 default logs this already
155  options.event_callbacks.incompatible_qos_callback = offered_incompatible_qos_cb;
156  options.event_callbacks.incompatible_type_callback = incompatible_qos_type_callback;
157 
158  // Set the event callbacks, else log
159  if (publisher_matched_callback) {
160  options.event_callbacks.matched_callback = publisher_matched_callback;
161  } else {
162  options.event_callbacks.matched_callback =
163  [topic_name](rclcpp::MatchedInfo & status) {
164  if (status.current_count_change > 0) {
165  RCLCPP_DEBUG(
166  rclcpp::get_logger("nav2::interfaces"),
167  "Connected: %d new subscriber(s) to [%s]. Total active: %zu.",
168  status.current_count_change,
169  topic_name.c_str(),
170  status.current_count);
171  } else if (status.current_count_change < 0) {
172  RCLCPP_DEBUG(
173  rclcpp::get_logger("nav2::interfaces"),
174  "Disconnected: %d subscriber(s) from [%s]. Total active: %zu.",
175  -status.current_count_change,
176  topic_name.c_str(),
177  status.current_count);
178  }
179  };
180  }
181 
182  options.event_callbacks.deadline_callback = qos_deadline_offered_callback;
183  options.event_callbacks.liveliness_callback = qos_liveliness_lost_callback;
184  return options;
185 }
186 
196 template<typename MessageT, typename NodeT, typename CallbackT>
197 typename nav2::Subscription<MessageT>::SharedPtr create_subscription(
198  const NodeT & node,
199  const std::string & topic_name,
200  CallbackT && callback,
201  const rclcpp::QoS & qos = nav2::qos::StandardTopicQoS(),
202  const rclcpp::CallbackGroup::SharedPtr & callback_group = nullptr)
203 {
204  bool allow_parameter_qos_overrides = nav2::declare_or_get_parameter(
205  node, "allow_parameter_qos_overrides", true);
206 
207  auto params_interface = node->get_node_parameters_interface();
208  auto topics_interface = node->get_node_topics_interface();
209  return rclcpp::create_subscription<MessageT, CallbackT>(
210  params_interface,
211  topics_interface,
212  topic_name,
213  qos,
214  std::forward<CallbackT>(callback),
215  createSubscriptionOptions(topic_name, allow_parameter_qos_overrides, callback_group));
216 }
217 
226 template<typename MessageT, typename NodeT>
227 typename nav2::Publisher<MessageT>::SharedPtr create_publisher(
228  const NodeT & node,
229  const std::string & topic_name,
230  const rclcpp::QoS & qos = nav2::qos::StandardTopicQoS(),
231  const rclcpp::CallbackGroup::SharedPtr & callback_group = nullptr)
232 {
233  bool allow_parameter_qos_overrides = nav2::declare_or_get_parameter(
234  node, "allow_parameter_qos_overrides", true);
235  using PublisherT = nav2::Publisher<MessageT>;
236  auto pub = rclcpp::create_publisher<MessageT, std::allocator<void>, PublisherT>(
237  *node,
238  topic_name,
239  qos,
240  createPublisherOptions(topic_name, allow_parameter_qos_overrides, callback_group));
241  return pub;
242 }
243 
251 template<typename SrvT, typename NodeT>
252 typename nav2::ServiceClient<SrvT>::SharedPtr create_client(
253  const NodeT & node,
254  const std::string & service_name,
255  bool use_internal_executor = false)
256 {
257  return std::make_shared<nav2::ServiceClient<SrvT>>(
258  service_name, node, use_internal_executor);
259 }
260 
269 template<typename SrvT, typename NodeT, typename CallbackT>
270 typename nav2::ServiceServer<SrvT>::SharedPtr create_service(
271  const NodeT & node,
272  const std::string & service_name,
273  CallbackT && callback,
274  rclcpp::CallbackGroup::SharedPtr callback_group = nullptr)
275 {
276  using Request = typename SrvT::Request;
277  using Response = typename SrvT::Response;
278  using CallbackFn = std::function<void (
279  const std::shared_ptr<rmw_request_id_t>,
280  const std::shared_ptr<Request>,
281  std::shared_ptr<Response>)>;
282  CallbackFn cb = std::forward<CallbackT>(callback);
283 
284  return std::make_shared<nav2::ServiceServer<SrvT>>(
285  service_name, node, cb, callback_group);
286 }
287 
300 template<typename ActionT, typename NodeT>
301 typename nav2::SimpleActionServer<ActionT>::SharedPtr create_action_server(
302  const NodeT & node,
303  const std::string & action_name,
304  typename nav2::SimpleActionServer<ActionT>::ExecuteCallback execute_callback,
305  typename nav2::SimpleActionServer<ActionT>::GoalReceivedCallback goal_received_callback = nullptr,
306  typename nav2::SimpleActionServer<ActionT>::CompletionCallback complete_cb = nullptr,
307  std::chrono::milliseconds server_timeout = std::chrono::milliseconds(500),
308  bool spin_thread = false,
309  const bool realtime = false)
310 {
311  return std::make_shared<nav2::SimpleActionServer<ActionT>>(
312  node, action_name, execute_callback, goal_received_callback, complete_cb,
313  server_timeout, spin_thread, realtime);
314 }
315 
323 template<typename ActionT, typename NodeT>
324 typename nav2::ActionClient<ActionT>::SharedPtr create_action_client(
325  const NodeT & node,
326  const std::string & action_name,
327  rclcpp::CallbackGroup::SharedPtr callback_group = nullptr)
328 {
329  auto client = rclcpp_action::create_client<ActionT>(node, action_name, callback_group);
330  nav2::setIntrospectionMode(
331  client,
332  node->get_node_parameters_interface(), node->get_clock());
333  return client;
334 }
335 
336 } // namespace interfaces
337 
347 template<typename NodeT, typename DurationRepT, typename DurationT, typename CallbackT>
348 rclcpp::TimerBase::SharedPtr create_timer(
349  NodeT node,
350  std::chrono::duration<DurationRepT, DurationT> period,
351  CallbackT callback,
352  rclcpp::CallbackGroup::SharedPtr group = nullptr)
353 {
354  return rclcpp::create_timer(
355  selectSteadyOrSimClock(node),
356  period,
357  std::move(callback),
358  group,
359  node->get_node_base_interface().get(),
360  node->get_node_timers_interface().get());
361 }
362 
363 } // namespace nav2
364 
365 #endif // NAV2_ROS_COMMON__INTERFACE_FACTORIES_HPP_
A QoS profile for standard reliable topics with a history of 10 messages.