AutoAPMS
Resilient Robot Mission Management
Loading...
Searching...
No Matches
task.hpp
Go to the documentation of this file.
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 "rclcpp/rclcpp.hpp"
20#include "rclcpp_action/rclcpp_action.hpp"
23
24namespace auto_apms_core
25{
26
36enum class TaskStatus : uint8_t
37{
38 RUNNING,
39 SUCCESS,
41};
42
50template <typename ActionT>
51class Task
52{
53public:
56 using Goal = typename ActionContextType::Goal;
60
61 static constexpr std::chrono::milliseconds DEFAULT_VALUE_EXECUTION_INTERVAL{ 10 };
62 static constexpr std::chrono::milliseconds DEFAULT_VALUE_FEEDBACK_INTERVAL{ 200 };
63 static constexpr char PARAM_NAME_FEEDBACK_INTERVAL[] = "feedback_interval_ms";
64
65 explicit Task(const std::string& name, rclcpp::Node::SharedPtr node_ptr,
66 std::shared_ptr<ActionContextType> action_context_ptr,
67 std::chrono::milliseconds execution_interval = DEFAULT_VALUE_EXECUTION_INTERVAL,
68 std::chrono::milliseconds feedback_interval = DEFAULT_VALUE_FEEDBACK_INTERVAL);
69 explicit Task(const std::string& name, rclcpp::Node::SharedPtr node_ptr,
70 std::chrono::milliseconds execution_interval = DEFAULT_VALUE_EXECUTION_INTERVAL,
71 std::chrono::milliseconds feedback_interval = DEFAULT_VALUE_FEEDBACK_INTERVAL);
72 explicit Task(const std::string& name, const rclcpp::NodeOptions& options,
73 std::chrono::milliseconds execution_interval = DEFAULT_VALUE_EXECUTION_INTERVAL,
74 std::chrono::milliseconds feedback_interval = DEFAULT_VALUE_FEEDBACK_INTERVAL);
75
76private:
81 virtual bool onGoalRequest(std::shared_ptr<const Goal> goal_ptr);
82
83 virtual void setInitialResult(std::shared_ptr<const Goal> goal_ptr, std::shared_ptr<Result> result_ptr);
84
85 virtual bool onCancelRequest(std::shared_ptr<const Goal> goal_ptr, std::shared_ptr<Result> result_ptr);
86
87 virtual Status cancelGoal(std::shared_ptr<const Goal> goal_ptr, std::shared_ptr<Result> result_ptr);
88
89 virtual Status executeGoal(std::shared_ptr<const Goal> goal_ptr, std::shared_ptr<Feedback> feedback_ptr,
90 std::shared_ptr<Result> result_ptr) = 0;
91
96 rclcpp_action::GoalResponse handle_goal_(const rclcpp_action::GoalUUID& uuid, std::shared_ptr<const Goal> goal_ptr);
97 rclcpp_action::CancelResponse handle_cancel_(std::shared_ptr<GoalHandle> goal_handle_ptr);
98 void handle_accepted_(std::shared_ptr<GoalHandle> goal_handle_ptr);
99
100 void execution_timer_callback_(std::shared_ptr<const Goal> goal_ptr);
101
102public:
103 rclcpp::node_interfaces::NodeBaseInterface::SharedPtr get_node_base_interface() const;
104
105protected:
106 rclcpp::Node::SharedPtr node_ptr_;
107 std::shared_ptr<ActionContextType> action_context_ptr_;
108
109private:
110 typename rclcpp_action::Server<ActionT>::SharedPtr action_server_ptr_;
111 rclcpp::TimerBase::SharedPtr execution_timer_ptr_;
112 const std::chrono::milliseconds execution_timer_interval_;
113 std::chrono::steady_clock::time_point last_feedback_ts_;
114};
115
116template <class ActionT>
117Task<ActionT>::Task(const std::string& name, rclcpp::Node::SharedPtr node_ptr,
118 std::shared_ptr<ActionContextType> action_context_ptr, std::chrono::milliseconds execution_interval,
119 std::chrono::milliseconds feedback_interval)
120 : node_ptr_{ node_ptr }, action_context_ptr_{ action_context_ptr }, execution_timer_interval_{ execution_interval }
121{
122 exposeToDebugLogging(node_ptr_->get_logger());
123
124 using namespace std::placeholders;
125 action_server_ptr_ = rclcpp_action::create_server<ActionT>(
126 node_ptr_, name, std::bind(&Task<ActionT>::handle_goal_, this, _1, _2),
127 std::bind(&Task<ActionT>::handle_cancel_, this, _1), std::bind(&Task<ActionT>::handle_accepted_, this, _1));
128
132 rcl_interfaces::msg::ParameterDescriptor param_desc;
133 param_desc.description = "Rate at which this task publishes feedback.";
134 node_ptr_->declare_parameter(PARAM_NAME_FEEDBACK_INTERVAL, feedback_interval.count(), param_desc);
135}
136
137template <class ActionT>
138Task<ActionT>::Task(const std::string& name, rclcpp::Node::SharedPtr node_ptr,
139 std::chrono::milliseconds execution_interval, std::chrono::milliseconds feedback_interval)
140 : Task{ name, node_ptr, std::make_shared<ActionContextType>(node_ptr->get_logger()), execution_interval,
141 feedback_interval }
142{
143}
144
145template <class ActionT>
146Task<ActionT>::Task(const std::string& name, const rclcpp::NodeOptions& options,
147 std::chrono::milliseconds execution_interval, std::chrono::milliseconds feedback_interval)
148 : Task{ name, std::make_shared<rclcpp::Node>("task_" + name + "_node", options), execution_interval,
149 feedback_interval }
150{
151}
152
153template <class ActionT>
154rclcpp::node_interfaces::NodeBaseInterface::SharedPtr Task<ActionT>::get_node_base_interface() const
155{
156 return node_ptr_->get_node_base_interface();
157}
158
159template <class ActionT>
160bool Task<ActionT>::onGoalRequest(std::shared_ptr<const Goal> goal_ptr)
161{
162 (void)goal_ptr;
163 // Always accept goal by default
164 return true;
165}
166
167template <class ActionT>
168void Task<ActionT>::setInitialResult(std::shared_ptr<const Goal> goal_ptr, std::shared_ptr<Result> result_ptr)
169{
170 // By default, the result is initialized using the default values specified in the action message definition.
171 (void)goal_ptr;
172 (void)result_ptr;
173}
174
175template <class ActionT>
176bool Task<ActionT>::onCancelRequest(std::shared_ptr<const Goal> goal_ptr, std::shared_ptr<Result> result_ptr)
177{
178 (void)goal_ptr;
179 (void)result_ptr;
180 // Always accept cancel request by default
181 return true;
182}
183
184template <class ActionT>
185TaskStatus Task<ActionT>::cancelGoal(std::shared_ptr<const Goal> goal_ptr, std::shared_ptr<Result> result_ptr)
186{
187 (void)goal_ptr;
188 (void)result_ptr;
189 // Do nothing by default
190 return TaskStatus::SUCCESS;
191}
192
193template <class ActionT>
194rclcpp_action::GoalResponse Task<ActionT>::handle_goal_(const rclcpp_action::GoalUUID& uuid,
195 std::shared_ptr<const Goal> goal_ptr)
196{
197 if (action_context_ptr_->isValid() && action_context_ptr_->getGoalHandlePtr()->is_active())
198 {
199 RCLCPP_DEBUG(node_ptr_->get_logger(),
200 "Goal %s was REJECTED because another one is still executing. ID of the executing goal: %s",
201 rclcpp_action::to_string(uuid).c_str(),
202 rclcpp_action::to_string(action_context_ptr_->getGoalHandlePtr()->get_goal_id()).c_str());
203 return rclcpp_action::GoalResponse::REJECT;
204 }
205
206 if (onGoalRequest(goal_ptr))
207 {
208 return rclcpp_action::GoalResponse::ACCEPT_AND_EXECUTE;
209 }
210
211 RCLCPP_DEBUG(node_ptr_->get_logger(), "Goal %s was REJECTED because onGoalRequest() returned false",
212 rclcpp_action::to_string(uuid).c_str());
213 return rclcpp_action::GoalResponse::REJECT;
214}
215
216template <class ActionT>
217rclcpp_action::CancelResponse Task<ActionT>::handle_cancel_(std::shared_ptr<GoalHandle> goal_handle_ptr)
218{
219 (void)goal_handle_ptr;
220
221 return onCancelRequest(action_context_ptr_->getGoalHandlePtr()->get_goal(), action_context_ptr_->getResultPtr()) ?
222 rclcpp_action::CancelResponse::ACCEPT :
223 rclcpp_action::CancelResponse::REJECT;
224}
225
226template <class ActionT>
227void Task<ActionT>::handle_accepted_(std::shared_ptr<GoalHandle> goal_handle_ptr)
228{
229 action_context_ptr_->setUp(goal_handle_ptr);
230 const auto goal_ptr = action_context_ptr_->getGoalHandlePtr()->get_goal();
231 setInitialResult(goal_ptr, action_context_ptr_->getResultPtr());
232 (void)goal_handle_ptr; // action_context_ptr_ takes ownership of goal handle from now on
233
234 // Create the timer that triggers the execution routine
235 execution_timer_ptr_ = node_ptr_->create_wall_timer(
236 execution_timer_interval_, [this, goal_ptr]() { this->execution_timer_callback_(goal_ptr); });
237
238 // Ensure that feedback is published already at the first cycle
239 const std::chrono::milliseconds feedback_interval{ node_ptr_->get_parameter(PARAM_NAME_FEEDBACK_INTERVAL).as_int() };
240 last_feedback_ts_ = std::chrono::steady_clock::now() - feedback_interval;
241}
242
243template <class ActionT>
244void Task<ActionT>::execution_timer_callback_(std::shared_ptr<const Goal> goal_ptr)
245{
246 // Cancel timer when goal has terminated
247 if (!action_context_ptr_->getGoalHandlePtr()->is_active())
248 {
249 execution_timer_ptr_->cancel();
250 return;
251 }
252
253 // Check if canceling
254 if (action_context_ptr_->getGoalHandlePtr()->is_canceling())
255 {
256 switch (cancelGoal(goal_ptr, action_context_ptr_->getResultPtr()))
257 {
259 return;
261 action_context_ptr_->cancel();
262 return;
264 action_context_ptr_->abort();
265 return;
266 }
267 }
268 else
269 {
270 const auto ret = executeGoal(goal_ptr, action_context_ptr_->getFeedbackPtr(), action_context_ptr_->getResultPtr());
271
272 // Publish feedback
273 const std::chrono::milliseconds feedback_interval{
274 node_ptr_->get_parameter(PARAM_NAME_FEEDBACK_INTERVAL).as_int()
275 };
276 if (feedback_interval <= (std::chrono::steady_clock::now() - last_feedback_ts_))
277 {
278 action_context_ptr_->publishFeedback();
279 last_feedback_ts_ = std::chrono::steady_clock::now();
280 }
281
282 switch (ret)
283 {
285 break;
287 action_context_ptr_->succeed();
288 return;
290 action_context_ptr_->abort();
291 return;
292 }
293 }
294}
295
296} // namespace auto_apms_core
typename ActionT::Goal Goal
rclcpp_action::ServerGoalHandle< ActionT > GoalHandle
typename ActionT::Result Result
typename ActionT::Feedback Feedback
Generic base class for robot actions.
Definition task.hpp:52
static constexpr std::chrono::milliseconds DEFAULT_VALUE_EXECUTION_INTERVAL
Definition task.hpp:61
static constexpr char PARAM_NAME_FEEDBACK_INTERVAL[]
Definition task.hpp:63
typename ActionContextType::Feedback Feedback
Definition task.hpp:57
rclcpp::Node::SharedPtr node_ptr_
Definition task.hpp:106
static constexpr std::chrono::milliseconds DEFAULT_VALUE_FEEDBACK_INTERVAL
Definition task.hpp:62
typename ActionContextType::Goal Goal
Definition task.hpp:56
Task(const std::string &name, const rclcpp::NodeOptions &options, std::chrono::milliseconds execution_interval=DEFAULT_VALUE_EXECUTION_INTERVAL, std::chrono::milliseconds feedback_interval=DEFAULT_VALUE_FEEDBACK_INTERVAL)
Definition task.hpp:146
Task(const std::string &name, rclcpp::Node::SharedPtr node_ptr, std::shared_ptr< ActionContextType > action_context_ptr, std::chrono::milliseconds execution_interval=DEFAULT_VALUE_EXECUTION_INTERVAL, std::chrono::milliseconds feedback_interval=DEFAULT_VALUE_FEEDBACK_INTERVAL)
Definition task.hpp:117
Task(const std::string &name, rclcpp::Node::SharedPtr node_ptr, std::chrono::milliseconds execution_interval=DEFAULT_VALUE_EXECUTION_INTERVAL, std::chrono::milliseconds feedback_interval=DEFAULT_VALUE_FEEDBACK_INTERVAL)
Definition task.hpp:138
std::shared_ptr< ActionContextType > action_context_ptr_
Definition task.hpp:107
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr get_node_base_interface() const
Definition task.hpp:154
typename ActionContextType::GoalHandle GoalHandle
Definition task.hpp:59
typename ActionContextType::Result Result
Definition task.hpp:58
TaskStatus
Status of the auto_apms_core::Task execution process.
Definition task.hpp:37
void exposeToDebugLogging(const rclcpp::Logger &logger)
Definition logging.cpp:22