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)
40 if (!tree_node_waitables_callback_group_ptr_) {
41 tree_node_waitables_callback_group_ptr_ =
42 node_ptr_->create_callback_group(rclcpp::CallbackGroupType::MutuallyExclusive,
false);
46 tree_node_waitables_executor_ptr_->add_callback_group(
51 TreeConstructor make_tree,
double tick_rate_sec,
int groot2_port)
54 throw exceptions::TreeExecutorError(
55 "Cannot start execution with tree '" +
getTreeName() +
"' currently executing.");
58 TreeBlackboardSharedPtr main_tree_bb_ptr = TreeBlackboard::create(global_blackboard_ptr_);
60 tree_ptr_.reset(
new Tree(make_tree(main_tree_bb_ptr)));
61 }
catch (
const std::exception & e) {
62 throw exceptions::TreeBuildError(
63 "Cannot start execution because creating the tree failed: " + std::string(e.what()));
67 groot2_publisher_ptr_.reset();
68 if (groot2_port != -1) {
70 groot2_publisher_ptr_ = std::make_unique<BT::Groot2Publisher>(*tree_ptr_, groot2_port);
71 }
catch (
const std::exception & e) {
72 throw exceptions::TreeExecutorError(
73 "Failed to initialize Groot2 publisher with port " + std::to_string(groot2_port) +
": " + e.what());
78 state_observer_ptr_.reset();
79 state_observer_ptr_ = std::make_unique<TreeStateObserver>(*tree_ptr_,
logger_);
80 state_observer_ptr_->enableTransitionToIdle(
false);
87 termination_reason_ =
"";
88 execution_stopped_ =
true;
91 auto promise_ptr = std::make_shared<std::promise<ExecutionResult>>();
92 TerminationCallback termination_callback = [
this, promise_ptr](
ExecutionResult result,
const std::string & msg) {
96 RCLCPP_ERROR(
logger_,
"Termination reason: %s", msg.c_str());
98 RCLCPP_INFO(
logger_,
"Termination reason: %s", msg.c_str());
102 promise_ptr->set_value(result);
103 execution_timer_ptr_->cancel();
108 const std::chrono::nanoseconds period =
109 std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::duration<double>(tick_rate_sec));
110 execution_timer_ptr_ =
node_ptr_->create_wall_timer(period, [
this, period, termination_callback]() {
112 tree_node_waitables_executor_ptr_->spin_all(period);
115 tick_callback_(termination_callback);
117 return promise_ptr->get_future();
120void TreeExecutorBase::tick_callback_(TerminationCallback termination_callback)
123 if (prev_execution_state_ != this_execution_state) {
125 logger_,
"Executor for tree '%s' changed state from '%s' to '%s'.",
getTreeName().c_str(),
126 toStr(prev_execution_state_).c_str(), toStr(this_execution_state).c_str());
127 prev_execution_state_ = this_execution_state;
132 execution_stopped_ =
false;
133 bool do_on_tick =
true;
134 switch (control_command_) {
136 execution_stopped_ =
true;
143 termination_reason_ =
"onInitialTick() returned false.";
152 termination_reason_ =
"onTick() returned false.";
160 termination_callback(
162 termination_reason_.empty() ?
"Control command was set to TERMINATE." : termination_reason_);
171#ifdef AUTO_APMS_BEHAVIOR_TREE__EXECUTOR_THROW_ON_TICK_ERROR
172 tree_ptr_->haltTree();
175 tree_ptr_->haltTree();
176 }
catch (
const std::exception & e) {
177 termination_callback(
179 "Error during haltTree() on command " +
toStr(control_command_) +
": " + std::string(e.what()));
185 throw std::logic_error(
186 "Handling control command " + std::to_string(
static_cast<int>(control_command_)) +
" '" +
187 toStr(control_command_) +
"' is not implemented.");
192 BT::NodeStatus bt_status = BT::NodeStatus::IDLE;
193#ifdef AUTO_APMS_BEHAVIOR_TREE__EXECUTOR_THROW_ON_TICK_ERROR
194 bt_status = tree_ptr_->tickExactlyOnce();
198 bt_status = tree_ptr_->tickExactlyOnce();
199 }
catch (
const std::exception & e) {
200 std::string msg =
"Ran into an exception during tick: " + std::string(e.what());
202 tree_ptr_->haltTree();
203 }
catch (
const std::exception & e) {
204 msg +=
"\nDuring haltTree(), another exception occurred: " + std::string(e.what());
216 if (bt_status == BT::NodeStatus::RUNNING)
return;
220 if (!(bt_status == BT::NodeStatus::SUCCESS || bt_status == BT::NodeStatus::FAILURE)) {
221 throw std::logic_error(
222 "bt_status is " + BT::toStr(bt_status) +
". Must be one of SUCCESS or FAILURE at this point.");
224 const bool success = bt_status == BT::NodeStatus::SUCCESS;
227 termination_callback(
229 "Terminated on tree result " + BT::toStr(bt_status) +
".");
236 throw std::logic_error(
"Execution routine is not intended to proceed to this statement.");
259 if (!tree_ptr_)
throw std::logic_error(
"tree_ptr_ cannot be nullptr when execution is started.");
260 if (tree_ptr_->rootNode()->status() == BT::NodeStatus::IDLE) {
271 if (tree_ptr_)
return tree_ptr_->subtrees[0]->tree_ID;
272 return "NO_TREE_NAME";
281 if (!state_observer_ptr_) {
282 throw exceptions::TreeExecutorError(
"Cannot get state observer because executor is not busy.");
284 return *state_observer_ptr_;
291 return node_ptr_->get_node_base_interface();
296 return tree_node_waitables_callback_group_ptr_;
301 return tree_node_waitables_executor_ptr_;
351 return "TREE_SUCCEEDED";
353 return "TREE_FAILED";
355 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.
void clearGlobalBlackboard()
Reset the global blackboard and clear all entries.
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.
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...
void exposeToGlobalDebugLogging(const rclcpp::Logger &logger)
Enable ROS 2 debug logging, if the C preprocessor flag _AUTO_APMS_DEBUG_LOGGING is set.
const char * toStr(const ActionNodeErrorCode &err)
Convert the action error code to string.
Useful tooling for incorporating behavior trees for task development.