AutoAPMS
Resilient Robot Mission Management
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 bool print_help = false;
34 if (argc > 1) {
35 const std::string arg(argv[1]);
36 print_help = "-h" == arg || "--help" == arg;
37 }
38 if (print_help || argc < 2) {
39 std::cerr << "run_tree_node: The program accepts: \n\t1.) YAML representation of "
40 "NodeRegistrationOptions encoded in a string.\n\t2.) Optional: YAML map of specific node port values "
41 "encoded in a string.\n";
42 std::cerr << "Usage: run_tree_node <registration_params> [<port_values>]\n";
43 return EXIT_FAILURE;
44 }
45
46 // Ensure that rclcpp is not shut down before the tree has been halted (on destruction) and all pending actions have
47 // been successfully canceled
48 rclcpp::init(argc, argv, rclcpp::InitOptions(), rclcpp::SignalHandlerOptions::SigTerm);
49 signal(SIGINT, [](int /*sig*/) { termination_requested = 1; });
50 rclcpp::Node::SharedPtr node_ptr = std::make_shared<rclcpp::Node>("run_tree_node_cpp");
51 auto_apms_util::exposeToGlobalDebugLogging(node_ptr->get_logger());
52
53 core::NodeRegistrationOptions registration_params;
54 try {
55 registration_params = core::NodeRegistrationOptions::decode(auto_apms_util::trimWhitespaces(argv[1]));
56 } catch (std::exception & e) {
57 RCLCPP_ERROR(node_ptr->get_logger(), "ERROR interpreting argument registration_params: %s", e.what());
58 return EXIT_FAILURE;
59 }
60
62 if (argc > 2) {
63 try {
64 port_values = YAML::Load(auto_apms_util::trimWhitespaces(argv[2])).as<std::map<std::string, std::string>>();
65 } catch (std::exception & e) {
66 RCLCPP_ERROR(node_ptr->get_logger(), "ERROR interpreting argument port_values: %s", e.what());
67 return EXIT_FAILURE;
68 }
69 }
70
71 TreeExecutorBase executor(node_ptr);
72 core::TreeBuilder builder(
73 node_ptr, executor.getTreeNodeWaitablesCallbackGroupPtr(), executor.getTreeNodeWaitablesExecutorPtr());
74 try {
75 builder.newTree("RunTreeNodeCPP")
76 .makeRoot()
77 .insertNode(registration_params.class_name, registration_params)
78 .setPorts(port_values);
79 } catch (const std::exception & e) {
80 RCLCPP_ERROR(node_ptr->get_logger(), "ERROR inserting tree node: %s", e.what());
81 return EXIT_FAILURE;
82 }
83
84 RCLCPP_DEBUG(node_ptr->get_logger(), "Creating a tree with a single node:\n%s", builder.writeToString().c_str());
85
86 std::shared_future<TreeExecutorBase::ExecutionResult> future =
87 executor.startExecution([&builder](TreeBlackboardSharedPtr bb) { return builder.instantiate(bb); });
88
89 const std::chrono::duration<double> termination_timeout(2);
90 rclcpp::Time termination_start;
91 bool termination_started = false;
92 try {
93 while (rclcpp::spin_until_future_complete(node_ptr, future, std::chrono::milliseconds(250)) !=
94 rclcpp::FutureReturnCode::SUCCESS) {
95 if (termination_started) {
96 if (node_ptr->now() - termination_start > termination_timeout) {
97 RCLCPP_WARN(node_ptr->get_logger(), "Termination took too long. Aborted.");
98 return EXIT_FAILURE;
99 }
100 } else if (termination_requested) {
101 termination_start = node_ptr->now();
102 executor.setControlCommand(TreeExecutorBase::ControlCommand::TERMINATE);
103 termination_started = true;
104 RCLCPP_INFO(node_ptr->get_logger(), "Terminating tree execution...");
105 }
106 }
107 } catch (const std::exception & e) {
108 RCLCPP_ERROR(node_ptr->get_logger(), "ERROR during behavior tree execution: %s", e.what());
109 return EXIT_FAILURE;
110 }
111
112 // To prevent a deadlock, throw if future isn't ready at this point. However, this shouldn't happen.
113 if (future.wait_for(std::chrono::seconds(0)) != std::future_status::ready) {
114 throw std::logic_error("Future object is not ready.");
115 }
116
117 RCLCPP_INFO(node_ptr->get_logger(), "Finished with status %s.", toStr(future.get()).c_str());
118 rclcpp::shutdown();
119
120 return EXIT_SUCCESS;
121}
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
void exposeToGlobalDebugLogging(const rclcpp::Logger &logger)
Enable ROS 2 debug logging, if the C preprocessor flag _AUTO_APMS_DEBUG_LOGGING is set.
Definition logging.cpp:25
Useful tooling for incorporating behavior trees for task development.
Definition builder.hpp:30
Parameters for loading and registering a behavior tree node class from a shared library using e....
std::string class_name
Fully qualified name of the behavior tree node plugin class.