AutoAPMS
Resilient Robot Mission Management
Loading...
Searching...
No Matches
create_node_model.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// 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
15#include <filesystem>
16#include <fstream>
17#include <iostream>
18
19#include "auto_apms_behavior_tree_core/node/node_manifest.hpp"
20#include "auto_apms_behavior_tree_core/node/node_registration_interface.hpp"
21#include "auto_apms_util/logging.hpp"
22#include "auto_apms_util/string.hpp"
23#include "behaviortree_cpp/xml_parsing.h"
24#include "class_loader/class_loader.hpp"
25
26using namespace auto_apms_behavior_tree;
27
28int main(int argc, char ** argv)
29{
30 if (argc < 4) {
31 std::cerr << "create_node_model: Missing inputs! The program requires: \n\t1.) The path to the node plugin "
32 "manifest.\n\t2. The exhaustive list of libraries to be loaded by ClassLoader (Separated by "
33 "';').\n\t3.) The xml file to "
34 "store the model.\n";
35 std::cerr << "Usage: create_node_model <manifest_file> <library_paths> <output_file>.\n";
36 return EXIT_FAILURE;
37 }
38
39 try {
40 const std::filesystem::path manifest_file = std::filesystem::absolute(argv[1]);
41 const std::vector<std::string> library_paths = auto_apms_util::splitString(argv[2], ";");
42 const std::filesystem::path output_file = std::filesystem::absolute(auto_apms_util::trimWhitespaces(argv[3]));
43
44 // Ensure that arguments are not empty
45 if (manifest_file.empty()) {
46 throw std::runtime_error("Argument manifest_file must not be empty.");
47 }
48 if (library_paths.empty()) {
49 throw std::runtime_error("Argument library_paths must not be empty.");
50 }
51 if (output_file.empty()) {
52 throw std::runtime_error("Argument output_file must not be empty.");
53 }
54
55 // Ensure correct extensions
56 if (output_file.extension() != ".xml") {
57 throw std::runtime_error("Output file '" + output_file.string() + "' has wrong extension. Must be '.xml'.");
58 }
59
60 const rclcpp::Logger logger = rclcpp::get_logger("create_node_model__" + output_file.stem().string());
62
63 BT::BehaviorTreeFactory factory;
64 const auto manifest = core::NodeManifest::fromFile(manifest_file.string());
65
70
71 // Instantiate loaders for all libraries in library_paths (We don't use class_loader::MultiLibraryClassLoader
72 // because we want to keep track of the libraries that the nodes come from for debugging purposes)
73 std::vector<std::unique_ptr<class_loader::ClassLoader>> class_loaders;
74 for (const auto & path : library_paths) class_loaders.push_back(std::make_unique<class_loader::ClassLoader>(path));
75
76 // Walk manifest and register all plugins with BT::BehaviorTreeFactory
77 for (const auto & [node_name, params] : manifest.map()) {
78 const std::string required_class_name =
79 "auto_apms_behavior_tree::core::NodeRegistrationTemplate<" + params.class_name + ">";
80
81 class_loader::ClassLoader * loader = nullptr;
82 for (const auto & l : class_loaders) {
83 if (l->isClassAvailable<core::NodeRegistrationInterface>(required_class_name)) loader = l.get();
84 }
85
86 if (!loader) {
87 throw std::runtime_error(
88 "Node '" + node_name + " (Class: " + params.class_name +
89 ")' cannot be registered, because the required registration class '" + required_class_name +
90 "' couldn't be found. Check that the class name is spelled correctly and "
91 "the node is declared by calling auto_apms_behavior_tree_declare_nodes() in the CMakeLists.txt of the "
92 "corresponding package. Also make sure that you called the "
93 "AUTO_APMS_BEHAVIOR_TREE_DECLARE_NODE macro in the source file.");
94 }
95
96 RCLCPP_DEBUG(
97 logger, "Declared behavior tree node '%s' (Class: %s) from library %s.", node_name.c_str(),
98 params.class_name.c_str(), loader->getLibraryPath().c_str());
99
100 try {
101 const auto plugin_instance = loader->createUniqueInstance<core::NodeRegistrationInterface>(required_class_name);
102 rclcpp::Node::SharedPtr node = nullptr;
103 rclcpp::CallbackGroup::SharedPtr group = nullptr;
104 rclcpp::executors::SingleThreadedExecutor::SharedPtr executor = nullptr;
105 core::RosNodeContext ros_node_context(
106 node, group, executor, params); // Values don't matter when not instantiating it
107 plugin_instance->registerWithBehaviorTreeFactory(factory, node_name, &ros_node_context);
108 } catch (const std::exception & e) {
109 throw std::runtime_error(
110 "Failed to register node '" + node_name + " (Class: " + params.class_name + ")': " + e.what());
111 }
112 }
113
114 // Generate and write node model
115 std::ofstream out_stream(output_file);
116 if (out_stream.is_open()) {
117 out_stream << BT::writeTreeNodesModelXML(factory);
118 out_stream.close();
119 } else {
120 throw std::runtime_error("Error opening node model output file '" + output_file.string() + "'.");
121 }
122 } catch (const std::exception & e) {
123 std::cerr << "ERROR (create_node_model): " << e.what() << "\n";
124 return EXIT_FAILURE;
125 }
126
127 return EXIT_SUCCESS;
128}
Additional parameters specific to ROS 2 determined at runtime by TreeBuilder.
std::string trimWhitespaces(const std::string &str)
Trim whitespaces from both ends of a string.
Definition string.cpp:84
std::vector< std::string > splitString(const std::string &str, const std::string &delimiter, bool remove_empty=true)
Split a string into multiple tokens using a specific delimiter string (Delimiter may consist of multi...
Definition string.cpp:24
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