AutoAPMS
Streamlining behaviors in ROS 2
Loading...
Searching...
No Matches
run_behavior.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_node.hpp"
20#include "auto_apms_util/logging.hpp"
21#include "auto_apms_util/string.hpp"
22#include "rclcpp/rclcpp.hpp"
23
24sig_atomic_t volatile termination_requested = 0;
25
26using namespace auto_apms_behavior_tree;
27
28int main(int argc, char ** argv)
29{
30 const std::vector<std::string> args_vector = rclcpp::remove_ros_arguments(argc, argv);
31
32 bool print_help = false;
33 std::string build_request = "";
34 std::string entrypoint = "";
35 core::NodeManifest node_manifest;
36 if (args_vector.size() > 1) {
37 const std::string & arg = args_vector[1];
38 print_help = "-h" == arg || "--help" == arg;
39 if (!print_help) build_request = auto_apms_util::trimWhitespaces(arg);
40 }
41 if (args_vector.size() > 2) {
42 const std::string & arg = args_vector[2];
43 print_help = "-h" == arg || "--help" == arg;
44 if (!print_help) entrypoint = auto_apms_util::trimWhitespaces(arg);
45 }
46 if (args_vector.size() > 3) {
47 const std::string & arg = args_vector[3];
48 print_help = "-h" == arg || "--help" == arg;
49 if (!print_help) node_manifest = core::NodeManifest::decode(auto_apms_util::trimWhitespaces(arg));
50 }
51 if (print_help) {
52 std::cerr << "run_behavior: The program accepts: \n\t1.) String specifying the behavior tree "
53 "build request to be passed to the build handler loaded by the underlying tree executor node. If "
54 "empty, build handler must be able to handle that.\n\t2.) Optional string specifying the single point "
55 "of entry for behavior execution.\n\t3.) Optional encoded node manifest specifying the behavior tree "
56 "nodes required for behavior execution.\n";
57 std::cerr << "Usage: run_behavior [<build_request>] [<entrypoint>] [<node_manifest>]\n";
58 return EXIT_FAILURE;
59 }
60
61 // Ensure that rclcpp is not shut down before the tree has been halted (on destruction) and all pending actions have
62 // been successfully canceled
63 rclcpp::init(argc, argv, rclcpp::InitOptions(), rclcpp::SignalHandlerOptions::SigTerm);
64 signal(SIGINT, [](int /*sig*/) { termination_requested = 1; });
65
66 // Create executor node
67 rclcpp::NodeOptions opt;
68 TreeExecutorNodeOptions executor_opt(opt);
69 TreeExecutorNode executor("run_behavior", executor_opt);
70 const rclcpp::Logger logger = executor.getNodePtr()->get_logger();
71
72 // Start tree execution
73 std::shared_future<TreeExecutorBase::ExecutionResult> future =
74 executor.startExecution(build_request, entrypoint, node_manifest);
75 RCLCPP_INFO(logger, "Executing tree '%s'.", executor.getTreeName().c_str());
76
77 const std::chrono::duration<double> termination_timeout(3);
78 rclcpp::Time termination_start;
79 bool termination_started = false;
80 while (
81 rclcpp::spin_until_future_complete(executor.get_node_base_interface(), future, std::chrono::milliseconds(250)) !=
82 rclcpp::FutureReturnCode::SUCCESS) {
83 if (termination_started) {
84 if (executor.getNodePtr()->now() - termination_start > termination_timeout) {
85 RCLCPP_WARN(logger, "Termination took too long. Aborted.");
86 return EXIT_FAILURE;
87 }
88 } else if (termination_requested) {
89 termination_start = executor.getNodePtr()->now();
90 executor.setControlCommand(TreeExecutorBase::ControlCommand::TERMINATE);
91 termination_started = true;
92 RCLCPP_INFO(logger, "Terminating tree execution...");
93 }
94 }
95
96 // To prevent a deadlock, throw if future isn't ready at this point. However, this shouldn't happen.
97 if (future.wait_for(std::chrono::seconds(0)) != std::future_status::ready) {
98 throw std::logic_error("Future object is not ready.");
99 }
100
101 RCLCPP_INFO(logger, "Finished with status %s.", toStr(future.get()).c_str());
102 rclcpp::shutdown();
103
104 return EXIT_SUCCESS;
105}
@ TERMINATE
Halt the currently executing tree and terminate the execution routine.
Configuration options for TreeExecutorNode.
Flexible ROS 2 node implementing a standardized interface for dynamically executing behavior trees.
Data structure for information about which behavior tree node plugin to load and how to configure the...
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