AutoAPMS
Streamlining behaviors in ROS 2
Loading...
Searching...
No Matches
run_tree_node.cpp
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// 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#include <signal.h>
16
17#include <chrono>
18
19#include "auto_apms_behavior_tree/executor/executor_base.hpp"
20#include "auto_apms_behavior_tree_core/builder.hpp"
21#include "auto_apms_util/logging.hpp"
22#include "auto_apms_util/string.hpp"
23#include "auto_apms_util/yaml.hpp"
24#include "rclcpp/rclcpp.hpp"
25
26sig_atomic_t volatile termination_requested = 0;
27
28using namespace std::chrono_literals;
29using namespace auto_apms_behavior_tree;
30
31int main(int argc, char ** argv)
32{
33 const std::vector<std::string> args_vector = rclcpp::remove_ros_arguments(argc, argv);
34
35 bool print_help = false;
36 if (args_vector.size() > 1) {
37 const std::string & arg = args_vector[1];
38 print_help = "-h" == arg || "--help" == arg;
39 }
40 if (print_help || args_vector.size() < 2) {
41 std::cerr << "run_tree_node: The program accepts: \n\t1.) YAML representation of NodeRegistrationOptions encoded "
42 "in a string.\n\t2.) Optional: YAML map of specific node port values "
43 "encoded in a string.\n";
44 std::cerr << "Usage: run_tree_node <registration_options> [<port_values>]\n";
45 return EXIT_FAILURE;
46 }
47
48 // Ensure that rclcpp is not shut down before the tree has been halted (on destruction) and all pending actions have
49 // been successfully canceled
50 rclcpp::init(argc, argv, rclcpp::InitOptions(), rclcpp::SignalHandlerOptions::SigTerm);
51 signal(SIGINT, [](int /*sig*/) { termination_requested = 1; });
52 rclcpp::Node::SharedPtr node_ptr = std::make_shared<rclcpp::Node>("run_tree_node");
53
54 core::NodeRegistrationOptions registration_options;
55 try {
56 registration_options = core::NodeRegistrationOptions::decode(auto_apms_util::trimWhitespaces(args_vector[1]));
57 } catch (std::exception & e) {
58 RCLCPP_ERROR(node_ptr->get_logger(), "ERROR interpreting argument registration_options: %s", e.what());
59 return EXIT_FAILURE;
60 }
61
63 if (args_vector.size() > 2) {
64 try {
65 port_values =
66 YAML::Load(auto_apms_util::trimWhitespaces(args_vector[2])).as<std::map<std::string, std::string>>();
67 } catch (std::exception & e) {
68 RCLCPP_ERROR(node_ptr->get_logger(), "ERROR interpreting argument port_values: %s", e.what());
69 return EXIT_FAILURE;
70 }
71 }
72
73 TreeExecutorBase executor(node_ptr);
74 core::TreeBuilder builder(
75 node_ptr, executor.getTreeNodeWaitablesCallbackGroupPtr(), executor.getTreeNodeWaitablesExecutorPtr());
76 try {
77 builder.newTree(std::string("Tree_") + node_ptr->get_name())
78 .makeRoot()
79 .insertNode(node_ptr->get_name(), registration_options)
80 .setPorts(port_values);
81 } catch (const std::exception & e) {
82 RCLCPP_ERROR(node_ptr->get_logger(), "ERROR inserting tree node: %s", e.what());
83 return EXIT_FAILURE;
84 }
85
86 RCLCPP_DEBUG(node_ptr->get_logger(), "Creating a tree with a single node:\n%s", builder.writeToString().c_str());
87
88 std::shared_future<TreeExecutorBase::ExecutionResult> future =
89 executor.startExecution([&builder](TreeBlackboardSharedPtr bb) { return builder.instantiate(bb); });
90
91 const std::chrono::duration<double> termination_timeout(2);
92 rclcpp::Time termination_start;
93 bool termination_started = false;
94 try {
95 while (rclcpp::spin_until_future_complete(node_ptr, future, std::chrono::milliseconds(250)) !=
96 rclcpp::FutureReturnCode::SUCCESS) {
97 if (termination_started) {
98 if (node_ptr->now() - termination_start > termination_timeout) {
99 RCLCPP_WARN(node_ptr->get_logger(), "Termination took too long. Aborted.");
100 return EXIT_FAILURE;
101 }
102 } else if (termination_requested) {
103 termination_start = node_ptr->now();
104 executor.setControlCommand(TreeExecutorBase::ControlCommand::TERMINATE);
105 termination_started = true;
106 RCLCPP_INFO(node_ptr->get_logger(), "Terminating tree execution...");
107 }
108 }
109 } catch (const std::exception & e) {
110 RCLCPP_ERROR(node_ptr->get_logger(), "ERROR during behavior tree execution: %s", e.what());
111 return EXIT_FAILURE;
112 }
113
114 // To prevent a deadlock, throw if future isn't ready at this point. However, this shouldn't happen.
115 if (future.wait_for(std::chrono::seconds(0)) != std::future_status::ready) {
116 throw std::logic_error("Future object is not ready.");
117 }
118
119 RCLCPP_INFO(node_ptr->get_logger(), "Finished with status %s.", toStr(future.get()).c_str());
120 rclcpp::shutdown();
121
122 return EXIT_SUCCESS;
123}
Base class that offers basic functionality for executing behavior trees.
@ TERMINATE
Halt the currently executing tree and terminate the execution routine.
Class for configuring and instantiating behavior trees.
Definition builder.hpp:55
std::map< std::string, std::string > PortValues
Mapping of port names and its respective value encoded as string.
std::string trimWhitespaces(const std::string &str)
Trim whitespaces from both ends of a string.
Definition string.cpp:84
Powerful tooling for incorporating behavior trees for task development.
Definition behavior.hpp:32
Parameters for loading and registering a behavior tree node class from a shared library using e....