15 #ifndef NAV2_ROS_COMMON__SIMPLE_ACTION_SERVER_HPP_
16 #define NAV2_ROS_COMMON__SIMPLE_ACTION_SERVER_HPP_
24 #include <type_traits>
26 #include "rclcpp/callback_group.hpp"
27 #include "rclcpp/executors/single_threaded_executor.hpp"
28 #include "rclcpp/logging.hpp"
29 #include "rclcpp/node_interfaces/node_base_interface.hpp"
30 #include "rclcpp/node_interfaces/node_clock_interface.hpp"
31 #include "rclcpp/node_interfaces/node_logging_interface.hpp"
32 #include "rclcpp/node_interfaces/node_parameters_interface.hpp"
33 #include "rclcpp/node_interfaces/node_waitables_interface.hpp"
34 #include "rclcpp/utilities.hpp"
35 #include "rclcpp_action/rclcpp_action.hpp"
36 #include "nav2_ros_common/node_thread.hpp"
37 #include "nav2_ros_common/node_utils.hpp"
46 template<
typename ActionT>
50 using SharedPtr = std::shared_ptr<nav2::SimpleActionServer<ActionT>>;
51 using UniquePtr = std::unique_ptr<nav2::SimpleActionServer<ActionT>>;
56 typedef std::function<void ()> ExecuteCallback;
60 typedef std::function<bool (std::shared_ptr<const typename ActionT::Goal>)> GoalReceivedCallback;
67 typedef std::function<void ()> CompletionCallback;
79 template<
typename NodeT>
82 const std::string & action_name,
83 ExecuteCallback execute_callback,
84 GoalReceivedCallback goal_received_callback =
nullptr,
85 CompletionCallback completion_callback =
nullptr,
86 std::chrono::milliseconds server_timeout = std::chrono::milliseconds(500),
87 bool spin_thread =
false,
88 const bool realtime =
false)
90 node->get_node_base_interface(),
91 node->get_node_clock_interface(),
92 node->get_node_logging_interface(),
93 node->get_node_waitables_interface(),
94 node->get_node_parameters_interface(),
95 action_name, execute_callback, goal_received_callback, completion_callback,
96 server_timeout, spin_thread, realtime)
110 rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_base_interface,
111 rclcpp::node_interfaces::NodeClockInterface::SharedPtr node_clock_interface,
112 rclcpp::node_interfaces::NodeLoggingInterface::SharedPtr node_logging_interface,
113 rclcpp::node_interfaces::NodeWaitablesInterface::SharedPtr node_waitables_interface,
114 rclcpp::node_interfaces::NodeParametersInterface::SharedPtr node_parameters_interface,
115 const std::string & action_name,
116 ExecuteCallback execute_callback,
117 GoalReceivedCallback goal_received_callback =
nullptr,
118 CompletionCallback completion_callback =
nullptr,
119 std::chrono::milliseconds server_timeout = std::chrono::milliseconds(500),
120 bool spin_thread =
false,
121 const bool realtime =
false)
122 : node_base_interface_(node_base_interface),
123 node_clock_interface_(node_clock_interface),
124 node_logging_interface_(node_logging_interface),
125 node_waitables_interface_(node_waitables_interface),
126 node_parameters_interface_(node_parameters_interface),
127 action_name_(action_name),
128 execute_callback_(execute_callback),
129 goal_received_callback_(goal_received_callback),
130 completion_callback_(completion_callback),
131 server_timeout_(server_timeout),
132 spin_thread_(spin_thread)
134 using namespace std::placeholders;
135 use_realtime_prioritization_ = realtime;
137 callback_group_ = node_base_interface->create_callback_group(
138 rclcpp::CallbackGroupType::MutuallyExclusive,
false);
140 action_server_ = rclcpp_action::create_server<ActionT>(
141 node_base_interface_,
142 node_clock_interface_,
143 node_logging_interface_,
144 node_waitables_interface_,
149 rcl_action_server_get_default_options(),
152 nav2::setIntrospectionMode(
153 this->action_server_,
154 node_parameters_interface_, node_clock_interface_->get_clock());
157 executor_ = std::make_shared<rclcpp::executors::SingleThreadedExecutor>();
158 executor_->add_callback_group(callback_group_, node_base_interface_);
159 executor_thread_ = std::make_unique<nav2::NodeThread>(executor_);
171 const rclcpp_action::GoalUUID & ,
172 std::shared_ptr<const typename ActionT::Goal> goal)
174 std::lock_guard<std::recursive_mutex> lock(update_mutex_);
176 if (!server_active_) {
178 node_logging_interface_->get_logger(),
179 "Action server is inactive. Rejecting the goal.");
180 return rclcpp_action::GoalResponse::REJECT;
183 if (goal_received_callback_ && !goal_received_callback_(goal)) {
184 debug_msg(
"Goal received callback rejected goal");
185 return rclcpp_action::GoalResponse::REJECT;
188 debug_msg(
"Received request for goal acceptance");
189 return rclcpp_action::GoalResponse::ACCEPT_AND_EXECUTE;
199 const std::shared_ptr<rclcpp_action::ServerGoalHandle<ActionT>> handle)
201 std::lock_guard<std::recursive_mutex> lock(update_mutex_);
203 if (!handle->is_active()) {
205 "Received request for goal cancellation,"
206 "but the handle is inactive, so reject the request");
207 return rclcpp_action::CancelResponse::REJECT;
210 debug_msg(
"Received request for goal cancellation");
211 return rclcpp_action::CancelResponse::ACCEPT;
219 if (use_realtime_prioritization_) {
220 nav2::setSoftRealTimePriority();
221 debug_msg(
"Soft realtime prioritization successfully set!");
229 void handle_accepted(
const std::shared_ptr<rclcpp_action::ServerGoalHandle<ActionT>> handle)
231 std::lock_guard<std::recursive_mutex> lock(update_mutex_);
235 debug_msg(
"An older goal is active, moving the new goal to a pending slot.");
239 "The pending slot is occupied."
240 " The previous pending goal will be terminated and replaced.");
243 pending_handle_ = handle;
244 preempt_requested_ =
true;
248 error_msg(
"Forgot to handle a preemption. Terminating the pending goal.");
250 preempt_requested_ =
false;
253 current_handle_ = handle;
256 debug_msg(
"Executing goal asynchronously.");
257 execution_future_ = std::async(
258 std::launch::async, [
this]() {
270 while (rclcpp::ok() && !stop_execution_ &&
is_active(current_handle_)) {
274 }
catch (std::exception & ex) {
276 node_logging_interface_->get_logger(),
277 "Action server failed while executing action callback: \"%s\"", ex.what());
279 if (completion_callback_) {completion_callback_();}
283 debug_msg(
"Blocking processing of new goal handles.");
284 std::lock_guard<std::recursive_mutex> lock(update_mutex_);
286 if (stop_execution_) {
287 warn_msg(
"Stopping the thread per request.");
289 if (completion_callback_) {completion_callback_();}
294 warn_msg(
"Current goal was not completed successfully.");
296 if (completion_callback_) {completion_callback_();}
300 debug_msg(
"Executing a pending handle on the existing thread.");
303 debug_msg(
"Done processing available goals.");
315 std::lock_guard<std::recursive_mutex> lock(update_mutex_);
316 server_active_ =
true;
317 stop_execution_ =
false;
328 std::lock_guard<std::recursive_mutex> lock(update_mutex_);
329 server_active_ =
false;
330 stop_execution_ =
true;
333 if (!execution_future_.valid()) {
339 "Requested to deactivate server but goal is still executing."
340 " Should check if action server is running before deactivating.");
343 using namespace std::chrono;
344 auto start_time = steady_clock::now();
345 while (execution_future_.wait_for(milliseconds(100)) != std::future_status::ready) {
346 info_msg(
"Waiting for async process to finish.");
347 if (steady_clock::now() - start_time >= server_timeout_) {
349 if (completion_callback_) {completion_callback_();}
350 error_msg(
"Action callback is still running and missed deadline to stop");
363 return execution_future_.valid() &&
364 (execution_future_.wait_for(std::chrono::milliseconds(0)) ==
365 std::future_status::timeout);
374 std::lock_guard<std::recursive_mutex> lock(update_mutex_);
375 return server_active_;
384 std::lock_guard<std::recursive_mutex> lock(update_mutex_);
385 return preempt_requested_;
394 std::lock_guard<std::recursive_mutex> lock(update_mutex_);
396 if (!pending_handle_ || !pending_handle_->is_active()) {
397 error_msg(
"Attempting to get pending goal when not available");
398 return std::shared_ptr<const typename ActionT::Goal>();
401 if (
is_active(current_handle_) && current_handle_ != pending_handle_) {
402 debug_msg(
"Cancelling the previous goal");
406 current_handle_ = pending_handle_;
407 pending_handle_.reset();
408 preempt_requested_ =
false;
412 return current_handle_->get_goal();
420 std::lock_guard<std::recursive_mutex> lock(update_mutex_);
422 if (!pending_handle_ || !pending_handle_->is_active()) {
423 error_msg(
"Attempting to terminate pending goal when not available");
428 preempt_requested_ =
false;
439 std::lock_guard<std::recursive_mutex> lock(update_mutex_);
442 error_msg(
"A goal is not available or has reached a final state");
443 return std::shared_ptr<const typename ActionT::Goal>();
446 return current_handle_->get_goal();
449 const rclcpp_action::GoalUUID get_current_goal_id()
const
451 std::lock_guard<std::recursive_mutex> lock(update_mutex_);
454 error_msg(
"A goal is not available or has reached a final state");
455 return rclcpp_action::GoalUUID();
458 return current_handle_->get_goal_id();
467 std::lock_guard<std::recursive_mutex> lock(update_mutex_);
469 if (!pending_handle_ || !pending_handle_->is_active()) {
470 error_msg(
"Attempting to get pending goal when not available");
471 return std::shared_ptr<const typename ActionT::Goal>();
474 return pending_handle_->get_goal();
483 std::lock_guard<std::recursive_mutex> lock(update_mutex_);
487 if (current_handle_ ==
nullptr) {
488 error_msg(
"Checking for cancel but current goal is not available");
492 if (pending_handle_ !=
nullptr) {
493 return pending_handle_->is_canceling();
496 return current_handle_->is_canceling();
504 typename std::shared_ptr<typename ActionT::Result> result =
505 std::make_shared<typename ActionT::Result>())
507 std::lock_guard<std::recursive_mutex> lock(update_mutex_);
510 preempt_requested_ =
false;
518 typename std::shared_ptr<typename ActionT::Result> result =
519 std::make_shared<typename ActionT::Result>())
521 std::lock_guard<std::recursive_mutex> lock(update_mutex_);
530 typename std::shared_ptr<typename ActionT::Result> result =
531 std::make_shared<typename ActionT::Result>())
533 std::lock_guard<std::recursive_mutex> lock(update_mutex_);
536 debug_msg(
"Setting succeed on current goal.");
537 current_handle_->succeed(result);
538 current_handle_.reset();
549 error_msg(
"Trying to publish feedback when the current goal handle is not active");
553 current_handle_->publish_feedback(feedback);
558 rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_base_interface_;
559 rclcpp::node_interfaces::NodeClockInterface::SharedPtr node_clock_interface_;
560 rclcpp::node_interfaces::NodeLoggingInterface::SharedPtr node_logging_interface_;
561 rclcpp::node_interfaces::NodeWaitablesInterface::SharedPtr node_waitables_interface_;
562 rclcpp::node_interfaces::NodeParametersInterface::SharedPtr node_parameters_interface_;
563 std::string action_name_;
565 ExecuteCallback execute_callback_;
566 GoalReceivedCallback goal_received_callback_;
567 CompletionCallback completion_callback_;
568 std::future<void> execution_future_;
569 bool stop_execution_{
false};
570 bool use_realtime_prioritization_{
false};
572 mutable std::recursive_mutex update_mutex_;
573 bool server_active_{
false};
574 bool preempt_requested_{
false};
575 std::chrono::milliseconds server_timeout_;
577 std::shared_ptr<rclcpp_action::ServerGoalHandle<ActionT>> current_handle_;
578 std::shared_ptr<rclcpp_action::ServerGoalHandle<ActionT>> pending_handle_;
580 typename rclcpp_action::Server<ActionT>::SharedPtr action_server_;
582 rclcpp::CallbackGroup::SharedPtr callback_group_{
nullptr};
583 rclcpp::executors::SingleThreadedExecutor::SharedPtr executor_;
584 std::unique_ptr<nav2::NodeThread> executor_thread_;
591 return std::make_shared<typename ActionT::Result>();
600 const std::shared_ptr<rclcpp_action::ServerGoalHandle<ActionT>> handle)
const
602 return handle !=
nullptr && handle->is_active();
611 template<
typename T,
typename =
void>
614 struct has_error_msg<T, std::void_t<decltype(T::error_msg)>>: std::true_type {};
615 template<
typename T,
typename =
void>
618 struct has_error_code<T, std::void_t<decltype(T::error_code)>>: std::true_type {};
621 void log_error_details_if_available(
const T & result)
627 "Aborting handle. error_code:" + std::to_string(result->error_code) +
628 ", error_msg:'" + result->error_msg +
"'.");
629 }
else if constexpr (has_error_code<typename ActionT::Result>::value) {
630 warn_msg(
"Aborting handle. error_code:" + std::to_string(result->error_code) +
".");
642 std::shared_ptr<rclcpp_action::ServerGoalHandle<ActionT>> & handle,
643 typename std::shared_ptr<typename ActionT::Result> result =
644 std::make_shared<typename ActionT::Result>())
646 std::lock_guard<std::recursive_mutex> lock(update_mutex_);
649 if (handle->is_canceling()) {
650 info_msg(
"Client requested to cancel the goal. Cancelling.");
651 handle->canceled(result);
653 log_error_details_if_available(result);
654 handle->abort(result);
666 node_logging_interface_->get_logger(),
667 "[%s] [ActionServer] %s", action_name_.c_str(), msg.c_str());
676 node_logging_interface_->get_logger(),
677 "[%s] [ActionServer] %s", action_name_.c_str(), msg.c_str());
686 node_logging_interface_->get_logger(),
687 "[%s] [ActionServer] %s", action_name_.c_str(), msg.c_str());
696 node_logging_interface_->get_logger(),
697 "[%s] [ActionServer] %s", action_name_.c_str(), msg.c_str());
An action server wrapper to make applications simpler using Actions.
void terminate_pending_goal()
Terminate pending goals.
constexpr bool is_active(const std::shared_ptr< rclcpp_action::ServerGoalHandle< ActionT >> handle) const
Whether a given goal handle is currently active.
constexpr auto empty_result() const
Generate an empty result object for an action type.
void publish_feedback(typename std::shared_ptr< typename ActionT::Feedback > feedback)
Publish feedback to the action server clients.
void setSoftRealTimePriority()
Sets thread priority level.
void warn_msg(const std::string &msg) const
Warn logging.
const std::shared_ptr< const typename ActionT::Goal > get_pending_goal() const
Get the pending goal object.
void debug_msg(const std::string &msg) const
Debug logging.
SimpleActionServer(NodeT node, const std::string &action_name, ExecuteCallback execute_callback, GoalReceivedCallback goal_received_callback=nullptr, CompletionCallback completion_callback=nullptr, std::chrono::milliseconds server_timeout=std::chrono::milliseconds(500), bool spin_thread=false, const bool realtime=false)
An constructor for SimpleActionServer.
void info_msg(const std::string &msg) const
Info logging.
void terminate(std::shared_ptr< rclcpp_action::ServerGoalHandle< ActionT >> &handle, typename std::shared_ptr< typename ActionT::Result > result=std::make_shared< typename ActionT::Result >())
Terminate a particular action with a result.
void terminate_current(typename std::shared_ptr< typename ActionT::Result > result=std::make_shared< typename ActionT::Result >())
Terminate the active action.
SimpleActionServer(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_base_interface, rclcpp::node_interfaces::NodeClockInterface::SharedPtr node_clock_interface, rclcpp::node_interfaces::NodeLoggingInterface::SharedPtr node_logging_interface, rclcpp::node_interfaces::NodeWaitablesInterface::SharedPtr node_waitables_interface, rclcpp::node_interfaces::NodeParametersInterface::SharedPtr node_parameters_interface, const std::string &action_name, ExecuteCallback execute_callback, GoalReceivedCallback goal_received_callback=nullptr, CompletionCallback completion_callback=nullptr, std::chrono::milliseconds server_timeout=std::chrono::milliseconds(500), bool spin_thread=false, const bool realtime=false)
An constructor for SimpleActionServer.
void work()
Computed background work and processes stop requests.
const std::shared_ptr< const typename ActionT::Goal > get_current_goal() const
Get the current goal object.
void activate()
Active action server.
void handle_accepted(const std::shared_ptr< rclcpp_action::ServerGoalHandle< ActionT >> handle)
Handles accepted goals and adds to preempted queue to switch to.
bool is_cancel_requested() const
Whether or not a cancel command has come in.
void deactivate()
Deactivate action server.
void terminate_all(typename std::shared_ptr< typename ActionT::Result > result=std::make_shared< typename ActionT::Result >())
Terminate all pending and active actions.
rclcpp_action::CancelResponse handle_cancel(const std::shared_ptr< rclcpp_action::ServerGoalHandle< ActionT >> handle)
Accepts cancellation requests of action server.
bool is_preempt_requested() const
Whether the action server has been asked to be preempted with a new goal.
void error_msg(const std::string &msg) const
Error logging.
bool is_running()
Whether the action server is munching on a goal.
void succeeded_current(typename std::shared_ptr< typename ActionT::Result > result=std::make_shared< typename ActionT::Result >())
Return success of the active action.
rclcpp_action::GoalResponse handle_goal(const rclcpp_action::GoalUUID &, std::shared_ptr< const typename ActionT::Goal > goal)
handle the goal requested: accept or reject. This implementation always accepts when the server is ac...
bool is_server_active()
Whether the action server is active or not.
const std::shared_ptr< const typename ActionT::Goal > accept_pending_goal()
Accept pending goals.
SFINAE (Substitution Failure Is Not An Error) to check for existence of error_code and error_msg in A...