AutoAPMS
Resilient Robot Mission Management
Loading...
Searching...
No Matches
action_wrapper.hpp
1// Copyright 2024 Robin Müller
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// https://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#pragma once
16
17#include <chrono>
18
19#include "auto_apms_util/action_context.hpp"
20// #include "auto_apms_util/action_wrapper_params.hpp" // TODO Uncomment if generate_parameter_library 0.4.0 is
21// available
22#include "action_wrapper_params.hpp" // TODO Remove if generate_parameter_library 0.4.0 is available
23#include "auto_apms_util/logging.hpp"
24#include "rclcpp/rclcpp.hpp"
25#include "rclcpp_action/rclcpp_action.hpp"
26
27namespace auto_apms_util
28{
29
34enum class ActionStatus : uint8_t
35{
36 RUNNING,
37 SUCCESS,
38 FAILURE
39};
40
41extern const std::string ACTION_WRAPPER_PARAM_NAME_LOOP_RATE;
42extern const std::string ACTION_WRAPPER_PARAM_NAME_FEEDBACK_RATE;
43
56template <typename ActionT>
58{
59public:
60 using Params = action_wrapper_params::Params;
61 using ParamListener = action_wrapper_params::ParamListener;
62 using Status = ActionStatus;
63 using ActionContextType = ActionContext<ActionT>;
64 using Goal = typename ActionContextType::Goal;
65 using Feedback = typename ActionContextType::Feedback;
66 using Result = typename ActionContextType::Result;
67 using GoalHandle = typename ActionContextType::GoalHandle;
68
75 explicit ActionWrapper(
76 const std::string & action_name, rclcpp::Node::SharedPtr node_ptr,
77 std::shared_ptr<ActionContextType> action_context_ptr);
78
84 explicit ActionWrapper(const std::string & action_name, rclcpp::Node::SharedPtr node_ptr);
85
91 explicit ActionWrapper(const std::string & action_name, const rclcpp::NodeOptions & options);
92
93private:
97
105 virtual bool onGoalRequest(std::shared_ptr<const Goal> goal_ptr);
106
114 virtual void setInitialResult(std::shared_ptr<const Goal> goal_ptr, std::shared_ptr<Result> result_ptr);
115
124 virtual bool onCancelRequest(std::shared_ptr<const Goal> goal_ptr, std::shared_ptr<Result> result_ptr);
125
137 virtual Status cancelGoal(std::shared_ptr<const Goal> goal_ptr, std::shared_ptr<Result> result_ptr);
138
149 virtual Status executeGoal(
150 std::shared_ptr<const Goal> goal_ptr, std::shared_ptr<Feedback> feedback_ptr,
151 std::shared_ptr<Result> result_ptr) = 0;
152
156
157 rclcpp_action::GoalResponse handle_goal_(const rclcpp_action::GoalUUID & uuid, std::shared_ptr<const Goal> goal_ptr);
158 rclcpp_action::CancelResponse handle_cancel_(std::shared_ptr<GoalHandle> goal_handle_ptr);
159 void handle_accepted_(std::shared_ptr<GoalHandle> goal_handle_ptr);
160
161 void execution_timer_callback_(std::shared_ptr<const Goal> goal_ptr);
162
163public:
164 rclcpp::node_interfaces::NodeBaseInterface::SharedPtr get_node_base_interface() const;
165
166protected:
167 rclcpp::Node::SharedPtr node_ptr_;
168 std::shared_ptr<ActionContextType> action_context_ptr_;
169 const ParamListener param_listener_;
170
171private:
172 typename rclcpp_action::Server<ActionT>::SharedPtr action_server_ptr_;
173 rclcpp::TimerBase::SharedPtr execution_timer_ptr_;
174 rclcpp::Time last_feedback_ts_;
175};
176
177template <class ActionT>
179 const std::string & action_name, rclcpp::Node::SharedPtr node_ptr,
180 std::shared_ptr<ActionContextType> action_context_ptr)
181: node_ptr_(node_ptr), action_context_ptr_(action_context_ptr), param_listener_(node_ptr)
182{
183 exposeToGlobalDebugLogging(node_ptr_->get_logger());
184
185 using namespace std::placeholders;
186 action_server_ptr_ = rclcpp_action::create_server<ActionT>(
187 node_ptr_, action_name, std::bind(&ActionWrapper<ActionT>::handle_goal_, this, _1, _2),
188 std::bind(&ActionWrapper<ActionT>::handle_cancel_, this, _1),
189 std::bind(&ActionWrapper<ActionT>::handle_accepted_, this, _1));
190}
191
192template <class ActionT>
193ActionWrapper<ActionT>::ActionWrapper(const std::string & action_name, rclcpp::Node::SharedPtr node_ptr)
194: ActionWrapper(action_name, node_ptr, std::make_shared<ActionContextType>(node_ptr->get_logger()))
195{
196}
197
198template <class ActionT>
199ActionWrapper<ActionT>::ActionWrapper(const std::string & action_name, const rclcpp::NodeOptions & options)
200: ActionWrapper(action_name, std::make_shared<rclcpp::Node>(action_name + "_node", options))
201{
202}
203
204template <class ActionT>
205rclcpp::node_interfaces::NodeBaseInterface::SharedPtr ActionWrapper<ActionT>::get_node_base_interface() const
206{
207 return node_ptr_->get_node_base_interface();
208}
209
210template <class ActionT>
211bool ActionWrapper<ActionT>::onGoalRequest(std::shared_ptr<const Goal> /*goal_ptr*/)
212{
213 // Always accept goal by default
214 return true;
215}
216
217template <class ActionT>
219 std::shared_ptr<const Goal> /*goal_ptr*/, std::shared_ptr<Result> /*result_ptr*/)
220{
221 // By default, the result is initialized using the default values specified in the action message definition.
222}
223
224template <class ActionT>
226 std::shared_ptr<const Goal> /*goal_ptr*/, std::shared_ptr<Result> /*result_ptr*/)
227{
228 // Always accept cancel request by default
229 return true;
230}
231
232template <class ActionT>
234 std::shared_ptr<const Goal> /*goal_ptr*/, std::shared_ptr<Result> /*result_ptr*/)
235{
236 // Do nothing by default
237 return ActionStatus::SUCCESS;
238}
239
240template <class ActionT>
241rclcpp_action::GoalResponse ActionWrapper<ActionT>::handle_goal_(
242 const rclcpp_action::GoalUUID & uuid, std::shared_ptr<const Goal> goal_ptr)
243{
244 if (action_context_ptr_->isValid() && action_context_ptr_->getGoalHandlePtr()->is_active()) {
245 RCLCPP_DEBUG(
246 node_ptr_->get_logger(), "Goal %s was REJECTED: Goal %s is still executing.",
247 rclcpp_action::to_string(uuid).c_str(),
248 rclcpp_action::to_string(action_context_ptr_->getGoalHandlePtr()->get_goal_id()).c_str());
249 return rclcpp_action::GoalResponse::REJECT;
250 }
251
252 if (!onGoalRequest(goal_ptr)) {
253 RCLCPP_DEBUG(
254 node_ptr_->get_logger(), "Goal %s was REJECTED because onGoalRequest() returned false",
255 rclcpp_action::to_string(uuid).c_str());
256 return rclcpp_action::GoalResponse::REJECT;
257 }
258
259 return rclcpp_action::GoalResponse::ACCEPT_AND_EXECUTE;
260}
261
262template <class ActionT>
263rclcpp_action::CancelResponse ActionWrapper<ActionT>::handle_cancel_(std::shared_ptr<GoalHandle> /*goal_handle_ptr*/)
264{
265 return onCancelRequest(action_context_ptr_->getGoalHandlePtr()->get_goal(), action_context_ptr_->getResultPtr())
266 ? rclcpp_action::CancelResponse::ACCEPT
267 : rclcpp_action::CancelResponse::REJECT;
268}
269
270template <class ActionT>
271void ActionWrapper<ActionT>::handle_accepted_(std::shared_ptr<GoalHandle> goal_handle_ptr)
272{
273 action_context_ptr_->setUp(goal_handle_ptr);
274 std::shared_ptr<const Goal> goal_ptr = action_context_ptr_->getGoalHandlePtr()->get_goal();
275 setInitialResult(goal_ptr, action_context_ptr_->getResultPtr());
276 (void)goal_handle_ptr; // action_context_ptr_ takes ownership of goal handle from now on
277
278 const Params & params = param_listener_.get_params();
279
280 // Ensure that feedback is published already after the first cycle
281 last_feedback_ts_ = node_ptr_->now() - rclcpp::Duration::from_seconds(params.feedback_rate);
282
283 // Create the timer that triggers the execution routine
284 execution_timer_ptr_ = node_ptr_->create_wall_timer(
285 std::chrono::duration<double>(params.loop_rate), [this, goal_ptr]() { this->execution_timer_callback_(goal_ptr); });
286}
287
288template <class ActionT>
289void ActionWrapper<ActionT>::execution_timer_callback_(std::shared_ptr<const Goal> goal_ptr)
290{
291 // Cancel timer when goal has terminated
292 if (!action_context_ptr_->getGoalHandlePtr()->is_active()) {
293 execution_timer_ptr_->cancel();
294 return;
295 }
296
297 // Check if canceling
298 if (action_context_ptr_->getGoalHandlePtr()->is_canceling()) {
299 switch (cancelGoal(goal_ptr, action_context_ptr_->getResultPtr())) {
300 case ActionStatus::RUNNING:
301 return;
302 case ActionStatus::SUCCESS:
303 action_context_ptr_->cancel();
304 return;
305 case ActionStatus::FAILURE:
306 action_context_ptr_->abort();
307 return;
308 }
309 } else {
310 const auto ret = executeGoal(goal_ptr, action_context_ptr_->getFeedbackPtr(), action_context_ptr_->getResultPtr());
311
312 // Publish feedback
313 const auto feedback_rate = rclcpp::Duration::from_seconds(param_listener_.get_params().feedback_rate);
314 if (node_ptr_->now() - last_feedback_ts_ > feedback_rate) {
315 action_context_ptr_->publishFeedback();
316 last_feedback_ts_ = node_ptr_->now();
317 }
318
319 switch (ret) {
320 case ActionStatus::RUNNING:
321 break;
322 case ActionStatus::SUCCESS:
323 action_context_ptr_->succeed();
324 return;
325 case ActionStatus::FAILURE:
326 action_context_ptr_->abort();
327 return;
328 }
329 }
330}
331
332} // namespace auto_apms_util
Helper class that stores contextual information related to a ROS 2 action.
virtual void setInitialResult(std::shared_ptr< const Goal > goal_ptr, std::shared_ptr< Result > result_ptr)
Configure the initial action result that is set once a goal is accepted.
virtual Status executeGoal(std::shared_ptr< const Goal > goal_ptr, std::shared_ptr< Feedback > feedback_ptr, std::shared_ptr< Result > result_ptr)=0
Callback that executes work asynchronously when the goal is executing.
virtual bool onCancelRequest(std::shared_ptr< const Goal > goal_ptr, std::shared_ptr< Result > result_ptr)
Callback for handling an incoming cancel request.
ActionWrapper(const std::string &action_name, rclcpp::Node::SharedPtr node_ptr, std::shared_ptr< ActionContextType > action_context_ptr)
Constructor.
virtual Status cancelGoal(std::shared_ptr< const Goal > goal_ptr, std::shared_ptr< Result > result_ptr)
Callback that executes work asynchronously when the goal is cancelling.
ActionWrapper(const std::string &action_name, rclcpp::Node::SharedPtr node_ptr)
Constructor.
virtual bool onGoalRequest(std::shared_ptr< const Goal > goal_ptr)
Callback for handling an incoming action goal request.
ActionWrapper(const std::string &action_name, const rclcpp::NodeOptions &options)
Constructor.
ActionStatus
Status of the auto_apms_util::ActionWrapper execution process.
void exposeToGlobalDebugLogging(const rclcpp::Logger &logger)
Enable ROS 2 debug logging, if the C preprocessor flag _AUTO_APMS_DEBUG_LOGGING is set.
Definition logging.cpp:25
Fundamental helper classes and utility functions.