Nav2 Navigation Stack - rolling  main
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 
31 namespace nav2
32 {
33 
34 namespace interfaces
35 {
36 
50 inline rclcpp::SubscriptionOptions createSubscriptionOptions(
51  const std::string & topic_name,
52  const bool allow_parameter_qos_overrides = true,
53  const rclcpp::CallbackGroup::SharedPtr callback_group_ptr = nullptr,
54  rclcpp::QOSMessageLostCallbackType qos_message_lost_callback = nullptr,
55  rclcpp::SubscriptionMatchedCallbackType subscription_matched_callback = nullptr,
56  rclcpp::IncompatibleTypeCallbackType incompatible_qos_type_callback = nullptr,
57  rclcpp::QOSRequestedIncompatibleQoSCallbackType requested_incompatible_qos_callback = nullptr,
58  rclcpp::QOSDeadlineRequestedCallbackType qos_deadline_requested_callback = nullptr,
59  rclcpp::QOSLivelinessChangedCallbackType qos_liveliness_changed_callback = nullptr)
60 {
61  rclcpp::SubscriptionOptions options;
62  // Allow for all topics to have QoS overrides
63  if (allow_parameter_qos_overrides) {
64  options.qos_overriding_options = rclcpp::QosOverridingOptions(
65  {rclcpp::QosPolicyKind::Depth, rclcpp::QosPolicyKind::Durability,
66  rclcpp::QosPolicyKind::Reliability, rclcpp::QosPolicyKind::History});
67  }
68 
69  // Set the callback group to use for this subscription, if given
70  options.callback_group = callback_group_ptr;
71 
72  // ROS 2 default logs this already
73  options.event_callbacks.incompatible_qos_callback = requested_incompatible_qos_callback;
74  options.event_callbacks.incompatible_type_callback = incompatible_qos_type_callback;
75 
76  // Set the event callbacks if given, else log
77  if (qos_message_lost_callback) {
78  options.event_callbacks.message_lost_callback =
79  qos_message_lost_callback;
80  } else {
81  options.event_callbacks.message_lost_callback =
82  [topic_name](rclcpp::QOSMessageLostInfo & info) {
83  RCLCPP_WARN(
84  rclcpp::get_logger("nav2::interfaces"),
85  "[%lu] Message was dropped on topic [%s] due to queue size or network constraints.",
86  info.total_count_change,
87  topic_name.c_str());
88  };
89  }
90 
91  if (subscription_matched_callback) {
92  options.event_callbacks.matched_callback = subscription_matched_callback;
93  } else {
94  options.event_callbacks.matched_callback =
95  [topic_name](rclcpp::MatchedInfo & status) {
96  if (status.current_count_change > 0) {
97  RCLCPP_DEBUG(
98  rclcpp::get_logger("nav2::interfaces"),
99  "Connected: %d new publisher(s) to [%s]. Total active: %zu.",
100  status.current_count_change,
101  topic_name.c_str(),
102  status.current_count);
103  } else if (status.current_count_change < 0) {
104  RCLCPP_DEBUG(
105  rclcpp::get_logger("nav2::interfaces"),
106  "Disconnected: %d publisher(s) from [%s]. Total active: %zu.",
107  -status.current_count_change,
108  topic_name.c_str(),
109  status.current_count);
110  }
111  };
112  }
113 
114  options.event_callbacks.deadline_callback = qos_deadline_requested_callback;
115  options.event_callbacks.liveliness_callback = qos_liveliness_changed_callback;
116  return options;
117 }
118 
131 inline rclcpp::PublisherOptions createPublisherOptions(
132  const std::string & topic_name,
133  const bool allow_parameter_qos_overrides = true,
134  const rclcpp::CallbackGroup::SharedPtr callback_group_ptr = nullptr,
135  rclcpp::PublisherMatchedCallbackType publisher_matched_callback = nullptr,
136  rclcpp::IncompatibleTypeCallbackType incompatible_qos_type_callback = nullptr,
137  rclcpp::QOSOfferedIncompatibleQoSCallbackType offered_incompatible_qos_cb = nullptr,
138  rclcpp::QOSDeadlineOfferedCallbackType qos_deadline_offered_callback = nullptr,
139  rclcpp::QOSLivelinessLostCallbackType qos_liveliness_lost_callback = nullptr)
140 {
141  rclcpp::PublisherOptions options;
142  // Allow for all topics to have QoS overrides
143  if (allow_parameter_qos_overrides) {
144  options.qos_overriding_options = rclcpp::QosOverridingOptions(
145  {rclcpp::QosPolicyKind::Depth, rclcpp::QosPolicyKind::Durability,
146  rclcpp::QosPolicyKind::Reliability, rclcpp::QosPolicyKind::History});
147  }
148 
149  // Set the callback group to use for this publisher, if given
150  options.callback_group = callback_group_ptr;
151 
152  // ROS 2 default logs this already
153  options.event_callbacks.incompatible_qos_callback = offered_incompatible_qos_cb;
154  options.event_callbacks.incompatible_type_callback = incompatible_qos_type_callback;
155 
156  // Set the event callbacks, else log
157  if (publisher_matched_callback) {
158  options.event_callbacks.matched_callback = publisher_matched_callback;
159  } else {
160  options.event_callbacks.matched_callback =
161  [topic_name](rclcpp::MatchedInfo & status) {
162  if (status.current_count_change > 0) {
163  RCLCPP_DEBUG(
164  rclcpp::get_logger("nav2::interfaces"),
165  "Connected: %d new subscriber(s) to [%s]. Total active: %zu.",
166  status.current_count_change,
167  topic_name.c_str(),
168  status.current_count);
169  } else if (status.current_count_change < 0) {
170  RCLCPP_DEBUG(
171  rclcpp::get_logger("nav2::interfaces"),
172  "Disconnected: %d subscriber(s) from [%s]. Total active: %zu.",
173  -status.current_count_change,
174  topic_name.c_str(),
175  status.current_count);
176  }
177  };
178  }
179 
180  options.event_callbacks.deadline_callback = qos_deadline_offered_callback;
181  options.event_callbacks.liveliness_callback = qos_liveliness_lost_callback;
182  return options;
183 }
184 
194 template<typename MessageT, typename NodeT, typename CallbackT>
195 typename nav2::Subscription<MessageT>::SharedPtr create_subscription(
196  const NodeT & node,
197  const std::string & topic_name,
198  CallbackT && callback,
199  const rclcpp::QoS & qos = nav2::qos::StandardTopicQoS(),
200  const rclcpp::CallbackGroup::SharedPtr & callback_group = nullptr)
201 {
202  bool allow_parameter_qos_overrides = nav2::declare_or_get_parameter(
203  node, "allow_parameter_qos_overrides", true);
204 
205  auto params_interface = node->get_node_parameters_interface();
206  auto topics_interface = node->get_node_topics_interface();
207  return rclcpp::create_subscription<MessageT, CallbackT>(
208  params_interface,
209  topics_interface,
210  topic_name,
211  qos,
212  std::forward<CallbackT>(callback),
213  createSubscriptionOptions(topic_name, allow_parameter_qos_overrides, callback_group));
214 }
215 
224 template<typename MessageT, typename NodeT>
225 typename nav2::Publisher<MessageT>::SharedPtr create_publisher(
226  const NodeT & node,
227  const std::string & topic_name,
228  const rclcpp::QoS & qos = nav2::qos::StandardTopicQoS(),
229  const rclcpp::CallbackGroup::SharedPtr & callback_group = nullptr)
230 {
231  bool allow_parameter_qos_overrides = nav2::declare_or_get_parameter(
232  node, "allow_parameter_qos_overrides", true);
233  using PublisherT = nav2::Publisher<MessageT>;
234  auto pub = rclcpp::create_publisher<MessageT, std::allocator<void>, PublisherT>(
235  *node,
236  topic_name,
237  qos,
238  createPublisherOptions(topic_name, allow_parameter_qos_overrides, callback_group));
239  return pub;
240 }
241 
249 template<typename SrvT, typename NodeT>
250 typename nav2::ServiceClient<SrvT>::SharedPtr create_client(
251  const NodeT & node,
252  const std::string & service_name,
253  bool use_internal_executor = false)
254 {
255  return std::make_shared<nav2::ServiceClient<SrvT>>(
256  service_name, node, use_internal_executor);
257 }
258 
267 template<typename SrvT, typename NodeT, typename CallbackT>
268 typename nav2::ServiceServer<SrvT>::SharedPtr create_service(
269  const NodeT & node,
270  const std::string & service_name,
271  CallbackT && callback,
272  rclcpp::CallbackGroup::SharedPtr callback_group = nullptr)
273 {
274  using Request = typename SrvT::Request;
275  using Response = typename SrvT::Response;
276  using CallbackFn = std::function<void(
277  const std::shared_ptr<rmw_request_id_t>,
278  const std::shared_ptr<Request>,
279  std::shared_ptr<Response>)>;
280  CallbackFn cb = std::forward<CallbackT>(callback);
281 
282  return std::make_shared<nav2::ServiceServer<SrvT>>(
283  service_name, node, cb, callback_group);
284 }
285 
298 template<typename ActionT, typename NodeT>
299 typename nav2::SimpleActionServer<ActionT>::SharedPtr create_action_server(
300  const NodeT & node,
301  const std::string & action_name,
302  typename nav2::SimpleActionServer<ActionT>::ExecuteCallback execute_callback,
303  typename nav2::SimpleActionServer<ActionT>::CompletionCallback complete_cb = nullptr,
304  std::chrono::milliseconds server_timeout = std::chrono::milliseconds(500),
305  bool spin_thread = false,
306  const bool realtime = false)
307 {
308  return std::make_shared<nav2::SimpleActionServer<ActionT>>(
309  node, action_name, execute_callback, complete_cb, server_timeout, spin_thread, realtime);
310 }
311 
319 template<typename ActionT, typename NodeT>
320 typename nav2::ActionClient<ActionT>::SharedPtr create_action_client(
321  const NodeT & node,
322  const std::string & action_name,
323  rclcpp::CallbackGroup::SharedPtr callback_group = nullptr)
324 {
325  auto client = rclcpp_action::create_client<ActionT>(node, action_name, callback_group);
326  nav2::setIntrospectionMode(client,
327  node->get_node_parameters_interface(), node->get_clock());
328  return client;
329 }
330 
331 } // namespace interfaces
332 
333 } // namespace nav2
334 
335 #endif // NAV2_ROS_COMMON__INTERFACE_FACTORIES_HPP_
A QoS profile for standard reliable topics with a history of 10 messages.