AutoAPMS
Resilient Robot Mission Management
Loading...
Searching...
No Matches
executor.cpp
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
16
17#include <chrono>
18
21
23{
24
25TreeExecutor::TreeExecutor(rclcpp::Node::SharedPtr node_ptr)
26 : node_ptr_(node_ptr)
27 , global_blackboard_ptr_(TreeBlackboard::create())
28 , control_command_(ControlCommand::RUN)
29 , execution_stopped_(true)
30{
31 auto_apms_core::exposeToDebugLogging(node_ptr_->get_logger());
32}
33
34std::shared_future<TreeExecutor::ExecutionResult> TreeExecutor::startExecution(CreateTreeCallback create_tree_cb)
35{
36 if (isBusy())
37 throw exceptions::TreeExecutionError("Cannot start execution with tree '" + getTreeName() +
38 "' currently executing.");
39
40 try
41 {
42 tree_ptr_.reset(new Tree(create_tree_cb(global_blackboard_ptr_)));
43 }
44 catch (const std::exception& e)
45 {
46 throw exceptions::TreeBuildError("Cannot start execution because creating the tree failed: " +
47 std::string(e.what()));
48 }
49
50 groot2_publisher_ptr_.reset();
51 groot2_publisher_ptr_ = std::make_unique<BT::Groot2Publisher>(*tree_ptr_, executor_params_.groot2_port);
52 state_observer_ptr_.reset();
53 state_observer_ptr_ = std::make_unique<BTStateObserver>(*tree_ptr_, node_ptr_->get_logger());
54 state_observer_ptr_->enableTransitionToIdle(false);
55
56 /* Start execution timer */
57
58 // Reset state variables
59 prev_execution_state_ = getExecutionState();
60 control_command_ = ControlCommand::RUN;
61 termination_reason_ = "";
62 execution_stopped_ = true;
63
64 // Create promise for asnychronous execution and configure termination callback
65 auto promise_ptr = std::make_shared<std::promise<ExecutionResult>>();
66 TerminationCallback termination_callback = [this, promise_ptr](ExecutionResult result, const std::string& msg) {
67 RCLCPP_DEBUG(node_ptr_->get_logger(), "Terminating behavior tree execution from state %s. Reason: %s.",
68 toStr(getExecutionState()).c_str(), msg.c_str());
69 onTermination(result); // is evaluated before the timer is cancelled, which means the execution state has not
70 // changed yet during the callback and can be evaluated to inspect the terminal state.
71 promise_ptr->set_value(result);
72 execution_timer_ptr_->cancel();
73 tree_ptr_.reset(); // Release the memory allocated by the tree
74 };
75
76 execution_timer_ptr_ =
77 node_ptr_->create_wall_timer(std::chrono::duration<double>(executor_params_.tick_rate),
78 [this, termination_callback]() { execution_routine_(termination_callback); });
79 return promise_ptr->get_future();
80}
81
83{
84 return execution_timer_ptr_ && !execution_timer_ptr_->is_canceled();
85}
86
88{
89 if (isBusy())
90 {
91 if (!tree_ptr_)
92 throw std::logic_error("tree_ptr_ cannot be nullptr when execution is started.");
93 if (tree_ptr_->rootNode()->status() == BT::NodeStatus::IDLE)
94 {
95 // The root node being IDLE here means that the tree hasn't been ticked yet since its creation or was halted
96 return execution_stopped_ ? ExecutionState::STARTING : ExecutionState::HALTED;
97 }
98 return execution_stopped_ ? ExecutionState::PAUSED : ExecutionState::RUNNING;
99 }
101}
102
104{
105 if (tree_ptr_)
106 return tree_ptr_->subtrees[0]->tree_ID;
107 return "NO_TREE_NAME";
108}
109
110void TreeExecutor::execution_routine_(TerminationCallback termination_callback)
111{
112 const ExecutionState this_execution_state = getExecutionState();
113 if (prev_execution_state_ != this_execution_state)
114 {
115 RCLCPP_DEBUG(node_ptr_->get_logger(), "Executor for tree '%s' changed state from '%s' to '%s'.",
116 getTreeName().c_str(), toStr(prev_execution_state_).c_str(), toStr(this_execution_state).c_str());
117 prev_execution_state_ = this_execution_state;
118 }
119
120 /* Evaluate control command */
121
122 execution_stopped_ = false;
123 bool do_on_tick = true;
124 switch (control_command_)
125 {
127 execution_stopped_ = true;
128 return;
130 if (this_execution_state == ExecutionState::STARTING)
131 {
132 // Evaluate initial tick callback before ticking for the first time since the timer has been created
133 if (!onInitialTick())
134 {
135 do_on_tick = false;
136 termination_reason_ = "onInitialTick() returned false";
137 }
138 }
139 // Evaluate tick callback everytime before actually ticking.
140 // This also happens the first time except if onInitialTick() returned false
141 if (do_on_tick)
142 {
143 if (onTick())
144 {
145 break;
146 }
147 else
148 {
149 termination_reason_ = "onTick() returned false";
150 }
151 }
152
153 // Fall through to terminate if any of the callbacks returned false
154 [[fallthrough]];
156 if (this_execution_state == ExecutionState::HALTED)
157 {
158 termination_callback(ExecutionResult::TERMINATED_PREMATURELY,
159 termination_reason_.empty() ? "Terminated by control command" : termination_reason_);
160 return;
161 }
162
163 // Fall through to halt tree before termination
164 [[fallthrough]];
166 // Check if already halted
167 if (this_execution_state != ExecutionState::HALTED)
168 {
169 try
170 {
171 tree_ptr_->haltTree();
172 }
173 catch (const std::exception& e)
174 {
175 termination_callback(ExecutionResult::ERROR, "Error during haltTree() on command " + toStr(control_command_) +
176 ": " + std::string(e.what()));
177 }
178 }
179 return;
180 default:
181 throw std::logic_error("Handling control command " + std::to_string(static_cast<int>(control_command_)) + " '" +
182 toStr(control_command_) + "' is not implemented.");
183 }
184
185 /* Tick the tree instance */
186
187 state_observer_ptr_->setLogging(executor_params_.state_change_logger);
188 BT::NodeStatus bt_status = BT::NodeStatus::IDLE;
189 try
190 {
191 // It is important to tick EXACTLY once to prevent loops induced by BT nodes from blocking
192 bt_status = tree_ptr_->tickExactlyOnce();
193 }
194 catch (const std::exception& e)
195 {
196 std::string msg = "Ran into an exception during tick: " + std::string(e.what());
197 try
198 {
199 tree_ptr_->haltTree(); // Try to halt tree before aborting
200 }
201 catch (const std::exception& e)
202 {
203 msg += "\nDuring haltTree(), another exception occured: " + std::string(e.what());
204 }
205 termination_callback(ExecutionResult::ERROR, msg);
206 return;
207 }
208
209 /* Continue execution until tree is not running anymore */
210
211 if (bt_status == BT::NodeStatus::RUNNING)
212 return;
213
214 /* Determine how to handle the behavior tree execution result */
215
216 if (!(bt_status == BT::NodeStatus::SUCCESS || bt_status == BT::NodeStatus::FAILURE))
217 {
218 throw std::logic_error("bt_status is " + BT::toStr(bt_status) +
219 ". Must be one of SUCCESS or FAILURE at this point.");
220 }
221 const bool success = bt_status == BT::NodeStatus::SUCCESS;
222 switch (onTreeExit(success))
223 {
225 termination_callback(success ? ExecutionResult::TREE_SUCCEEDED : ExecutionResult::TREE_FAILED,
226 "Terminated on tree result " + BT::toStr(bt_status));
227 return;
229 control_command_ = ControlCommand::RUN;
230 return;
231 }
232
233 throw std::logic_error("Execution routine is not intended to proceed to this statement.");
234}
235
236bool TreeExecutor::onInitialTick()
237{
238 return true;
239}
240
241bool TreeExecutor::onTick()
242{
243 return true;
244}
245
246TreeExecutor::TreeExitBehavior TreeExecutor::onTreeExit(bool /*success*/)
247{
249}
250
251void TreeExecutor::onTermination(const ExecutionResult& /*result*/)
252{
253}
254
256{
257 control_command_ = cmd;
258}
259
261{
262 executor_params_ = p;
263}
264
265rclcpp::Node::SharedPtr TreeExecutor::getNodePtr()
266{
267 return node_ptr_;
268}
269
270rclcpp::node_interfaces::NodeBaseInterface::SharedPtr TreeExecutor::get_node_base_interface()
271{
272 return node_ptr_->get_node_base_interface();
273}
274
276{
277 return global_blackboard_ptr_;
278}
279
284
286{
287 return *state_observer_ptr_;
288}
289
291{
292 switch (state)
293 {
295 return "IDLE";
297 return "STARTING";
299 return "RUNNING";
301 return "PAUSED";
303 return "HALTED";
304 }
305 return "undefined";
306}
307
309{
310 switch (cmd)
311 {
313 return "RUN";
315 return "PAUSE";
317 return "HALT";
319 return "TERMINATE";
320 }
321 return "undefined";
322}
323
325{
326 switch (behavior)
327 {
329 return "TERMINATE";
331 return "RESTART";
332 }
333 return "undefined";
334}
335
337{
338 switch (result)
339 {
341 return "TREE_SUCCEEDED";
343 return "TREE_FAILED";
345 return "TERMINATED_PREMATURELY";
347 return "ERROR";
348 }
349 return "undefined";
350}
351
352} // namespace auto_apms_behavior_tree
void setExecutorParameters(const ExecutorParams &p)
Definition executor.cpp:260
executor_params::Params ExecutorParams
Definition executor.hpp:66
std::function< Tree(TreeBlackboardSharedPtr)> CreateTreeCallback
Definition executor.hpp:65
void setControlCommand(ControlCommand cmd)
Definition executor.cpp:255
TreeExecutor(rclcpp::Node::SharedPtr node_ptr)
Definition executor.cpp:25
rclcpp::Node::SharedPtr getNodePtr()
Get a pointer to the internal ROS2 node instance.
Definition executor.cpp:265
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.
Definition executor.cpp:270
TreeBlackboardSharedPtr getGlobalBlackboardPtr()
Get a pointer to the global blackboard instance.
Definition executor.cpp:275
std::shared_future< ExecutionResult > startExecution(CreateTreeCallback create_tree_cb)
Definition executor.cpp:34
std::string toStr(TreeExecutor::ExecutionState state)
Definition executor.cpp:290
std::shared_ptr< TreeBlackboard > TreeBlackboardSharedPtr
BT::Blackboard TreeBlackboard
void exposeToDebugLogging(const rclcpp::Logger &logger)
Definition logging.cpp:22