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/rclcpp.hpp"
22 #include "nav2_ros_common/node_utils.hpp"
23 
24 namespace nav2
25 {
26 
31 template<typename ServiceT>
33 {
34 public:
35  using SharedPtr = std::shared_ptr<nav2::ServiceClient<ServiceT>>;
36  using UniquePtr = std::unique_ptr<nav2::ServiceClient<ServiceT>>;
37 
44  template<typename NodeT>
45  explicit ServiceClient(
46  const std::string & service_name,
47  const NodeT & provided_node, bool use_internal_executor = false)
48  : service_name_(service_name),
49  clock_(provided_node->get_clock()),
50  logger_(provided_node->get_logger()),
51  node_base_interface_(provided_node->get_node_base_interface()),
52  use_internal_executor_(use_internal_executor)
53  {
54  if (use_internal_executor) {
55  callback_group_ = provided_node->create_callback_group(
56  rclcpp::CallbackGroupType::MutuallyExclusive,
57  false);
58  callback_group_executor_ = std::make_shared<rclcpp::executors::SingleThreadedExecutor>();
59  callback_group_executor_->add_callback_group(
60  callback_group_,
61  provided_node->get_node_base_interface());
62  }
63  // When a nullptr is passed, the client will use the default callback group
64  client_ = rclcpp::create_client<ServiceT>(
65  provided_node->get_node_base_interface(),
66  provided_node->get_node_graph_interface(),
67  provided_node->get_node_services_interface(),
68  service_name,
69  rclcpp::ServicesQoS(), // Use consistent QoS settings
70  callback_group_);
71 
72  nav2::setIntrospectionMode(
73  this->client_,
74  provided_node->get_node_parameters_interface(), clock_);
75  }
76 
77  using RequestType = typename ServiceT::Request;
78  using ResponseType = typename ServiceT::Response;
79 
86  typename ResponseType::SharedPtr invoke(
87  typename RequestType::SharedPtr & request,
88  const std::chrono::nanoseconds timeout = std::chrono::nanoseconds(-1),
89  const std::chrono::nanoseconds wait_for_service_timeout = std::chrono::seconds(10))
90  {
91  auto now = clock_->now();
92  while (!client_->wait_for_service(std::chrono::seconds(1))) {
93  if (!rclcpp::ok()) {
94  throw std::runtime_error(
95  service_name_ + " service client: interrupted while waiting for service");
96  }
97  RCLCPP_INFO(
98  logger_, "%s service client: waiting for service to appear...",
99  service_name_.c_str());
100 
101  if (clock_->now() - now > wait_for_service_timeout) {
102  throw std::runtime_error(
103  service_name_ + " service client: timed out waiting for service");
104  }
105  }
106 
107  RCLCPP_DEBUG(
108  logger_, "%s service client: send async request",
109  service_name_.c_str());
110  auto future_result = client_->async_send_request(request);
111  if (spin_until_complete(future_result, timeout) != rclcpp::FutureReturnCode::SUCCESS) {
112  // Pending request must be manually cleaned up if execution is interrupted or timed out
113  client_->remove_pending_request(future_result);
114  throw std::runtime_error(service_name_ + " service client: async_send_request failed");
115  }
116 
117  return future_result.get();
118  }
119 
126  bool invoke(
127  typename RequestType::SharedPtr & request,
128  typename ResponseType::SharedPtr & response,
129  const std::chrono::nanoseconds wait_for_service_timeout = std::chrono::seconds(10))
130  {
131  auto now = clock_->now();
132  while (!client_->wait_for_service(std::chrono::seconds(1))) {
133  if (!rclcpp::ok()) {
134  throw std::runtime_error(
135  service_name_ + " service client: interrupted while waiting for service");
136  }
137  RCLCPP_INFO(
138  logger_, "%s service client: waiting for service to appear...",
139  service_name_.c_str());
140 
141  if (clock_->now() - now > wait_for_service_timeout) {
142  throw std::runtime_error(
143  service_name_ + " service client: timed out waiting for service");
144  }
145  }
146 
147  RCLCPP_DEBUG(
148  logger_, "%s service client: send async request",
149  service_name_.c_str());
150  auto future_result = client_->async_send_request(request);
151  if (spin_until_complete(future_result) != rclcpp::FutureReturnCode::SUCCESS) {
152  // Pending request must be manually cleaned up if execution is interrupted or timed out
153  client_->remove_pending_request(future_result);
154  return false;
155  }
156 
157  response = future_result.get();
158  return response.get();
159  }
160 
166  std::shared_future<typename ResponseType::SharedPtr> async_call(
167  typename RequestType::SharedPtr & request)
168  {
169  auto future_result = client_->async_send_request(request);
170  return future_result.share();
171  }
172 
173 
179  template<typename CallbackT>
180  void async_call(typename RequestType::SharedPtr request, CallbackT && callback)
181  {
182  client_->async_send_request(request, callback);
183  }
184 
190  bool wait_for_service(const std::chrono::nanoseconds timeout = std::chrono::nanoseconds::max())
191  {
192  return client_->wait_for_service(timeout);
193  }
194 
202  template<typename FutureT>
203  rclcpp::FutureReturnCode spin_until_complete(
204  const FutureT & future,
205  const std::chrono::nanoseconds timeout = std::chrono::nanoseconds(-1))
206  {
207  if (use_internal_executor_) {
208  return callback_group_executor_->spin_until_future_complete(future, timeout);
209  } else {
210  return rclcpp::spin_until_future_complete(node_base_interface_, future, timeout);
211  }
212  }
213 
218  std::string getServiceName()
219  {
220  return service_name_;
221  }
222 
226  void stop()
227  {
228  if (client_) {
229  callback_group_executor_->cancel();
230  }
231  }
232 
233 protected:
234  std::string service_name_;
235  rclcpp::Clock::SharedPtr clock_;
236  rclcpp::Logger logger_{rclcpp::get_logger("nav2_ros_common")};
237  rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_base_interface_;
238  rclcpp::CallbackGroup::SharedPtr callback_group_{nullptr};
239  rclcpp::executors::SingleThreadedExecutor::SharedPtr callback_group_executor_;
240  typename rclcpp::Client<ServiceT>::SharedPtr client_;
241  bool use_internal_executor_;
242 };
243 
244 } // namespace nav2
245 
246 #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.