16#ifndef EASYFLEET_CORE__DETAIL__ACTION_SERVER_BASE_IMPL_HPP_
17#define EASYFLEET_CORE__DETAIL__ACTION_SERVER_BASE_IMPL_HPP_
30#include "easyfleet_core/detail/namespace_utils.hpp"
31#include "easyfleet_core/detail/param_utils.hpp"
33namespace easyfleet_core
36template<
typename ActionT>
37ActionServerBase<ActionT>::ActionServerBase(
38 rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_base,
39 rclcpp::node_interfaces::NodeClockInterface::SharedPtr node_clock,
40 rclcpp::node_interfaces::NodeLoggingInterface::SharedPtr node_logging,
41 rclcpp::node_interfaces::NodeParametersInterface::SharedPtr node_parameters,
42 rclcpp::node_interfaces::NodeWaitablesInterface::SharedPtr node_waitables,
43 const std::string & action_name,
44 bool default_allow_preemption)
45: node_base_(std::move(node_base)),
46 node_clock_(std::move(node_clock)),
47 node_logging_(std::move(node_logging)),
48 node_parameters_(std::move(node_parameters)),
49 node_waitables_(std::move(node_waitables)),
50 action_name_(action_name),
51 param_name_(detail::sanitize_parameter_name(action_name) +
".allow_preemption")
53 if (!node_base_ || !node_clock_ || !node_logging_ || !node_parameters_ || !node_waitables_) {
54 throw std::invalid_argument(
"ActionServerBase: node must not be null");
56 robot_name_ = detail::robot_label(node_base_->get_namespace());
58 if (!node_parameters_->has_parameter(param_name_)) {
59 rcl_interfaces::msg::ParameterDescriptor descriptor;
60 descriptor.description =
61 "Whether the '" + action_name_ +
62 "' action server may preempt an active goal when a new one is accepted.";
63 node_parameters_->declare_parameter(
64 param_name_, rclcpp::ParameterValue(default_allow_preemption), descriptor);
66 allow_preemption_.store(node_parameters_->get_parameter(param_name_).as_bool());
68 param_cb_handle_ = node_parameters_->add_post_set_parameters_callback(
69 [
this](
const std::vector<rclcpp::Parameter> & params) {
70 this->on_parameters_set(params);
73 server_ = rclcpp_action::create_server<ActionT>(
79 [
this](
const rclcpp_action::GoalUUID & uuid, std::shared_ptr<const Goal> goal) {
80 return this->handle_goal(uuid, goal);
82 [
this](
const GoalHandleSharedPtr goal_handle) {
83 return this->handle_cancel(goal_handle);
85 [
this](
const GoalHandleSharedPtr goal_handle) {
86 this->handle_accepted(goal_handle);
89 worker_ = std::thread(&ActionServerBase::worker_loop,
this);
92template<
typename ActionT>
93ActionServerBase<ActionT>::~ActionServerBase()
95 if (param_cb_handle_) {
96 node_parameters_->remove_post_set_parameters_callback(param_cb_handle_.get());
99 std::lock_guard<std::mutex> lock(mutex_);
100 shutting_down_.store(
true);
103 if (worker_.joinable()) {
108template<
typename ActionT>
114template<
typename ActionT>
117 std::lock_guard<std::mutex> lock(mutex_);
118 return active_handle_ !=
nullptr;
121template<
typename ActionT>
124 return allow_preemption_.load();
127template<
typename ActionT>
131 return rclcpp_action::CancelResponse::ACCEPT;
134template<
typename ActionT>
139template<
typename ActionT>
142 return preempt_requested_.load();
145template<
typename ActionT>
148 return shutting_down_.load();
151template<
typename ActionT>
154 return node_logging_->get_logger();
157template<
typename ActionT>
158rclcpp_action::GoalResponse ActionServerBase<ActionT>::handle_goal(
159 const rclcpp_action::GoalUUID & uuid,
160 std::shared_ptr<const Goal> goal)
163 if (response != rclcpp_action::GoalResponse::ACCEPT_AND_EXECUTE) {
167 std::lock_guard<std::mutex> lock(mutex_);
168 if (active_handle_ && !allow_preemption_.load()) {
169 return rclcpp_action::GoalResponse::REJECT;
171 return rclcpp_action::GoalResponse::ACCEPT_AND_EXECUTE;
174template<
typename ActionT>
175rclcpp_action::CancelResponse ActionServerBase<ActionT>::handle_cancel(
176 const GoalHandleSharedPtr goal_handle)
178 return this->on_cancel_requested(goal_handle);
181template<
typename ActionT>
182void ActionServerBase<ActionT>::handle_accepted(GoalHandleSharedPtr goal_handle)
184 GoalHandleSharedPtr bumped;
185 GoalHandleSharedPtr preempted_active;
187 std::lock_guard<std::mutex> lock(mutex_);
188 if (pending_handle_) {
191 bumped = pending_handle_;
193 pending_handle_ = goal_handle;
194 if (active_handle_) {
195 preempt_requested_.store(
true);
196 preempted_active = active_handle_;
202 node_logging_->get_logger(),
203 "Action '%s': a queued goal was superseded before it could start executing.",
204 action_name_.c_str());
205 bumped->abort(std::make_shared<Result>());
207 if (preempted_active) {
208 this->on_preempted(preempted_active);
213template<
typename ActionT>
214void ActionServerBase<ActionT>::on_parameters_set(
const std::vector<rclcpp::Parameter> & params)
216 for (
const auto & param : params) {
217 if (param.get_name() == param_name_ &&
218 param.get_type() == rclcpp::ParameterType::PARAMETER_BOOL)
220 allow_preemption_.store(param.as_bool());
225template<
typename ActionT>
226void ActionServerBase<ActionT>::worker_loop()
229 GoalHandleSharedPtr goal;
231 std::unique_lock<std::mutex> lock(mutex_);
234 return shutting_down_.load() || pending_handle_ !=
nullptr;
236 if (shutting_down_.load() && !pending_handle_) {
239 goal = pending_handle_;
240 pending_handle_.reset();
241 active_handle_ = goal;
242 preempt_requested_.store(
false);
245 if (goal->is_canceling()) {
247 goal->canceled(std::make_shared<Result>());
250 node_logging_->get_logger(),
251 "Capability '%s' on robot '%s': goal execution started.",
252 action_name_.c_str(), robot_name_.c_str());
258 this->on_execute(goal);
259 }
catch (
const std::exception & e) {
261 node_logging_->get_logger(),
262 "Action '%s': exception thrown from on_execute(): %s",
263 action_name_.c_str(), e.what());
264 if (goal->is_active()) {
265 goal->abort(std::make_shared<Result>());
268 if (goal->is_active()) {
270 node_logging_->get_logger(),
271 "Action '%s': on_execute() returned without settling the goal; aborting it.",
272 action_name_.c_str());
273 goal->abort(std::make_shared<Result>());
277 node_logging_->get_logger(),
278 "Capability '%s' on robot '%s': goal execution finished.",
279 action_name_.c_str(), robot_name_.c_str());
282 std::lock_guard<std::mutex> lock(mutex_);
283 active_handle_.reset();
bool is_shutdown_requested() const noexcept
Whether this object is being destroyed.
Definition action_server_base_impl.hpp:146
const std::string & get_action_name() const noexcept
Fully-qualified name of the action served by this instance.
Definition action_server_base_impl.hpp:109
rclcpp::Logger logger() const
Logger of the node hosting this action server.
Definition action_server_base_impl.hpp:152
virtual rclcpp_action::CancelResponse on_cancel_requested(const GoalHandleSharedPtr goal_handle)
Decide whether a cancel request (via the action's cancel service) should be accepted.
Definition action_server_base_impl.hpp:128
bool is_preempt_requested() const noexcept
Whether a newer goal has been accepted and is waiting to replace the one currently executing.
Definition action_server_base_impl.hpp:140
bool is_preemptable() const noexcept
Current value of the "allow_preemption" parameter for this action.
Definition action_server_base_impl.hpp:122
virtual void on_preempted(const GoalHandleSharedPtr &preempted_goal_handle)
Optional hook invoked (from the accepting thread, not the worker thread) when a new goal is about to ...
Definition action_server_base_impl.hpp:135
std::shared_ptr< GoalHandle > GoalHandleSharedPtr
Shared pointer to a GoalHandle.
Definition action_server_base.hpp:79
virtual rclcpp_action::GoalResponse on_goal_received(const rclcpp_action::GoalUUID &uuid, std::shared_ptr< const Goal > goal)=0
bool is_active() const
Whether a goal is currently executing.
Definition action_server_base_impl.hpp:115