Nav2 Navigation Stack - lyrical  lyrical
ROS 2 Navigation Stack
service_client.hpp
1 // Copyright (c) 2018 Intel Corporation
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__SERVICE_CLIENT_HPP_
16 #define NAV2_ROS_COMMON__SERVICE_CLIENT_HPP_
17 
18 #include <string>
19 #include <memory>
20 #include <chrono>
21 #include "rclcpp/callback_group.hpp"
22 #include "rclcpp/client.hpp"
23 #include "rclcpp/clock.hpp"
24 #include "rclcpp/create_client.hpp"
25 #include "rclcpp/executors.hpp"
26 #include "rclcpp/executors/single_threaded_executor.hpp"
27 #include "rclcpp/future_return_code.hpp"
28 #include "rclcpp/logger.hpp"
29 #include "rclcpp/logging.hpp"
30 #include "rclcpp/node_interfaces/node_base_interface.hpp"
31 #include "rclcpp/qos.hpp"
32 #include "rclcpp/utilities.hpp"
33 #include "nav2_ros_common/node_utils.hpp"
34 
35 namespace nav2
36 {
37 
42 template<typename ServiceT>
44 {
45 public:
46  using SharedPtr = std::shared_ptr<nav2::ServiceClient<ServiceT>>;
47  using UniquePtr = std::unique_ptr<nav2::ServiceClient<ServiceT>>;
48 
55  template<typename NodeT>
56  explicit ServiceClient(
57  const std::string & service_name,
58  const NodeT & provided_node, bool use_internal_executor = false)
59  : service_name_(service_name),
60  clock_(provided_node->get_clock()),
61  logger_(provided_node->get_logger()),
62  node_base_interface_(provided_node->get_node_base_interface()),
63  use_internal_executor_(use_internal_executor)
64  {
65  if (use_internal_executor) {
66  callback_group_ = provided_node->create_callback_group(
67  rclcpp::CallbackGroupType::MutuallyExclusive,
68  false);
69  callback_group_executor_ = std::make_shared<rclcpp::executors::SingleThreadedExecutor>();
70  callback_group_executor_->add_callback_group(
71  callback_group_,
72  provided_node->get_node_base_interface());
73  }
74  // When a nullptr is passed, the client will use the default callback group
75  client_ = rclcpp::create_client<ServiceT>(
76  provided_node->get_node_base_interface(),
77  provided_node->get_node_graph_interface(),
78  provided_node->get_node_services_interface(),
79  service_name,
80  rclcpp::ServicesQoS(), // Use consistent QoS settings
81  callback_group_);
82 
83  nav2::setIntrospectionMode(
84  this->client_,
85  provided_node->get_node_parameters_interface(), clock_);
86  }
87 
88  using RequestType = typename ServiceT::Request;
89  using ResponseType = typename ServiceT::Response;
90 
97  typename ResponseType::SharedPtr invoke(
98  typename RequestType::SharedPtr & request,
99  const std::chrono::nanoseconds timeout = std::chrono::nanoseconds(-1),
100  const std::chrono::nanoseconds wait_for_service_timeout = std::chrono::seconds(10))
101  {
102  auto now = clock_->now();
103  while (!client_->wait_for_service(std::chrono::seconds(1))) {
104  if (!rclcpp::ok()) {
105  throw std::runtime_error(
106  service_name_ + " service client: interrupted while waiting for service");
107  }
108  RCLCPP_INFO(
109  logger_, "%s service client: waiting for service to appear...",
110  service_name_.c_str());
111 
112  if (clock_->now() - now > wait_for_service_timeout) {
113  throw std::runtime_error(
114  service_name_ + " service client: timed out waiting for service");
115  }
116  }
117 
118  RCLCPP_DEBUG(
119  logger_, "%s service client: send async request",
120  service_name_.c_str());
121  auto future_result = client_->async_send_request(request);
122  if (spin_until_complete(future_result, timeout) != rclcpp::FutureReturnCode::SUCCESS) {
123  // Pending request must be manually cleaned up if execution is interrupted or timed out
124  client_->remove_pending_request(future_result);
125  throw std::runtime_error(service_name_ + " service client: async_send_request failed");
126  }
127 
128  return future_result.get();
129  }
130 
137  bool invoke(
138  typename RequestType::SharedPtr & request,
139  typename ResponseType::SharedPtr & response,
140  const std::chrono::nanoseconds wait_for_service_timeout = std::chrono::seconds(10))
141  {
142  auto now = clock_->now();
143  while (!client_->wait_for_service(std::chrono::seconds(1))) {
144  if (!rclcpp::ok()) {
145  throw std::runtime_error(
146  service_name_ + " service client: interrupted while waiting for service");
147  }
148  RCLCPP_INFO(
149  logger_, "%s service client: waiting for service to appear...",
150  service_name_.c_str());
151 
152  if (clock_->now() - now > wait_for_service_timeout) {
153  throw std::runtime_error(
154  service_name_ + " service client: timed out waiting for service");
155  }
156  }
157 
158  RCLCPP_DEBUG(
159  logger_, "%s service client: send async request",
160  service_name_.c_str());
161  auto future_result = client_->async_send_request(request);
162  if (spin_until_complete(future_result) != rclcpp::FutureReturnCode::SUCCESS) {
163  // Pending request must be manually cleaned up if execution is interrupted or timed out
164  client_->remove_pending_request(future_result);
165  return false;
166  }
167 
168  response = future_result.get();
169  return response.get();
170  }
171 
177  std::shared_future<typename ResponseType::SharedPtr> async_call(
178  typename RequestType::SharedPtr & request)
179  {
180  auto future_result = client_->async_send_request(request);
181  return future_result.share();
182  }
183 
184 
190  template<typename CallbackT>
191  void async_call(typename RequestType::SharedPtr request, CallbackT && callback)
192  {
193  client_->async_send_request(request, callback);
194  }
195 
201  bool wait_for_service(const std::chrono::nanoseconds timeout = std::chrono::nanoseconds::max())
202  {
203  return client_->wait_for_service(timeout);
204  }
205 
213  template<typename FutureT>
214  rclcpp::FutureReturnCode spin_until_complete(
215  const FutureT & future,
216  const std::chrono::nanoseconds timeout = std::chrono::nanoseconds(-1))
217  {
218  if (use_internal_executor_) {
219  return callback_group_executor_->spin_until_future_complete(future, timeout);
220  } else {
221  return rclcpp::spin_until_future_complete(node_base_interface_, future, timeout);
222  }
223  }
224 
229  std::string getServiceName()
230  {
231  return service_name_;
232  }
233 
237  void stop()
238  {
239  if (client_) {
240  callback_group_executor_->cancel();
241  }
242  }
243 
244 protected:
245  std::string service_name_;
246  rclcpp::Clock::SharedPtr clock_;
247  rclcpp::Logger logger_{rclcpp::get_logger("nav2_ros_common")};
248  rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_base_interface_;
249  rclcpp::CallbackGroup::SharedPtr callback_group_{nullptr};
250  rclcpp::executors::SingleThreadedExecutor::SharedPtr callback_group_executor_;
251  typename rclcpp::Client<ServiceT>::SharedPtr client_;
252  bool use_internal_executor_;
253 };
254 
255 } // namespace nav2
256 
257 #endif // NAV2_ROS_COMMON__SERVICE_CLIENT_HPP_
A simple wrapper on ROS2 services client.
std::shared_future< typename ResponseType::SharedPtr > async_call(typename RequestType::SharedPtr &request)
Asynchronously call the service.
void stop()
Stop any running spin operations on the internal executor.
rclcpp::FutureReturnCode spin_until_complete(const FutureT &future, const std::chrono::nanoseconds timeout=std::chrono::nanoseconds(-1))
Spins the executor until the provided future is complete or the timeout is reached.
ResponseType::SharedPtr invoke(typename RequestType::SharedPtr &request, const std::chrono::nanoseconds timeout=std::chrono::nanoseconds(-1), const std::chrono::nanoseconds wait_for_service_timeout=std::chrono::seconds(10))
Invoke the service and block until completed or timed out.
void async_call(typename RequestType::SharedPtr request, CallbackT &&callback)
Asynchronously call the service with a callback.
bool invoke(typename RequestType::SharedPtr &request, typename ResponseType::SharedPtr &response, const std::chrono::nanoseconds wait_for_service_timeout=std::chrono::seconds(10))
Invoke the service and block until completed.
ServiceClient(const std::string &service_name, const NodeT &provided_node, bool use_internal_executor=false)
A constructor.
bool wait_for_service(const std::chrono::nanoseconds timeout=std::chrono::nanoseconds::max())
Block until a service is available or timeout.
std::string getServiceName()
Gets the service name.