ROS 2 rclcpp + rcl - humble  humble
ROS 2 C++ Client Library with ROS Client Library
client.cpp
1 // Copyright 2015 Open Source Robotics Foundation, Inc.
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 #include "rclcpp/client.hpp"
16 
17 #include <algorithm>
18 #include <chrono>
19 #include <cstdio>
20 #include <memory>
21 #include <string>
22 
23 #include "rcl/graph.h"
24 #include "rcl/node.h"
25 #include "rcl/wait.h"
26 #include "rclcpp/exceptions.hpp"
27 #include "rclcpp/node_interfaces/node_base_interface.hpp"
28 #include "rclcpp/node_interfaces/node_graph_interface.hpp"
29 #include "rclcpp/utilities.hpp"
30 #include "rclcpp/logging.hpp"
31 
32 using rclcpp::ClientBase;
34 using rclcpp::exceptions::throw_from_rcl_error;
35 
36 ClientBase::ClientBase(
38  rclcpp::node_interfaces::NodeGraphInterface::SharedPtr node_graph)
39 : node_graph_(node_graph),
40  node_handle_(node_base->get_shared_rcl_node_handle()),
41  context_(node_base->get_context()),
42  node_logger_(rclcpp::get_node_logger(node_handle_.get()))
43 {
44  std::weak_ptr<rcl_node_t> weak_node_handle(node_handle_);
45  rcl_client_t * new_rcl_client = new rcl_client_t;
46  *new_rcl_client = rcl_get_zero_initialized_client();
47  client_handle_.reset(
48  new_rcl_client, [weak_node_handle](rcl_client_t * client)
49  {
50  auto handle = weak_node_handle.lock();
51  if (handle) {
52  if (rcl_client_fini(client, handle.get()) != RCL_RET_OK) {
53  RCLCPP_ERROR(
54  rclcpp::get_node_logger(handle.get()).get_child("rclcpp"),
55  "Error in destruction of rcl client handle: %s", rcl_get_error_string().str);
56  rcl_reset_error();
57  }
58  } else {
59  RCLCPP_ERROR(
60  rclcpp::get_logger("rclcpp"),
61  "Error in destruction of rcl client handle: "
62  "the Node Handle was destructed too early. You will leak memory");
63  }
64  delete client;
65  });
66 }
67 
68 ClientBase::~ClientBase()
69 {
71  // Make sure the client handle is destructed as early as possible and before the node handle
72  client_handle_.reset();
73 }
74 
75 bool
76 ClientBase::take_type_erased_response(void * response_out, rmw_request_id_t & request_header_out)
77 {
79  this->get_client_handle().get(),
80  &request_header_out,
81  response_out);
82  if (RCL_RET_CLIENT_TAKE_FAILED == ret) {
83  return false;
84  } else if (RCL_RET_OK != ret) {
85  rclcpp::exceptions::throw_from_rcl_error(ret);
86  }
87  return true;
88 }
89 
90 const char *
92 {
93  return rcl_client_get_service_name(this->get_client_handle().get());
94 }
95 
96 std::shared_ptr<rcl_client_t>
98 {
99  return client_handle_;
100 }
101 
102 std::shared_ptr<const rcl_client_t>
104 {
105  return client_handle_;
106 }
107 
108 bool
110 {
111  bool is_ready;
113  this->get_rcl_node_handle(),
114  this->get_client_handle().get(),
115  &is_ready);
116  if (RCL_RET_NODE_INVALID == ret) {
117  const rcl_node_t * node_handle = this->get_rcl_node_handle();
118  if (node_handle && !rcl_context_is_valid(node_handle->context)) {
119  // context is shutdown, do a soft failure
120  return false;
121  }
122  }
123  if (ret != RCL_RET_OK) {
124  throw_from_rcl_error(ret, "rcl_service_server_is_available failed");
125  }
126  return is_ready;
127 }
128 
129 bool
130 ClientBase::wait_for_service_nanoseconds(std::chrono::nanoseconds timeout)
131 {
132  auto start = std::chrono::steady_clock::now();
133  // make an event to reuse, rather than create a new one each time
134  auto node_ptr = node_graph_.lock();
135  if (!node_ptr) {
136  throw InvalidNodeError();
137  }
138  // check to see if the server is ready immediately
139  if (this->service_is_ready()) {
140  return true;
141  }
142  if (timeout == std::chrono::nanoseconds(0)) {
143  // check was non-blocking, return immediately
144  return false;
145  }
146  auto event = node_ptr->get_graph_event();
147  // update the time even on the first loop to account for time spent in the first call
148  // to this->server_is_ready()
149  std::chrono::nanoseconds time_to_wait =
150  timeout > std::chrono::nanoseconds(0) ?
151  timeout - (std::chrono::steady_clock::now() - start) :
152  std::chrono::nanoseconds::max();
153  if (time_to_wait < std::chrono::nanoseconds(0)) {
154  // Do not allow the time_to_wait to become negative when timeout was originally positive.
155  // Setting time_to_wait to 0 will allow one non-blocking wait because of the do-while.
156  time_to_wait = std::chrono::nanoseconds(0);
157  }
158  do {
159  if (!rclcpp::ok(this->context_)) {
160  return false;
161  }
162  // Limit each wait to 100ms to workaround an issue specific to the Connext RMW implementation.
163  // A race condition means that graph changes for services becoming available may trigger the
164  // wait set to wake up, but then not be reported as ready immediately after the wake up
165  // (see https://github.com/ros2/rmw_connext/issues/201)
166  // If no other graph events occur, the wait set will not be triggered again until the timeout
167  // has been reached, despite the service being available, so we artificially limit the wait
168  // time to limit the delay.
169  node_ptr->wait_for_graph_change(
170  event, std::min(time_to_wait, std::chrono::nanoseconds(RCL_MS_TO_NS(100))));
171  // Because of the aforementioned race condition, we check if the service is ready even if the
172  // graph event wasn't triggered.
173  event->check_and_clear();
174  if (this->service_is_ready()) {
175  return true;
176  }
177  // server is not ready, loop if there is time left
178  if (timeout > std::chrono::nanoseconds(0)) {
179  time_to_wait = timeout - (std::chrono::steady_clock::now() - start);
180  }
181  // if timeout is negative, time_to_wait will never reach zero
182  } while (time_to_wait > std::chrono::nanoseconds(0));
183  return false; // timeout exceeded while waiting for the server to be ready
184 }
185 
186 rcl_node_t *
187 ClientBase::get_rcl_node_handle()
188 {
189  return node_handle_.get();
190 }
191 
192 const rcl_node_t *
193 ClientBase::get_rcl_node_handle() const
194 {
195  return node_handle_.get();
196 }
197 
198 bool
200 {
201  return in_use_by_wait_set_.exchange(in_use_state);
202 }
203 
206 {
207  const rmw_qos_profile_t * qos =
208  rcl_client_request_publisher_get_actual_qos(client_handle_.get());
209  if (!qos) {
210  auto msg =
211  std::string("failed to get client's request publisher qos settings: ") +
212  rcl_get_error_string().str;
213  rcl_reset_error();
214  throw std::runtime_error(msg);
215  }
216 
217  rclcpp::QoS request_publisher_qos =
219 
220  return request_publisher_qos;
221 }
222 
225 {
226  const rmw_qos_profile_t * qos =
228  if (!qos) {
229  auto msg =
230  std::string("failed to get client's response subscription qos settings: ") +
231  rcl_get_error_string().str;
232  rcl_reset_error();
233  throw std::runtime_error(msg);
234  }
235 
236  rclcpp::QoS response_subscription_qos =
238 
239  return response_subscription_qos;
240 }
241 
242 void
243 ClientBase::set_on_new_response_callback(rcl_event_callback_t callback, const void * user_data)
244 {
246  client_handle_.get(),
247  callback,
248  user_data);
249 
250  if (RCL_RET_OK != ret) {
251  using rclcpp::exceptions::throw_from_rcl_error;
252  throw_from_rcl_error(ret, "failed to set the on new response callback for client");
253  }
254 }
RCLCPP_PUBLIC bool exchange_in_use_by_wait_set_state(bool in_use_state)
Exchange the "in use by wait set" state for this client.
Definition: client.cpp:199
RCLCPP_PUBLIC rclcpp::QoS get_request_publisher_actual_qos() const
Get the actual request publsher QoS settings, after the defaults have been determined.
Definition: client.cpp:205
RCLCPP_PUBLIC std::shared_ptr< rcl_client_t > get_client_handle()
Return the rcl_client_t client handle in a std::shared_ptr.
Definition: client.cpp:97
void set_on_new_response_callback(std::function< void(size_t)> callback)
Set a callback to be called when each new response is received.
Definition: client.hpp:281
RCLCPP_PUBLIC bool take_type_erased_response(void *response_out, rmw_request_id_t &request_header_out)
Take the next response for this client as a type erased pointer.
Definition: client.cpp:76
RCLCPP_PUBLIC const char * get_service_name() const
Return the name of the service.
Definition: client.cpp:91
void clear_on_new_response_callback()
Unset the callback registered for new responses, if any.
Definition: client.hpp:329
RCLCPP_PUBLIC rclcpp::QoS get_response_subscription_actual_qos() const
Get the actual response subscription QoS settings, after the defaults have been determined.
Definition: client.cpp:224
RCLCPP_PUBLIC bool service_is_ready() const
Return if the service is ready.
Definition: client.cpp:109
Encapsulation of Quality of Service settings.
Definition: qos.hpp:111
Thrown when a method is trying to use a node, but it is invalid.
Definition: exceptions.hpp:35
Pure virtual interface class for the NodeBase part of the Node API.
RCL_PUBLIC RCL_WARN_UNUSED const rmw_qos_profile_t * rcl_client_request_publisher_get_actual_qos(const rcl_client_t *client)
Get the actual qos settings of the client's request publisher.
Definition: client.c:314
RCL_PUBLIC RCL_WARN_UNUSED const char * rcl_client_get_service_name(const rcl_client_t *client)
Get the name of the service that this client will request a response from.
Definition: client.c:210
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_client_fini(rcl_client_t *client, rcl_node_t *node)
Finalize a rcl_client_t.
Definition: client.c:168
RCL_PUBLIC RCL_WARN_UNUSED rcl_client_t rcl_get_zero_initialized_client(void)
Return a rcl_client_t struct with members set to NULL.
Definition: client.c:46
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_take_response(const rcl_client_t *client, rmw_request_id_t *request_header, void *ros_response)
backwards compatibility function that takes a rmw_request_id_t only
Definition: client.c:290
struct rcl_client_s rcl_client_t
Structure which encapsulates a ROS Client.
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_client_set_on_new_response_callback(const rcl_client_t *client, rcl_event_callback_t callback, const void *user_data)
Set the on new response callback function for the client.
Definition: client.c:332
RCL_PUBLIC RCL_WARN_UNUSED const rmw_qos_profile_t * rcl_client_response_subscription_get_actual_qos(const rcl_client_t *client)
Get the actual qos settings of the client's response subscription.
Definition: client.c:323
RCL_PUBLIC RCL_WARN_UNUSED bool rcl_context_is_valid(const rcl_context_t *context)
Return true if the given context is currently valid, otherwise false.
Definition: context.c:94
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_service_server_is_available(const rcl_node_t *node, const rcl_client_t *client, bool *is_available)
Check if a service server is available for the given service client.
Definition: graph.c:717
Versions of rosidl_typesupport_cpp::get_message_type_support_handle that handle adapted types.
RCLCPP_PUBLIC Logger get_node_logger(const rcl_node_t *node)
Return a named logger using an rcl_node_t.
Definition: logger.cpp:38
RCLCPP_PUBLIC bool ok(rclcpp::Context::SharedPtr context=nullptr)
Check rclcpp's status.
RCLCPP_PUBLIC Logger get_logger(const std::string &name)
Return a named logger.
Definition: logger.cpp:27
Structure which encapsulates a ROS Client.
Definition: client.h:37
Structure which encapsulates a ROS Node.
Definition: node.h:42
rcl_context_t * context
Context associated with this node.
Definition: node.h:44
static QoSInitialization from_rmw(const rmw_qos_profile_t &rmw_qos)
Create a QoSInitialization from an existing rmw_qos_profile_t, using its history and depth.
Definition: qos.cpp:51
#define RCL_MS_TO_NS
Convenience macro to convert milliseconds to nanoseconds.
Definition: time.h:34
#define RCL_RET_CLIENT_TAKE_FAILED
Failed to take a response from the client return code.
Definition: types.h:78
#define RCL_RET_OK
Success return code.
Definition: types.h:26
#define RCL_RET_NODE_INVALID
Invalid rcl_node_t given return code.
Definition: types.h:56
rmw_ret_t rcl_ret_t
The type that holds an rcl return code.
Definition: types.h:23