AutoAPMS
Resilient Robot Mission Management
Loading...
Searching...
No Matches
tree_resource.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 "auto_apms_behavior_tree_core/tree/tree_resource.hpp"
16
17#include <tinyxml2.h>
18
19#include <filesystem>
20
21#include "ament_index_cpp/get_resource.hpp"
22#include "auto_apms_behavior_tree_core/definitions.hpp"
23#include "auto_apms_behavior_tree_core/exceptions.hpp"
24#include "auto_apms_behavior_tree_core/tree/tree_document.hpp"
25#include "auto_apms_util/resource.hpp"
26#include "auto_apms_util/string.hpp"
27
29{
30
31TreeResourceIdentity::TreeResourceIdentity(const std::string & identity)
32{
33 std::vector<std::string> tokens = auto_apms_util::splitString(identity, "::", false);
34 if (tokens.size() == 1) {
35 tokens.insert(tokens.begin(), "");
36 tokens.push_back("");
37 }
38 if (tokens.size() != 3) {
39 throw auto_apms_util::exceptions::ResourceIdentityFormatError(
40 "Identity string '" + identity + "' has wrong format. Number of string tokens separated by '::' must be 3.");
41 }
42 package_name = tokens[0];
43 file_stem = tokens[1];
44 tree_name = tokens[2];
45 if (file_stem.empty() && tree_name.empty()) {
46 throw auto_apms_util::exceptions::ResourceIdentityFormatError(
47 "Behavior tree resource identity string '" + identity +
48 "' has wrong format. It's not allowed to omit both <tree_file_stem> and <tree_name>.");
49 }
50}
51
52TreeResourceIdentity::TreeResourceIdentity(const char * identity) : TreeResourceIdentity(std::string(identity)) {}
53
54bool TreeResourceIdentity::operator==(const TreeResourceIdentity & other) const { return str() == other.str(); }
55
56bool TreeResourceIdentity::operator<(const TreeResourceIdentity & other) const { return str() < other.str(); }
57
58std::string TreeResourceIdentity::str() const { return package_name + "::" + file_stem + "::" + tree_name; }
59
60bool TreeResourceIdentity::empty() const { return package_name.empty() && file_stem.empty() && tree_name.empty(); }
61
62TreeResource::TreeResource(const Identity & identity) : identity_(identity)
63{
64 if (identity.empty()) {
65 throw auto_apms_util::exceptions::ResourceIdentityFormatError("Cannot create TreeResource with empty identity.");
66 }
67
68 std::set<std::string> search_packages;
69 if (!identity.package_name.empty()) {
70 search_packages.insert(identity.package_name);
71 } else {
72 search_packages =
73 auto_apms_util::getPackagesWithResourceType(_AUTO_APMS_BEHAVIOR_TREE_CORE__RESOURCE_TYPE_NAME__TREE);
74 }
75
76 size_t matching_count = 0;
77 std::string matching_package_name;
78 std::string matching_tree_file_path;
79 std::vector<std::string> matching_node_manifest_file_paths;
80 for (const auto & p : search_packages) {
81 std::string content;
82 std::string base_path;
83 if (ament_index_cpp::get_resource(
84 _AUTO_APMS_BEHAVIOR_TREE_CORE__RESOURCE_TYPE_NAME__TREE, p, content, &base_path)) {
85 for (const auto & line : auto_apms_util::splitString(content, "\n")) {
86 const std::vector<std::string> parts = auto_apms_util::splitString(line, "|", false);
87 if (parts.size() != 4) {
88 throw auto_apms_util::exceptions::ResourceError(
89 "Invalid behavior tree resource file (Package: '" + p + "'). Invalid line: " + line + ".");
90 }
91 const std::string & found_tree_file_stem = parts[0];
92 const std::vector<std::string> found_tree_names = auto_apms_util::splitString(parts[1], ";");
93
94 // Determine if resource is matching
95 if (identity.tree_name.empty()) {
96 if (found_tree_file_stem != identity.file_stem) {
97 continue;
98 }
99 } else if (identity.file_stem.empty()) {
100 if (!auto_apms_util::contains(found_tree_names, identity.tree_name)) {
101 continue;
102 }
103 } else if (
104 found_tree_file_stem != identity.file_stem ||
105 !auto_apms_util::contains(found_tree_names, identity.tree_name)) {
106 continue;
107 }
108
109 matching_count++;
110 matching_package_name = p;
111 matching_tree_file_path = base_path + "/" + parts[2];
112 for (const std::string & path : auto_apms_util::splitString(parts[3], ";")) {
113 matching_node_manifest_file_paths.push_back(
114 std::filesystem::path(path).is_absolute() ? path : (base_path + "/" + path));
115 }
116 }
117 }
118 }
119
120 if (matching_count == 0) {
121 throw auto_apms_util::exceptions::ResourceError(
122 "No behavior tree file with identity '" + identity.str() + "' was registered.");
123 }
124 if (matching_count > 1) {
125 throw auto_apms_util::exceptions::ResourceError(
126 "Resource identity '" + identity.str() + "' is ambiguous. You must be more precise.");
127 }
128
129 // Set the members since we've found the matching resource
130 package_name_ = matching_package_name;
131 tree_file_path_ = matching_tree_file_path;
132 node_manifest_file_paths_ = matching_node_manifest_file_paths;
133
134 // Verify that the file is ok
135 TreeDocument doc;
136 try {
137 doc.mergeFile(tree_file_path_, true);
138 } catch (const std::exception & e) {
139 throw auto_apms_util::exceptions::ResourceError(
140 "Failed to create TreeResource with identity '" + identity.str() + "' because tree file " + tree_file_path_ +
141 " cannot be parsed: " + e.what());
142 }
143
144 // Verify that the tree <tree_name> specified by the identity string is actually present
145 if (!identity.tree_name.empty()) {
146 if (!auto_apms_util::contains(doc.getAllTreeNames(), identity.tree_name)) {
147 throw auto_apms_util::exceptions::ResourceError(
148 "Cannot create TreeResource with identity '" + identity.str() + "' because '" + identity.tree_name +
149 "' does not exist in tree file " + tree_file_path_ + ".");
150 }
151 }
152
153 // Save the root tree name if available
154 if (doc.hasRootTreeName()) {
155 doc_root_tree_name_ = doc.getRootTreeName();
156 }
157}
158
159TreeResource::TreeResource(const std::string & identity) : TreeResource(TreeResourceIdentity(identity)) {}
160
161TreeResource::TreeResource(const char * identity) : TreeResource(std::string(identity)) {}
162
163TreeResource TreeResource::selectByTreeName(const std::string & tree_name, const std::string & package_name)
164{
165 return TreeResource(package_name + "::::" + tree_name);
166}
167
168TreeResource TreeResource::selectByFileStem(const std::string & file_name, const std::string & package_name)
169{
170 return TreeResource(package_name + "::" + file_name + "::");
171}
172
173bool TreeResource::hasRootTreeName() const { return !identity_.tree_name.empty() || !doc_root_tree_name_.empty(); }
174
176{
177 if (!identity_.tree_name.empty()) return identity_.tree_name;
178
179 // If <tree_name> wasn't provided, look for root tree attribute in XML file
180 if (!doc_root_tree_name_.empty()) return doc_root_tree_name_;
181
182 // Root tree cannot be determined
183 throw auto_apms_util::exceptions::ResourceError(
184 "Cannot get root tree name of tree resource '" + identity_.str() + "'. Since there is no XML attribute named '" +
185 TreeDocument::ROOT_TREE_ATTRIBUTE_NAME +
186 "' and the resource identity doesn't specify <tree_name>, the root tree is unkown.");
187}
188
189NodeManifest TreeResource::getNodeManifest() const { return NodeManifest::fromFiles(node_manifest_file_paths_); }
190
191std::string TreeResource::getPackageName() const { return package_name_; }
192
193std::string TreeResource::getFileStem() const { return std::filesystem::path(tree_file_path_).stem(); }
194
195TreeResourceIdentity TreeResource::createIdentity(const std::string & tree_name) const
196{
197 Identity i;
198 i.package_name = package_name_;
200 i.tree_name = tree_name;
201 return i;
202}
203
204} // namespace auto_apms_behavior_tree::core
Data structure for information about which behavior tree node plugin to load and how to configure the...
static NodeManifest fromFiles(const std::vector< std::string > &paths)
Create a node plugin manifest from multiple files. They are loaded in the given order.
TreeDocument & mergeFile(const std::string &path, bool adopt_root_tree=false)
Create a tree document from a file and merge it with this one.
std::string getRootTreeName() const
Get the name of the root tree of this behavior tree resource.
static TreeResource selectByFileStem(const std::string &file_stem, const std::string &package_name="")
Find an installed behavior tree resource using a specific behavior tree XML file stem.
TreeResource(const Identity &identity)
Assemble a behavior tree resource using a TreeResourceIdentity.
std::string getPackageName() const
Get the name of the package this resource was registered by.
std::string getFileStem() const
Get the file stem of the XML file containing the tree document associated with this resource.
Identity createIdentity(const std::string &tree_name="") const
Create a valid tree resource identity string representing this resource.
static TreeResource selectByTreeName(const std::string &tree_name, const std::string &package_name="")
Find an installed behavior tree resource using a specific behavior tree name.
NodeManifest getNodeManifest() const
Get the node manifest associated with this resource.
bool hasRootTreeName() const
Determine if this behavior tree resource specifies a root tree.
bool contains(const ContainerT< ValueT, AllocatorT > &c, const ValueT &val)
Check whether a particular container structure contains a value.
Definition container.hpp:36
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
Core API for AutoAPMS's behavior tree implementation.
Definition builder.hpp:30
Struct that encapsulates the identity string for a declared behavior tree.
std::string tree_name
Name of a specific tree inside the resource's tree document.
bool empty() const
Determine whether this tree resource identity object is considered empty.
std::string file_stem
Name of the file (without extension) that contains the resource's tree document.
TreeResourceIdentity()=default
Constructor of an empty tree resource identity object.
TreeResourceIdentity(const std::string &identity)
Constructor of a behavior tree resource identity object.
std::string package_name
Name of the package that registers the resource.
std::string str() const
Create the corresponding identity string.