AutoAPMS
Streamlining behaviors in ROS 2
Loading...
Searching...
No Matches
action_based_executor_node.hpp
1// Copyright 2026 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// http://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 <map>
18
19#include "auto_apms_behavior_tree/executor/generic_executor_node.hpp"
20#include "auto_apms_util/action_context.hpp"
21#include "rclcpp_action/rclcpp_action.hpp"
22
24{
25
45template <typename ActionT>
47{
48public:
49 using TriggerActionContext = auto_apms_util::ActionContext<ActionT>;
50 using TriggerGoal = typename ActionT::Goal;
51 using TriggerFeedback = typename ActionT::Feedback;
52 using TriggerResult = typename ActionT::Result;
53 using TriggerGoalHandle = rclcpp_action::ServerGoalHandle<ActionT>;
54
61 ActionBasedTreeExecutorNode(rclcpp::Node::SharedPtr node_ptr, const std::string & action_name, Options options);
62
69 ActionBasedTreeExecutorNode(const std::string & name, const std::string & action_name, Options options);
70
76 ActionBasedTreeExecutorNode(const std::string & name, const std::string & action_name);
77
78 virtual ~ActionBasedTreeExecutorNode() override = default;
79
80protected:
81 /* Virtual methods to implement/override */
82
92 virtual TreeConstructor getTreeConstructorFromGoal(std::shared_ptr<const TriggerGoal> goal_ptr) = 0;
93
102 virtual bool shouldAcceptGoal(const rclcpp_action::GoalUUID & uuid, std::shared_ptr<const TriggerGoal> goal_ptr);
103
108 virtual void onAcceptedGoal(std::shared_ptr<TriggerGoalHandle> goal_handle_ptr);
109
117 virtual void onExecutionStarted(std::shared_ptr<TriggerGoalHandle> goal_handle_ptr);
118
127 virtual void onGoalExecutionTermination(const ExecutionResult & result, TriggerActionContext & context);
128
129 TriggerActionContext trigger_action_context_;
130 std::map<rclcpp_action::GoalUUID, TreeConstructor> pending_tree_constructors_;
131
132private:
133 void onTermination(const ExecutionResult & result) override final;
134
135 /* Internal action callbacks */
136
137 rclcpp_action::GoalResponse handle_trigger_goal_(
138 const rclcpp_action::GoalUUID & uuid, std::shared_ptr<const TriggerGoal> goal_ptr);
139 rclcpp_action::CancelResponse handle_trigger_cancel_(std::shared_ptr<TriggerGoalHandle> goal_handle_ptr);
140 void handle_trigger_accept_(std::shared_ptr<TriggerGoalHandle> goal_handle_ptr);
141
142 typename rclcpp_action::Server<ActionT>::SharedPtr trigger_action_ptr_;
143};
144
145// #####################################################################################################################
146// ################################ DEFINITIONS ##############################################
147// #####################################################################################################################
148
149template <typename ActionT>
151 rclcpp::Node::SharedPtr node_ptr, const std::string & action_name, Options options)
152: GenericTreeExecutorNode(node_ptr, options), trigger_action_context_(logger_)
153{
154 using namespace std::placeholders;
155 trigger_action_ptr_ = rclcpp_action::create_server<ActionT>(
156 node_ptr_, action_name, std::bind(&ActionBasedTreeExecutorNode::handle_trigger_goal_, this, _1, _2),
157 std::bind(&ActionBasedTreeExecutorNode::handle_trigger_cancel_, this, _1),
158 std::bind(&ActionBasedTreeExecutorNode::handle_trigger_accept_, this, _1));
159}
160
161template <typename ActionT>
163 const std::string & name, const std::string & action_name, Options options)
164: ActionBasedTreeExecutorNode(std::make_shared<rclcpp::Node>(name, options.getROSNodeOptions()), action_name, options)
165{
166}
167
168template <typename ActionT>
170 const std::string & name, const std::string & action_name)
171: ActionBasedTreeExecutorNode(name, action_name, Options(rclcpp::NodeOptions()))
172{
173}
174
175template <typename ActionT>
177 const rclcpp_action::GoalUUID & uuid, std::shared_ptr<const TriggerGoal> /*goal_ptr*/)
178{
179 if (isBusy()) {
180 RCLCPP_WARN(
181 logger_, "Goal %s was REJECTED: Tree '%s' is currently executing.", rclcpp_action::to_string(uuid).c_str(),
182 getTreeName().c_str());
183 return false;
184 }
185 return true;
186}
187
188template <typename ActionT>
189void ActionBasedTreeExecutorNode<ActionT>::onAcceptedGoal(std::shared_ptr<TriggerGoalHandle> /*goal_handle_ptr*/)
190{
191}
192
193template <typename ActionT>
194void ActionBasedTreeExecutorNode<ActionT>::onExecutionStarted(std::shared_ptr<TriggerGoalHandle> goal_handle_ptr)
195{
196 trigger_action_context_.setUp(goal_handle_ptr);
197 RCLCPP_INFO(logger_, "Successfully started execution of tree '%s' via action trigger.", getTreeName().c_str());
198}
199
200template <typename ActionT>
202 const ExecutionResult & result, TriggerActionContext & context)
203{
204 switch (result) {
206 context.succeed();
207 break;
209 context.abort();
210 break;
212 if (context.getGoalHandlePtr()->is_canceling()) {
213 context.cancel();
214 } else {
215 context.abort();
216 }
217 break;
219 default:
220 context.abort();
221 break;
222 }
223}
224
225template <typename ActionT>
226void ActionBasedTreeExecutorNode<ActionT>::onTermination(const ExecutionResult & result)
227{
228 if (trigger_action_context_.isValid()) {
229 onGoalExecutionTermination(result, trigger_action_context_);
230 trigger_action_context_.invalidate();
231 }
232}
233
234template <typename ActionT>
235rclcpp_action::GoalResponse ActionBasedTreeExecutorNode<ActionT>::handle_trigger_goal_(
236 const rclcpp_action::GoalUUID & uuid, std::shared_ptr<const TriggerGoal> goal_ptr)
237{
238 if (!shouldAcceptGoal(uuid, goal_ptr)) {
239 return rclcpp_action::GoalResponse::REJECT;
240 }
241
242 try {
243 pending_tree_constructors_[uuid] = getTreeConstructorFromGoal(goal_ptr);
244 } catch (const std::exception & e) {
245 RCLCPP_WARN(
246 logger_, "Goal %s was REJECTED: Exception in getTreeConstructorFromGoal(): %s",
247 rclcpp_action::to_string(uuid).c_str(), e.what());
248 return rclcpp_action::GoalResponse::REJECT;
249 }
250 return rclcpp_action::GoalResponse::ACCEPT_AND_EXECUTE;
251}
252
253template <typename ActionT>
254rclcpp_action::CancelResponse ActionBasedTreeExecutorNode<ActionT>::handle_trigger_cancel_(
255 std::shared_ptr<TriggerGoalHandle> /*goal_handle_ptr*/)
256{
257 setControlCommand(ControlCommand::TERMINATE);
258 return rclcpp_action::CancelResponse::ACCEPT;
259}
260
261template <typename ActionT>
262void ActionBasedTreeExecutorNode<ActionT>::handle_trigger_accept_(std::shared_ptr<TriggerGoalHandle> goal_handle_ptr)
263{
264 onAcceptedGoal(goal_handle_ptr);
265
266 const rclcpp_action::GoalUUID uuid = goal_handle_ptr->get_goal_id();
267 auto node = pending_tree_constructors_.extract(uuid);
268 if (node.empty()) {
269 RCLCPP_ERROR(logger_, "No pending tree constructor found for goal %s.", rclcpp_action::to_string(uuid).c_str());
270 auto result_ptr = std::make_shared<TriggerResult>();
271 goal_handle_ptr->abort(result_ptr);
272 return;
273 }
274 TreeConstructor tree_constructor = std::move(node.mapped());
275
276 const ExecutorParameters params = executor_param_listener_.get_params();
277 try {
278 startExecution(tree_constructor, params.tick_rate, params.groot2_port);
279 } catch (const std::exception & e) {
280 auto result_ptr = std::make_shared<TriggerResult>();
281 goal_handle_ptr->abort(result_ptr);
282 RCLCPP_ERROR(logger_, "An error occurred trying to start execution: %s", e.what());
283 return;
284 }
285
286 onExecutionStarted(goal_handle_ptr);
287}
288
289} // namespace auto_apms_behavior_tree
ActionBasedTreeExecutorNode(const std::string &name, const std::string &action_name)
Constructor with default executor options.
virtual void onExecutionStarted(std::shared_ptr< TriggerGoalHandle > goal_handle_ptr)
Hook called after execution has been started successfully.
virtual void onAcceptedGoal(std::shared_ptr< TriggerGoalHandle > goal_handle_ptr)
Hook called after a trigger action goal has been accepted and before execution begins.
virtual void onGoalExecutionTermination(const ExecutionResult &result, TriggerActionContext &context)
Handle the execution result for the action client.
ActionBasedTreeExecutorNode(rclcpp::Node::SharedPtr node_ptr, const std::string &action_name, Options options)
Constructor using an existing ROS 2 node.
ActionBasedTreeExecutorNode(const std::string &name, const std::string &action_name, Options options)
Constructor which creates a new ROS 2 node.
virtual TreeConstructor getTreeConstructorFromGoal(std::shared_ptr< const TriggerGoal > goal_ptr)=0
Create a TreeConstructor from the received action goal.
virtual bool shouldAcceptGoal(const rclcpp_action::GoalUUID &uuid, std::shared_ptr< const TriggerGoal > goal_ptr)
Determine whether an incoming trigger action goal should be accepted.
GenericTreeExecutorNode(rclcpp::Node::SharedPtr node_ptr, Options options)
Constructor using an existing ROS 2 node.
bool isBusy()
Determine whether this executor is currently executing a behavior tree.
rclcpp::Node::SharedPtr node_ptr_
Shared pointer to the parent ROS 2 node.
std::string getTreeName()
Get the name of the tree that is currently executing.
ExecutionResult
Enum representing possible behavior tree execution results.
@ TREE_SUCCEEDED
Tree completed with BT::NodeStatus::SUCCESS.
@ TERMINATED_PREMATURELY
Execution terminated before the tree was able to propagate the tick to all its nodes.
@ TREE_FAILED
Tree completed with BT::NodeStatus::FAILURE.
const rclcpp::Logger logger_
Logger associated with the parent ROS 2 node.
Helper class that stores contextual information related to a ROS 2 action.
void cancel()
Terminate the current goal and mark it as canceled.
std::shared_ptr< GoalHandle > getGoalHandlePtr()
Get the goal handle managed by this ActionContext instance.
void succeed()
Terminate the current goal and mark it as succeeded.
void abort()
Terminate the current goal and mark it as aborted.
Powerful tooling for incorporating behavior trees for task development.
Definition behavior.hpp:32