16#ifndef EASYFLEET_CORE__DETAIL__ACTION_CLIENT_IMPL_HPP_
17#define EASYFLEET_CORE__DETAIL__ACTION_CLIENT_IMPL_HPP_
30#include "easyfleet_core/detail/param_utils.hpp"
32namespace easyfleet_core
35template<
typename ActionT>
37 rclcpp::Node & parent_node,
38 const std::string & action_name,
39 std::chrono::milliseconds default_server_timeout)
41 return SharedPtr(
new ActionClient(parent_node, action_name, default_server_timeout));
44template<
typename ActionT>
45ActionClient<ActionT>::ActionClient(
46 rclcpp::Node & parent_node,
47 const std::string & action_name,
48 std::chrono::milliseconds default_server_timeout)
49: action_name_(action_name),
50 default_server_timeout_(default_server_timeout)
52 static std::atomic<uint64_t> instance_counter{0};
53 const std::string node_name =
54 detail::sanitize_identifier(
55 std::string(parent_node.get_name()) +
"_ac_" +
56 detail::sanitize_parameter_name(action_name)) +
57 "_" + std::to_string(instance_counter.fetch_add(1));
59 rclcpp::NodeOptions options;
60 options.start_parameter_services(
false);
61 options.start_parameter_event_publisher(
false);
62 options.use_global_arguments(
false);
64 internal_node_ = std::make_shared<rclcpp::Node>(
65 node_name, parent_node.get_namespace(), options);
67 client_ = rclcpp_action::create_client<ActionT>(internal_node_, action_name_);
69 executor_ = std::make_shared<rclcpp::executors::SingleThreadedExecutor>();
70 executor_->add_node(internal_node_);
71 spin_thread_ = std::thread([
this] {executor_->spin();});
75 while (!executor_->is_spinning()) {
76 std::this_thread::yield();
80template<
typename ActionT>
81ActionClient<ActionT>::~ActionClient()
86 if (spin_thread_.joinable()) {
89 if (executor_ && internal_node_) {
90 executor_->remove_node(internal_node_);
94template<
typename ActionT>
100template<
typename ActionT>
103 return client_->action_server_is_ready();
106template<
typename ActionT>
109 return client_->wait_for_action_server(default_server_timeout_);
112template<
typename ActionT>
115 return client_->wait_for_action_server(timeout);
118template<
typename ActionT>
125 if (!client_->action_server_is_ready()) {
126 if (result_callback) {
127 result_callback(
GoalResult{GoalOutcome::SERVER_UNAVAILABLE, {},
nullptr});
132 typename rclcpp_action::Client<ActionT>::SendGoalOptions send_options;
134 if (feedback_callback) {
135 send_options.feedback_callback =
137 typename ClientGoalHandle::SharedPtr ,
138 const std::shared_ptr<const Feedback> feedback)
140 feedback_callback(feedback);
144 send_options.goal_response_callback =
145 [
this, result_callback, goal_response_callback](
typename ClientGoalHandle::SharedPtr handle)
148 if (goal_response_callback) {
149 goal_response_callback(
false, rclcpp_action::GoalUUID{});
151 if (result_callback) {
152 result_callback(
GoalResult{GoalOutcome::REJECTED, {},
nullptr});
157 std::lock_guard<std::mutex> lock(goals_mutex_);
158 active_goals_[rclcpp_action::to_string(handle->get_goal_id())] = handle;
160 if (goal_response_callback) {
161 goal_response_callback(
true, handle->get_goal_id());
165 send_options.result_callback =
166 [
this, result_callback](
const typename ClientGoalHandle::WrappedResult & wrapped)
169 std::lock_guard<std::mutex> lock(goals_mutex_);
170 active_goals_.erase(rclcpp_action::to_string(wrapped.goal_id));
172 if (result_callback) {
173 result_callback(to_goal_result(wrapped));
177 client_->async_send_goal(goal, send_options);
181template<
typename ActionT>
185 std::chrono::milliseconds timeout)
187 auto promise = std::make_shared<std::promise<GoalResult>>();
188 auto future = promise->get_future();
193 promise->set_value(result);
197 if (timeout.count() <= 0) {
200 if (future.wait_for(timeout) == std::future_status::timeout) {
201 return GoalResult{GoalOutcome::TIMEOUT, {},
nullptr};
206template<
typename ActionT>
209 typename ClientGoalHandle::SharedPtr handle;
211 std::lock_guard<std::mutex> lock(goals_mutex_);
212 auto it = active_goals_.find(rclcpp_action::to_string(goal_id));
213 if (it == active_goals_.end()) {
218 client_->async_cancel_goal(handle);
222template<
typename ActionT>
225 client_->async_cancel_all_goals();
228template<
typename ActionT>
231 std::lock_guard<std::mutex> lock(goals_mutex_);
232 return active_goals_.size();
235template<
typename ActionT>
237 const typename ClientGoalHandle::WrappedResult & wrapped)
240 switch (wrapped.code) {
241 case rclcpp_action::ResultCode::SUCCEEDED:
242 outcome = GoalOutcome::SUCCEEDED;
244 case rclcpp_action::ResultCode::CANCELED:
245 outcome = GoalOutcome::CANCELED;
247 case rclcpp_action::ResultCode::ABORTED:
249 outcome = GoalOutcome::ABORTED;
252 return GoalResult{outcome, wrapped.goal_id, wrapped.result};
bool wait_for_server()
Block until the server is reachable, using the constructor's default_server_timeout.
Definition action_client_impl.hpp:107
bool is_server_ready() const
Whether the action server is currently reachable.
Definition action_client_impl.hpp:101
std::function< void(const GoalResult &)> ResultCallback
Callback invoked once a goal reaches a terminal state.
Definition action_client.hpp:89
const std::string & get_action_name() const noexcept
The action name this client was created for.
Definition action_client_impl.hpp:95
std::function< void(bool accepted, const rclcpp_action::GoalUUID &goal_id)> GoalResponseCallback
Callback invoked once the server has accepted or rejected a goal.
Definition action_client.hpp:91
std::shared_ptr< ActionClient< ActionT > > SharedPtr
Shared pointer to an ActionClient<ActionT>.
Definition action_client.hpp:51
bool send_goal(const Goal &goal, ResultCallback result_callback=nullptr, FeedbackCallback feedback_callback=nullptr, GoalResponseCallback goal_response_callback=nullptr)
Send a goal without blocking.
Definition action_client_impl.hpp:119
std::size_t active_goal_count() const
Number of goals accepted by the server and not yet in a terminal state.
Definition action_client_impl.hpp:229
GoalResult send_goal_and_wait(const Goal &goal, FeedbackCallback feedback_callback=nullptr, std::chrono::milliseconds timeout=std::chrono::milliseconds(0))
Send a goal and block the calling thread until it reaches a terminal state (or timeout elapses,...
Definition action_client_impl.hpp:182
bool cancel_goal(const rclcpp_action::GoalUUID &goal_id)
Request cancellation of a specific in-flight goal.
Definition action_client_impl.hpp:207
std::function< void(std::shared_ptr< const Feedback >)> FeedbackCallback
Callback invoked with feedback as it arrives for an in-flight goal.
Definition action_client.hpp:62
typename ActionT::Goal Goal
Goal type of the wrapped action.
Definition action_client.hpp:53
void cancel_all_goals()
Request cancellation of every goal currently tracked by this client.
Definition action_client_impl.hpp:223
static SharedPtr create(rclcpp::Node &parent_node, const std::string &action_name, std::chrono::milliseconds default_server_timeout=std::chrono::seconds(5))
Constructs a new client for action_name, spinning its own internal node in the background.
Definition action_client_impl.hpp:36
Terminal outcome of a goal, as delivered to a ResultCallback or returned by send_goal_and_wait().
Definition action_client.hpp:79