Nav2 Navigation Stack - kilted  kilted
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_UTIL__SERVICE_CLIENT_HPP_
16 #define NAV2_UTIL__SERVICE_CLIENT_HPP_
17 
18 #include <string>
19 #include <memory>
20 #include <chrono>
21 #include "rclcpp/rclcpp.hpp"
22 
23 namespace nav2_util
24 {
25 
30 template<class ServiceT, typename NodeT = rclcpp::Node::SharedPtr>
32 {
33 public:
40  explicit ServiceClient(
41  const std::string & service_name,
42  const NodeT & provided_node, bool use_internal_executor = false)
43  : service_name_(service_name), node_(provided_node), use_internal_executor_(use_internal_executor)
44  {
45  if (use_internal_executor) {
46  callback_group_ = node_->create_callback_group(
47  rclcpp::CallbackGroupType::MutuallyExclusive,
48  false);
49  callback_group_executor_ = std::make_shared<rclcpp::executors::SingleThreadedExecutor>();
50  callback_group_executor_->add_callback_group(callback_group_,
51  node_->get_node_base_interface());
52  }
53  // When a nullptr is passed, the client will use the default callback group
54  client_ = node_->template create_client<ServiceT>(
55  service_name,
56  rclcpp::SystemDefaultsQoS(),
57  callback_group_);
58  rcl_service_introspection_state_t introspection_state = RCL_SERVICE_INTROSPECTION_OFF;
59  if (!node_->has_parameter("service_introspection_mode")) {
60  node_->declare_parameter("service_introspection_mode", "disabled");
61  }
62  std::string service_introspection_mode =
63  node_->get_parameter("service_introspection_mode").as_string();
64  if (service_introspection_mode == "metadata") {
65  introspection_state = RCL_SERVICE_INTROSPECTION_METADATA;
66  } else if (service_introspection_mode == "contents") {
67  introspection_state = RCL_SERVICE_INTROSPECTION_CONTENTS;
68  }
69 
70  this->client_->configure_introspection(
71  node_->get_clock(), rclcpp::SystemDefaultsQoS(), introspection_state);
72  }
73 
74  using RequestType = typename ServiceT::Request;
75  using ResponseType = typename ServiceT::Response;
76  using SharedPtr = std::shared_ptr<ServiceClient<ServiceT, NodeT>>;
77 
84  typename ResponseType::SharedPtr invoke(
85  typename RequestType::SharedPtr & request,
86  const std::chrono::nanoseconds timeout = std::chrono::nanoseconds(-1))
87  {
88  while (!client_->wait_for_service(std::chrono::seconds(1))) {
89  if (!rclcpp::ok()) {
90  throw std::runtime_error(
91  service_name_ + " service client: interrupted while waiting for service");
92  }
93  RCLCPP_INFO(
94  node_->get_logger(), "%s service client: waiting for service to appear...",
95  service_name_.c_str());
96  }
97 
98  RCLCPP_DEBUG(
99  node_->get_logger(), "%s service client: send async request",
100  service_name_.c_str());
101  auto future_result = client_->async_send_request(request);
102  if (spin_until_complete(future_result, timeout) != rclcpp::FutureReturnCode::SUCCESS) {
103  // Pending request must be manually cleaned up if execution is interrupted or timed out
104  client_->remove_pending_request(future_result);
105  throw std::runtime_error(service_name_ + " service client: async_send_request failed");
106  }
107 
108  return future_result.get();
109  }
110 
117  bool invoke(
118  typename RequestType::SharedPtr & request,
119  typename ResponseType::SharedPtr & response)
120  {
121  while (!client_->wait_for_service(std::chrono::seconds(1))) {
122  if (!rclcpp::ok()) {
123  throw std::runtime_error(
124  service_name_ + " service client: interrupted while waiting for service");
125  }
126  RCLCPP_INFO(
127  node_->get_logger(), "%s service client: waiting for service to appear...",
128  service_name_.c_str());
129  }
130 
131  RCLCPP_DEBUG(
132  node_->get_logger(), "%s service client: send async request",
133  service_name_.c_str());
134  auto future_result = client_->async_send_request(request);
135  if (spin_until_complete(future_result) != rclcpp::FutureReturnCode::SUCCESS) {
136  // Pending request must be manually cleaned up if execution is interrupted or timed out
137  client_->remove_pending_request(future_result);
138  return false;
139  }
140 
141  response = future_result.get();
142  return response.get();
143  }
144 
150  std::shared_future<typename ResponseType::SharedPtr> async_call(
151  typename RequestType::SharedPtr & request)
152  {
153  auto future_result = client_->async_send_request(request);
154  return future_result.share();
155  }
156 
157 
163  template<typename CallbackT>
164  void async_call(typename RequestType::SharedPtr request, CallbackT && callback)
165  {
166  client_->async_send_request(request, callback);
167  }
168 
174  bool wait_for_service(const std::chrono::nanoseconds timeout = std::chrono::nanoseconds::max())
175  {
176  return client_->wait_for_service(timeout);
177  }
178 
186  template<typename FutureT>
187  rclcpp::FutureReturnCode spin_until_complete(
188  const FutureT & future,
189  const std::chrono::nanoseconds timeout = std::chrono::nanoseconds(-1))
190  {
191  if (use_internal_executor_) {
192  return callback_group_executor_->spin_until_future_complete(future, timeout);
193  } else {
194  return rclcpp::spin_until_future_complete(node_, future, timeout);
195  }
196  }
197 
202  std::string getServiceName()
203  {
204  return service_name_;
205  }
206 
207 protected:
208  std::string service_name_;
209  NodeT node_;
210  rclcpp::CallbackGroup::SharedPtr callback_group_{nullptr};
211  rclcpp::executors::SingleThreadedExecutor::SharedPtr callback_group_executor_;
212  typename rclcpp::Client<ServiceT>::SharedPtr client_;
213  bool use_internal_executor_;
214 };
215 
216 } // namespace nav2_util
217 
218 #endif // NAV2_UTIL__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.
bool invoke(typename RequestType::SharedPtr &request, typename ResponseType::SharedPtr &response)
Invoke the service and block until completed.
ServiceClient(const std::string &service_name, const NodeT &provided_node, bool use_internal_executor=false)
A constructor.
ResponseType::SharedPtr invoke(typename RequestType::SharedPtr &request, const std::chrono::nanoseconds timeout=std::chrono::nanoseconds(-1))
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.
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.
std::string getServiceName()
Gets the service name.
bool wait_for_service(const std::chrono::nanoseconds timeout=std::chrono::nanoseconds::max())
Block until a service is available or timeout.