AutoAPMS
Resilient Robot Mission Management
Loading...
Searching...
No Matches
generate_node_model.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
15#include <filesystem>
16#include <fstream>
17#include <iostream>
18
19#include "behaviortree_cpp/xml_parsing.h"
20#include "class_loader/multi_library_class_loader.hpp"
23
24using namespace auto_apms_behavior_tree;
25
26int main(int argc, char** argv)
27{
28 if (argc < 3)
29 {
30 std::cerr << "generate_node_model: Missing inputs! The program requires: \n\t1.) The path to the node plugin "
31 "manifest.\n\t2.) The xml file to store the model.\n";
32 std::cerr << "Usage: generate_node_model <manifest_file> <output_file>.\n";
33 return EXIT_FAILURE;
34 }
35
36 try
37 {
38 const std::string manifest_file{ std::filesystem::absolute(argv[1]).string() };
39 const std::filesystem::path output_file{ std::filesystem::absolute(argv[2]) };
40
41 // Ensure that arguments are not empty
42 if (manifest_file.empty())
43 {
44 throw std::runtime_error("Argument manifest_file must not be empty.");
45 }
46 if (output_file.empty())
47 {
48 throw std::runtime_error("Argument output_file must not be empty.");
49 }
50
51 // Ensure correct extensions
52 if (output_file.extension() != ".xml")
53 {
54 throw std::runtime_error("Output file '" + output_file.string() + "' has wrong extension. Must be '.xml'.");
55 }
56
57 rclcpp::init(argc, argv);
58 auto node_ptr = std::make_shared<rclcpp::Node>("_generate_node_model_temp_node");
59 auto_apms_core::exposeToDebugLogging(node_ptr->get_logger());
60
61 // Create manifest
62 BT::BehaviorTreeFactory factory;
63 const auto manifest = NodeManifest::fromFile(manifest_file);
64
70 auto class_loader = class_loader::MultiLibraryClassLoader{ false };
71 for (const auto& [node_name, params] : manifest.getInternalMap())
72 {
73 if (params.library.empty())
74 {
75 // Library path is a required field now
76 throw std::runtime_error("Parameters for node '" + node_name + "' do not specify a library path.");
77 }
78 const auto& library_path = params.library;
79
80 // Make sure that library is loaded
81 if (!class_loader.isLibraryAvailable(library_path))
82 {
83 try
84 {
85 class_loader.loadLibrary(library_path);
86 }
87 catch (const std::exception& e)
88 {
89 throw std::runtime_error("Failed to load library '" + library_path + "': " + e.what() + ".");
90 }
91 }
92
93 // Look if the class we search for is actually present in the library.
94 const std::string factory_classname =
95 "auto_apms_behavior_tree::NodeRegistrationFactory<" + params.class_name + ">";
96 const auto classes = class_loader.getAvailableClassesForLibrary<NodeRegistrationInterface>(library_path);
97 if (std::find(classes.begin(), classes.end(), factory_classname) == classes.end())
98 {
99 throw std::runtime_error{ "Node '" + node_name + " (Class: " + params.class_name +
100 ")' cannot be loaded, because factory class '" + factory_classname +
101 "' couldn't be found. Check that the class name is spelled correctly and registered "
102 "by calling auto_apms_behavior_tree_register_nodes() in the CMakeLists.txt of the "
103 "corresponding package. Also make sure that you called the "
104 "AUTO_APMS_BEHAVIOR_TREE_REGISTER_NODE macro in the source file." };
105 }
106
107 RCLCPP_DEBUG(node_ptr->get_logger(), "Loading behavior tree node '%s' (Class: %s) from library %s.",
108 node_name.c_str(), params.class_name.c_str(), library_path.c_str());
109
110 try
111 {
112 const auto plugin_instance = class_loader.createUniqueInstance<NodeRegistrationInterface>(factory_classname);
113 RosNodeContext ros_node_context(node_ptr, params); // Values don't matter when not instantiating it
114 plugin_instance->registerWithBehaviorTreeFactory(factory, node_name, &ros_node_context);
115 }
116 catch (const std::exception& e)
117 {
118 throw std::runtime_error("Failed to load and register node '" + node_name + " (Class: " + params.class_name +
119 ")': " + e.what() + ".");
120 }
121 }
122
123 // Generate and write node model
124 std::ofstream out_stream{ output_file };
125 if (out_stream.is_open())
126 {
127 out_stream << BT::writeTreeNodesModelXML(factory);
128 out_stream.close();
129 }
130 else
131 {
132 throw std::runtime_error("Error opening node model output file '" + output_file.string() + "'.");
133 }
134 }
135 catch (const std::exception& e)
136 {
137 std::cerr << "ERROR (generate_node_model): " << e.what() << "\n";
138 return EXIT_FAILURE;
139 }
140
141 return EXIT_SUCCESS;
142}
static NodeManifest fromFile(const std::string &file_path)
Create a node plugin manifest from a file.
int main(int argc, char **argv)
void exposeToDebugLogging(const rclcpp::Logger &logger)
Definition logging.cpp:22