15 #ifndef RCLCPP__CLIENT_HPP_
16 #define RCLCPP__CLIENT_HPP_
26 #include <unordered_map>
32 #include "rcl/error_handling.h"
33 #include "rcl/event_callback.h"
34 #include "rcl/service_introspection.h"
37 #include "rclcpp/clock.hpp"
38 #include "rclcpp/exceptions.hpp"
39 #include "rclcpp/expand_topic_or_service_name.hpp"
40 #include "rclcpp/function_traits.hpp"
41 #include "rclcpp/logging.hpp"
42 #include "rclcpp/macros.hpp"
43 #include "rclcpp/node_interfaces/node_graph_interface.hpp"
44 #include "rclcpp/qos.hpp"
45 #include "rclcpp/type_support_decl.hpp"
46 #include "rclcpp/visibility_control.hpp"
48 #include "rmw/error_handling.h"
56 template<
typename FutureT>
63 : future(std::move(impl)), request_id(req_id)
67 operator FutureT &() {
return this->future;}
72 auto get() {
return this->future.get();}
74 bool valid() const noexcept {
return this->future.valid();}
76 void wait()
const {
return this->future.wait();}
78 template<
class Rep,
class Period>
80 const std::chrono::duration<Rep, Period> & timeout_duration)
const
82 return this->future.wait_for(timeout_duration);
85 template<
class Clock,
class Duration>
87 const std::chrono::time_point<Clock, Duration> & timeout_time)
const
89 return this->future.wait_until(timeout_time);
107 template<
typename PendingRequestsT,
typename AllocatorT = std::allocator<
int64_t>>
109 prune_requests_older_than_impl(
110 PendingRequestsT & pending_requests,
111 std::mutex & pending_requests_mutex,
112 std::chrono::time_point<std::chrono::system_clock> time_point,
113 std::vector<int64_t, AllocatorT> * pruned_requests =
nullptr)
115 std::lock_guard guard(pending_requests_mutex);
116 auto old_size = pending_requests.size();
117 for (
auto it = pending_requests.begin(), last = pending_requests.end(); it != last; ) {
118 if (it->second.first < time_point) {
119 if (pruned_requests) {
120 pruned_requests->push_back(it->first);
122 it = pending_requests.erase(it);
127 return old_size - pending_requests.size();
131 namespace node_interfaces
133 class NodeBaseInterface;
139 RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(
ClientBase)
144 const rclcpp::node_interfaces::NodeGraphInterface::SharedPtr & node_graph);
182 std::shared_ptr<rcl_client_t>
191 std::shared_ptr<const rcl_client_t>
207 template<
typename RepT =
int64_t,
typename RatioT = std::milli>
210 std::chrono::duration<RepT, RatioT> timeout = std::chrono::duration<RepT, RatioT>(-1))
212 return wait_for_service_nanoseconds(
213 std::chrono::duration_cast<std::chrono::nanoseconds>(timeout)
217 virtual std::shared_ptr<void> create_response() = 0;
218 virtual std::shared_ptr<rmw_request_id_t> create_request_header() = 0;
219 virtual void handle_response(
220 const std::shared_ptr<rmw_request_id_t> & request_header,
221 const std::shared_ptr<void> & response) = 0;
309 wait_for_service_nanoseconds(std::chrono::nanoseconds timeout);
313 get_rcl_node_handle();
317 get_rcl_node_handle()
const;
323 rclcpp::node_interfaces::NodeGraphInterface::WeakPtr node_graph_;
324 std::shared_ptr<rcl_node_t> node_handle_;
325 std::shared_ptr<rclcpp::Context> context_;
328 std::recursive_mutex callback_mutex_;
333 std::function<void(
size_t)> on_new_response_callback_{
nullptr};
335 std::shared_ptr<rcl_client_t> client_handle_;
337 std::atomic<bool> in_use_by_wait_set_{
false};
340 template<
typename ServiceT>
344 using Request =
typename ServiceT::Request;
345 using Response =
typename ServiceT::Response;
347 using SharedRequest =
typename ServiceT::Request::SharedPtr;
348 using SharedResponse =
typename ServiceT::Response::SharedPtr;
350 using Promise = std::promise<SharedResponse>;
351 using PromiseWithRequest = std::promise<std::pair<SharedRequest, SharedResponse>>;
353 using SharedPromise = std::shared_ptr<Promise>;
354 using SharedPromiseWithRequest = std::shared_ptr<PromiseWithRequest>;
356 using Future = std::future<SharedResponse>;
357 using SharedFuture = std::shared_future<SharedResponse>;
358 using SharedFutureWithRequest = std::shared_future<std::pair<SharedRequest, SharedResponse>>;
360 using CallbackType = std::function<void (SharedFuture)>;
361 using CallbackWithRequestType = std::function<void (SharedFutureWithRequest)>;
363 RCLCPP_SMART_PTR_DEFINITIONS(
Client)
381 SharedFuture
share() noexcept {
return this->future.share();}
410 std::shared_future<std::pair<SharedRequest, SharedResponse>>
427 const rclcpp::node_interfaces::NodeGraphInterface::SharedPtr & node_graph,
428 const std::string & service_name,
431 srv_type_support_handle_(rosidl_typesupport_cpp::get_service_type_support_handle<ServiceT>())
435 this->get_rcl_node_handle(),
436 srv_type_support_handle_,
437 service_name.c_str(),
441 auto rcl_node_handle = this->get_rcl_node_handle();
450 rclcpp::exceptions::throw_from_rcl_error(ret,
"could not create client");
472 take_response(
typename ServiceT::Response & response_out, rmw_request_id_t & request_header_out)
481 std::shared_ptr<void>
484 return std::shared_ptr<void>(
new typename ServiceT::Response());
491 std::shared_ptr<rmw_request_id_t>
496 return std::shared_ptr<rmw_request_id_t>(
new rmw_request_id_t);
506 const std::shared_ptr<rmw_request_id_t> & request_header,
507 const std::shared_ptr<void> & response)
override
509 std::optional<CallbackInfoVariant>
510 optional_pending_request = this->get_and_erase_pending_request(request_header->sequence_number);
511 if (!optional_pending_request) {
514 auto & value = *optional_pending_request;
515 auto typed_response = std::static_pointer_cast<typename ServiceT::Response>(
517 if (std::holds_alternative<Promise>(value)) {
518 auto & promise = std::get<Promise>(value);
519 promise.set_value(std::move(typed_response));
520 }
else if (std::holds_alternative<CallbackTypeValueVariant>(value)) {
521 auto & inner = std::get<CallbackTypeValueVariant>(value);
522 const auto & callback = std::get<CallbackType>(inner);
523 auto & promise = std::get<Promise>(inner);
524 auto & future = std::get<SharedFuture>(inner);
525 promise.set_value(std::move(typed_response));
526 callback(std::move(future));
527 }
else if (std::holds_alternative<CallbackWithRequestTypeValueVariant>(value)) {
528 auto & inner = std::get<CallbackWithRequestTypeValueVariant>(value);
529 const auto & callback = std::get<CallbackWithRequestType>(inner);
530 auto & promise = std::get<PromiseWithRequest>(inner);
531 auto & future = std::get<SharedFutureWithRequest>(inner);
532 auto & request = std::get<SharedRequest>(inner);
533 promise.set_value(std::make_pair(std::move(request), std::move(typed_response)));
534 callback(std::move(future));
570 auto future = promise.get_future();
571 auto req_id = async_send_request_impl(
594 typename std::enable_if<
601 SharedFutureAndRequestId
605 auto shared_future = promise.get_future().share();
606 auto req_id = async_send_request_impl(
609 CallbackType{std::forward<CallbackT>(cb)},
611 std::move(promise)));
625 typename std::enable_if<
628 CallbackWithRequestType
632 SharedFutureWithRequestAndRequestId
635 PromiseWithRequest promise;
636 auto shared_future = promise.get_future().share();
637 auto req_id = async_send_request_impl(
640 CallbackWithRequestType{std::forward<CallbackT>(cb)},
643 std::move(promise)));
661 std::lock_guard guard(pending_requests_mutex_);
662 return pending_requests_.erase(request_id) != 0u;
708 std::lock_guard guard(pending_requests_mutex_);
709 auto ret = pending_requests_.size();
710 pending_requests_.clear();
721 template<
typename AllocatorT = std::allocator<
int64_t>>
724 std::chrono::time_point<std::chrono::system_clock> time_point,
725 std::vector<int64_t, AllocatorT> * pruned_requests =
nullptr)
727 return detail::prune_requests_older_than_impl(
729 pending_requests_mutex_,
745 const Clock::SharedPtr & clock,
const QoS & qos_service_event_pub,
746 rcl_service_introspection_state_t introspection_state)
752 client_handle_.get(),
754 clock->get_clock_handle(),
755 srv_type_support_handle_,
757 introspection_state);
760 rclcpp::exceptions::throw_from_rcl_error(ret,
"failed to configure client introspection");
765 using CallbackTypeValueVariant = std::tuple<CallbackType, SharedFuture, Promise>;
766 using CallbackWithRequestTypeValueVariant = std::tuple<
767 CallbackWithRequestType, SharedRequest, SharedFutureWithRequest, PromiseWithRequest>;
769 using CallbackInfoVariant = std::variant<
770 std::promise<SharedResponse>,
771 CallbackTypeValueVariant,
772 CallbackWithRequestTypeValueVariant>;
775 async_send_request_impl(
const Request & request, CallbackInfoVariant value)
777 int64_t sequence_number;
778 std::lock_guard<std::mutex> lock(pending_requests_mutex_);
781 rclcpp::exceptions::throw_from_rcl_error(ret,
"failed to send request");
783 pending_requests_.try_emplace(
785 std::make_pair(std::chrono::system_clock::now(), std::move(value)));
786 return sequence_number;
789 std::optional<CallbackInfoVariant>
790 get_and_erase_pending_request(int64_t request_number)
792 std::unique_lock<std::mutex> lock(pending_requests_mutex_);
793 auto it = this->pending_requests_.find(request_number);
794 if (it == this->pending_requests_.end()) {
795 RCUTILS_LOG_DEBUG_NAMED(
797 "Received invalid sequence number. Ignoring...");
800 std::optional<CallbackInfoVariant> value = std::move(it->second.second);
801 this->pending_requests_.erase(request_number);
805 RCLCPP_DISABLE_COPY(
Client)
810 std::chrono::time_point<std::chrono::system_clock>,
811 CallbackInfoVariant>>
813 std::mutex pending_requests_mutex_;
816 const rosidl_service_type_support_t * srv_type_support_handle_;
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.
RCLCPP_PUBLIC rclcpp::QoS get_request_publisher_actual_qos() const
Get the actual request publsher QoS settings, after the defaults have been determined.
RCLCPP_PUBLIC std::shared_ptr< rcl_client_t > get_client_handle()
Return the rcl_client_t client handle in a std::shared_ptr.
RCLCPP_PUBLIC void set_on_new_response_callback(const std::function< void(size_t)> &callback)
Set a callback to be called when each new response is received.
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.
bool wait_for_service(std::chrono::duration< RepT, RatioT > timeout=std::chrono::duration< RepT, RatioT >(-1))
Wait for a service to be ready.
RCLCPP_PUBLIC void clear_on_new_response_callback()
Unset the callback registered for new responses, if any.
RCLCPP_PUBLIC const char * get_service_name() const
Return the name of the service.
RCLCPP_PUBLIC rclcpp::QoS get_response_subscription_actual_qos() const
Get the actual response subscription QoS settings, after the defaults have been determined.
RCLCPP_PUBLIC bool service_is_ready() const
Return if the service is ready.
bool take_response(typename ServiceT::Response &response_out, rmw_request_id_t &request_header_out)
Take the next response for this client.
std::shared_ptr< rmw_request_id_t > create_request_header() override
Create a shared pointer with a rmw_request_id_t.
bool remove_pending_request(int64_t request_id)
Cleanup a pending request.
std::shared_ptr< void > create_response() override
Create a shared pointer with the response type.
size_t prune_requests_older_than(std::chrono::time_point< std::chrono::system_clock > time_point, std::vector< int64_t, AllocatorT > *pruned_requests=nullptr)
Clean all pending requests older than a time_point.
void configure_introspection(const Clock::SharedPtr &clock, const QoS &qos_service_event_pub, rcl_service_introspection_state_t introspection_state)
Configure client introspection.
FutureAndRequestId async_send_request(const SharedRequest &request)
Send a request to the service server.
Client(rclcpp::node_interfaces::NodeBaseInterface *node_base, const rclcpp::node_interfaces::NodeGraphInterface::SharedPtr &node_graph, const std::string &service_name, rcl_client_options_t &client_options)
Default constructor.
size_t prune_pending_requests()
Clean all pending requests.
bool remove_pending_request(const SharedFutureAndRequestId &future)
Cleanup a pending request.
SharedFutureWithRequestAndRequestId async_send_request(const SharedRequest &request, CallbackT &&cb)
Send a request to the service server and schedule a callback in the executor.
bool remove_pending_request(const SharedFutureWithRequestAndRequestId &future)
Cleanup a pending request.
void handle_response(const std::shared_ptr< rmw_request_id_t > &request_header, const std::shared_ptr< void > &response) override
Handle a server response.
SharedFutureAndRequestId async_send_request(const SharedRequest &request, CallbackT &&cb)
Send a request to the service server and schedule a callback in the executor.
bool remove_pending_request(const FutureAndRequestId &future)
Cleanup a pending request.
Encapsulation of Quality of Service settings.
rmw_qos_profile_t & get_rmw_qos_profile()
Return the rmw qos profile.
Pure virtual interface class for the NodeBase part of the Node API.
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_client_configure_service_introspection(rcl_client_t *client, rcl_node_t *node, rcl_clock_t *clock, const rosidl_service_type_support_t *type_support, const rcl_publisher_options_t publisher_options, rcl_service_introspection_state_t introspection_state)
Configures service introspection features for the client.
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_client_init(rcl_client_t *client, const rcl_node_t *node, const rosidl_service_type_support_t *type_support, const char *service_name, const rcl_client_options_t *options)
Initialize a rcl client.
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_send_request(const rcl_client_t *client, const void *ros_request, int64_t *sequence_number)
Send a ROS request using a client.
Versions of rosidl_typesupport_cpp::get_message_type_support_handle that handle adapted types.
RCLCPP_PUBLIC std::string expand_topic_or_service_name(const std::string &name, const std::string &node_name, const std::string &namespace_, bool is_service=false)
Expand a topic or service name and throw if it is not valid.
RCL_PUBLIC RCL_WARN_UNUSED const char * rcl_node_get_name(const rcl_node_t *node)
Return the name of the node.
RCL_PUBLIC RCL_WARN_UNUSED const char * rcl_node_get_namespace(const rcl_node_t *node)
Return the namespace of the node.
RCL_PUBLIC RCL_WARN_UNUSED rcl_publisher_options_t rcl_publisher_get_default_options(void)
Return the default publisher options in a rcl_publisher_options_t.
Options available for a rcl_client_t.
Structure which encapsulates a ROS Node.
Options available for a rcl publisher.
rmw_qos_profile_t qos
Middleware quality of service settings for the publisher.
A convenient Client::Future and request id pair.
SharedFuture share() noexcept
See std::future::share().
A convenient Client::SharedFuture and request id pair.
A convenient Client::SharedFutureWithRequest and request id pair.
std::future_status wait_for(const std::chrono::duration< Rep, Period > &timeout_duration) const
See std::future::wait_for().
std::future_status wait_until(const std::chrono::time_point< Clock, Duration > &timeout_time) const
See std::future::wait_until().
FutureAndRequestId & operator=(FutureAndRequestId &&other) noexcept=default
Move assignment.
FutureAndRequestId & operator=(const FutureAndRequestId &other)=delete
Deleted copy assignment, each instance is a unique owner of the future.
FutureAndRequestId(const FutureAndRequestId &other)=delete
Deleted copy constructor, each instance is a unique owner of the future.
void wait() const
See std::future::wait().
FutureAndRequestId(FutureAndRequestId &&other) noexcept=default
Move constructor.
~FutureAndRequestId()=default
Destructor.
auto get()
See std::future::get().
bool valid() const noexcept
See std::future::valid().
#define RCL_RET_SERVICE_NAME_INVALID
Service name (same as topic name) does not pass validation.
#define RCL_RET_OK
Success return code.
rmw_ret_t rcl_ret_t
The type that holds an rcl return code.