15#include "auto_apms_behavior_tree/executor/executor_base.hpp"
19#include "auto_apms_util/container.hpp"
20#include "auto_apms_util/logging.hpp"
26 rclcpp::Node::SharedPtr node_ptr, rclcpp::CallbackGroup::SharedPtr tree_node_callback_group_ptr)
29 tree_node_waitables_callback_group_ptr_(tree_node_callback_group_ptr),
30 tree_node_waitables_executor_ptr_(rclcpp::executors::SingleThreadedExecutor::make_shared()),
31 global_blackboard_ptr_(TreeBlackboard::create()),
33 execution_stopped_(true)
38 if (!tree_node_waitables_callback_group_ptr_) {
39 tree_node_waitables_callback_group_ptr_ =
40 node_ptr_->create_callback_group(rclcpp::CallbackGroupType::MutuallyExclusive,
false);
44 tree_node_waitables_executor_ptr_->add_callback_group(
49 TreeConstructor make_tree,
double tick_rate_sec,
int groot2_port)
52 throw exceptions::TreeExecutorError(
53 "Cannot start execution with tree '" +
getTreeName() +
"' currently executing.");
56 TreeBlackboardSharedPtr main_tree_bb_ptr = TreeBlackboard::create(global_blackboard_ptr_);
58 tree_ptr_.reset(
new Tree(make_tree(main_tree_bb_ptr)));
59 }
catch (
const std::exception & e) {
60 throw exceptions::TreeBuildError(
61 "Cannot start execution because creating the tree failed: " + std::string(e.what()));
65 groot2_publisher_ptr_.reset();
66 if (groot2_port != -1) {
68 groot2_publisher_ptr_ = std::make_unique<BT::Groot2Publisher>(*tree_ptr_, groot2_port);
69 }
catch (
const std::exception & e) {
70 throw exceptions::TreeExecutorError(
71 "Failed to initialize Groot2 publisher with port " + std::to_string(groot2_port) +
": " + e.what());
76 state_observer_ptr_.reset();
77 state_observer_ptr_ = std::make_unique<TreeStateObserver>(*tree_ptr_,
logger_);
78 state_observer_ptr_->enableTransitionToIdle(
false);
85 termination_reason_ =
"";
86 execution_stopped_ =
true;
89 auto promise_ptr = std::make_shared<std::promise<ExecutionResult>>();
90 TerminationCallback termination_callback = [
this, promise_ptr](
ExecutionResult result,
const std::string & msg) {
94 RCLCPP_ERROR(
logger_,
"Termination reason: %s", msg.c_str());
96 RCLCPP_INFO(
logger_,
"Termination reason: %s", msg.c_str());
100 promise_ptr->set_value(result);
101 execution_timer_ptr_->cancel();
106 const std::chrono::nanoseconds period =
107 std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::duration<double>(tick_rate_sec));
108 execution_timer_ptr_ =
node_ptr_->create_wall_timer(period, [
this, period, termination_callback]() {
110 tree_node_waitables_executor_ptr_->spin_all(period);
113 tick_callback_(termination_callback);
115 return promise_ptr->get_future();
118void TreeExecutorBase::tick_callback_(TerminationCallback termination_callback)
121 if (prev_execution_state_ != this_execution_state) {
123 logger_,
"Executor for tree '%s' changed state from '%s' to '%s'.",
getTreeName().c_str(),
124 toStr(prev_execution_state_).c_str(), toStr(this_execution_state).c_str());
125 prev_execution_state_ = this_execution_state;
130 execution_stopped_ =
false;
131 bool do_on_tick =
true;
132 switch (control_command_) {
134 execution_stopped_ =
true;
141 termination_reason_ =
"onInitialTick() returned false.";
150 termination_reason_ =
"onTick() returned false.";
158 termination_callback(
160 termination_reason_.empty() ?
"Control command was set to TERMINATE." : termination_reason_);
169#ifdef AUTO_APMS_BEHAVIOR_TREE__EXECUTOR_THROW_ON_TICK_ERROR
170 tree_ptr_->haltTree();
173 tree_ptr_->haltTree();
174 }
catch (
const std::exception & e) {
175 termination_callback(
177 "Error during haltTree() on command " +
toStr(control_command_) +
": " + std::string(e.what()));
183 throw std::logic_error(
184 "Handling control command " + std::to_string(
static_cast<int>(control_command_)) +
" '" +
185 toStr(control_command_) +
"' is not implemented.");
190 BT::NodeStatus bt_status = BT::NodeStatus::IDLE;
191#ifdef AUTO_APMS_BEHAVIOR_TREE__EXECUTOR_THROW_ON_TICK_ERROR
192 bt_status = tree_ptr_->tickExactlyOnce();
196 bt_status = tree_ptr_->tickExactlyOnce();
197 }
catch (
const std::exception & e) {
198 std::string msg =
"Ran into an exception during tick: " + std::string(e.what());
200 tree_ptr_->haltTree();
201 }
catch (
const std::exception & e) {
202 msg +=
"\nDuring haltTree(), another exception occurred: " + std::string(e.what());
214 if (bt_status == BT::NodeStatus::RUNNING)
return;
218 if (!(bt_status == BT::NodeStatus::SUCCESS || bt_status == BT::NodeStatus::FAILURE)) {
219 throw std::logic_error(
220 "bt_status is " + BT::toStr(bt_status) +
". Must be one of SUCCESS or FAILURE at this point.");
222 const bool success = bt_status == BT::NodeStatus::SUCCESS;
225 termination_callback(
227 "Terminated on tree result " + BT::toStr(bt_status) +
".");
234 throw std::logic_error(
"Execution routine is not intended to proceed to this statement.");
254 global_blackboard_ptr_ = TreeBlackboard::create();
258 logger_,
"clearGlobalBlackboard() was called when executor is %s, but this is only allowed when IDLE. Ignoring...",
259 toStr(curr_state).c_str());
270 if (!tree_ptr_)
throw std::logic_error(
"tree_ptr_ cannot be nullptr when execution is started.");
271 if (tree_ptr_->rootNode()->status() == BT::NodeStatus::IDLE) {
284 if (tree_ptr_)
return tree_ptr_->subtrees[0]->tree_ID;
285 return "NO_TREE_NAME";
292 if (!state_observer_ptr_) {
293 throw exceptions::TreeExecutorError(
"Cannot get state observer because executor is not busy.");
295 return *state_observer_ptr_;
302 return node_ptr_->get_node_base_interface();
307 return tree_node_waitables_callback_group_ptr_;
312 return tree_node_waitables_executor_ptr_;
362 return "TREE_SUCCEEDED";
364 return "TREE_FAILED";
366 return "TERMINATED_PREMATURELY";
ExecutionState
Enum representing possible behavior tree execution states.
@ STARTING
Initializing execution.
@ RUNNING
Executor is busy and tree has been ticked at least once.
@ PAUSED
Execution routine is active, but tree is not being ticked.
@ HALTED
Execution routine is active, but tree is not being ticked and has been halted before.
virtual void onTermination(const ExecutionResult &result)
Callback invoked when the execution routine terminates.
std::shared_future< ExecutionResult > startExecution(TreeConstructor make_tree, double tick_rate_sec=0.1, int groot2_port=-1)
Start a behavior tree that is built using a callback.
ExecutionState getExecutionState()
Get a status code indicating the current state of execution.
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.
virtual TreeExitBehavior onTreeExit(bool success)
Callback invoked last thing when the execution routine completes because the behavior tree is finishe...
virtual bool onInitialTick()
Callback invoked once before the behavior tree is ticked for the very first time.
rclcpp::executors::SingleThreadedExecutor::SharedPtr getTreeNodeWaitablesExecutorPtr()
Get the ROS 2 executor instance used for spinning waitables registered by behavior tree nodes.
rclcpp::CallbackGroup::SharedPtr getTreeNodeWaitablesCallbackGroupPtr()
Get the callback group used for all waitables registered by behavior tree nodes.
void setControlCommand(ControlCommand cmd)
Set the command that handles the control flow of the execution routine.
virtual bool onTick()
Callback invoked every time before the behavior tree is ticked.
virtual bool afterTick()
Callback invoked every time after the behavior tree is ticked.
TreeExitBehavior
Enum representing possible options for what to do when a behavior tree is completed.
@ RESTART
Restart execution and keep on running.
@ TERMINATE
Stop execution and reset the timer.
virtual bool clearGlobalBlackboard()
Reset the global blackboard and clear all entries.
ControlCommand
Enum representing possible commands for controlling the behavior tree execution routine.
@ TERMINATE
Halt the currently executing tree and terminate the execution routine.
@ PAUSE
Pause the execution routine.
@ RUN
Start/Resume the execution routine.
@ HALT
Halt the currently executing tree and pause the execution routine.
TreeStateObserver & getStateObserver()
Get a reference to the current behavior tree state observer.
TreeExecutorBase(rclcpp::Node::SharedPtr node_ptr, rclcpp::CallbackGroup::SharedPtr tree_node_callback_group_ptr=nullptr)
Constructor.
rclcpp::Node::SharedPtr getNodePtr()
Get a shared pointer to the parent ROS 2 node.
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr get_node_base_interface()
Get the node's base interface. Is required to be able to register derived classes as ROS2 components.
TreeBlackboardSharedPtr getGlobalBlackboardPtr()
Get a shared pointer to the global blackboard instance.
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.
@ ERROR
An unexpected error occurred.
const rclcpp::Logger logger_
Logger associated with the parent ROS 2 node.
State observer for a particular behavior tree object that writes introspection and debugging informat...
const char * toStr(const ActionNodeErrorCode &err)
Convert the action error code to string.
Powerful tooling for incorporating behavior trees for task development.