AutoAPMS
Streamlining behaviors in ROS 2
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// 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 "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: BehaviorResourceIdentity(identity, _AUTO_APMS_BEHAVIOR_TREE_CORE__DEFAULT_BEHAVIOR_CATEGORY__TREE)
33{
34 std::vector<std::string> tokens =
35 auto_apms_util::splitString(behavior_alias, _AUTO_APMS_BEHAVIOR_TREE_CORE__RESOURCE_IDENTITY_ALIAS_SEP, true);
36
37 if (tokens.size() == 2) {
38 file_stem = tokens[0];
39 tree_name = tokens[1];
40 } else if (tokens.size() == 1) {
41 file_stem = tokens[0];
42 tree_name = "";
43 }
44
45 if (file_stem.empty() || tree_name.empty()) {
46 throw auto_apms_util::exceptions::ResourceIdentityFormatError(
47 "Behavior tree resource identity string '" + identity +
48 "' is invalid. Both <tree_file_stem> and <tree_name> must be provided.");
49 }
50}
51
52TreeResourceIdentity::TreeResourceIdentity(const char * identity) : TreeResourceIdentity(std::string(identity)) {}
53
55{
56 // Fill the tree specific fields for the unique identity
57 const std::vector<std::string> & tokens = auto_apms_util::splitString(
58 unique_identity_.behavior_alias, _AUTO_APMS_BEHAVIOR_TREE_CORE__RESOURCE_IDENTITY_ALIAS_SEP, false);
59
60 if (tokens.size() != 2) {
61 throw auto_apms_util::exceptions::ResourceIdentityFormatError(
62 "Unique tree resource identity string '" + unique_identity_.str() +
63 "' is invalid. Behavior alias must be <tree_file_stem>" +
64 _AUTO_APMS_BEHAVIOR_TREE_CORE__RESOURCE_IDENTITY_ALIAS_SEP + "<tree_name>.");
65 }
66
67 unique_identity_.file_stem = tokens[0];
68 unique_identity_.tree_name = tokens[1];
69
70 // Verify that the file is ok
71 TreeDocument doc;
72 try {
73 doc.mergeFile(build_request_file_path_, true);
74 } catch (const std::exception & e) {
75 throw auto_apms_util::exceptions::ResourceError(
76 "Failed to create TreeResource with identity '" + unique_identity_.str() + "' because tree file " +
77 build_request_file_path_ + " cannot be parsed: " + e.what());
78 }
79
80 // Verify that the tree <tree_name> specified by the identity string is actually present
81 if (!unique_identity_.tree_name.empty()) {
82 if (!auto_apms_util::contains(doc.getAllTreeNames(), unique_identity_.tree_name)) {
83 throw auto_apms_util::exceptions::ResourceError(
84 "Cannot create TreeResource with identity '" + unique_identity_.str() + "' because '" +
85 unique_identity_.tree_name + "' does not exist in tree file " + build_request_file_path_ + ".");
86 }
87 }
88
89 // Save the root tree name if available
90 if (doc.hasRootTreeName()) {
91 doc_root_tree_name_ = doc.getRootTreeName();
92 }
93}
94
95TreeResource::TreeResource(const std::string & search_identity) : TreeResource(TreeResourceIdentity(search_identity)) {}
96
97TreeResource::TreeResource(const char * search_identity) : TreeResource(std::string(search_identity)) {}
98
100{
101 return !unique_identity_.tree_name.empty() || !doc_root_tree_name_.empty();
102}
103
105{
106 if (!unique_identity_.tree_name.empty()) return unique_identity_.tree_name;
107
108 // If <tree_name> wasn't provided, look for root tree attribute in XML file
109 if (!doc_root_tree_name_.empty()) return doc_root_tree_name_;
110
111 // Root tree cannot be determined
112 throw auto_apms_util::exceptions::ResourceError(
113 "Cannot get root tree name of tree resource '" + unique_identity_.str() +
114 "'. Since there is no XML attribute named '" + TreeDocument::ROOT_TREE_ATTRIBUTE_NAME +
115 "' and the resource identity doesn't specify <tree_name>, the root tree is unknown.");
116}
117
119{
121 i.package_name = unique_identity_.package_name;
122 i.file_stem = unique_identity_.file_stem;
123 i.tree_name = tree_name;
124 return i;
125}
126
127TreeResource TreeResource::findByTreeName(const std::string & tree_name, const std::string & package_name)
128{
129 if (tree_name.empty()) {
130 throw auto_apms_util::exceptions::ResourceError("Cannot find tree resource: tree_name must not be empty.");
131 }
132
133 std::set<std::string> search_packages;
134 if (!package_name.empty()) {
135 search_packages.insert(package_name);
136 } else {
137 search_packages =
138 auto_apms_util::getPackagesWithResourceType(_AUTO_APMS_BEHAVIOR_TREE_CORE__RESOURCE_TYPE_NAME__BEHAVIOR);
139 }
140
142 size_t matching_count = 0;
143 for (const auto & p : search_packages) {
144 std::string content;
145 std::string base_path;
146 if (ament_index_cpp::get_resource(
147 _AUTO_APMS_BEHAVIOR_TREE_CORE__RESOURCE_TYPE_NAME__BEHAVIOR, p, content, &base_path)) {
148 for (const auto & line :
149 auto_apms_util::splitString(content, _AUTO_APMS_BEHAVIOR_TREE_CORE__RESOURCE_MARKER_FILE_LINE_SEP)) {
150 const std::vector<std::string> parts = auto_apms_util::splitString(
151 line, _AUTO_APMS_BEHAVIOR_TREE_CORE__RESOURCE_MARKER_FILE_FIELD_PER_LINE_SEP, false);
152 if (parts.size() != 6) continue;
153
154 const std::string & found_category = parts[0];
155 const std::string & found_alias = parts[1];
156
157 // Only consider tree-category resources
158 if (found_category != _AUTO_APMS_BEHAVIOR_TREE_CORE__DEFAULT_BEHAVIOR_CATEGORY__TREE) continue;
159
160 // Split alias into file_stem and tree_name
161 const std::vector<std::string> alias_tokens =
162 auto_apms_util::splitString(found_alias, _AUTO_APMS_BEHAVIOR_TREE_CORE__RESOURCE_IDENTITY_ALIAS_SEP, false);
163 if (alias_tokens.size() != 2) continue;
164
165 if (alias_tokens[1] == tree_name) {
166 matching_count++;
167 match.category_name = found_category;
168 match.package_name = p;
169 match.behavior_alias = found_alias;
170 match.file_stem = alias_tokens[0];
171 match.tree_name = alias_tokens[1];
172 }
173 }
174 }
175 }
176
177 if (matching_count == 0) {
178 throw auto_apms_util::exceptions::ResourceError(
179 "No tree resource with tree name '" + tree_name + "' was found" +
180 (package_name.empty() ? "." : (" in package '" + package_name + "'.")));
181 }
182 if (matching_count > 1) {
183 throw auto_apms_util::exceptions::ResourceError(
184 "Tree name '" + tree_name + "' is ambiguous (" + std::to_string(matching_count) +
185 " matches). You must be more precise.");
186 }
187
188 return TreeResource(match);
189}
190
191TreeResource TreeResource::findByFileStem(const std::string & file_stem, const std::string & package_name)
192{
193 if (file_stem.empty()) {
194 throw auto_apms_util::exceptions::ResourceError("Cannot find tree resource: file_stem must not be empty.");
195 }
196
197 std::set<std::string> search_packages;
198 if (!package_name.empty()) {
199 search_packages.insert(package_name);
200 } else {
201 search_packages =
202 auto_apms_util::getPackagesWithResourceType(_AUTO_APMS_BEHAVIOR_TREE_CORE__RESOURCE_TYPE_NAME__BEHAVIOR);
203 }
204
206 size_t matching_count = 0;
207 for (const auto & p : search_packages) {
208 std::string content;
209 std::string base_path;
210 if (ament_index_cpp::get_resource(
211 _AUTO_APMS_BEHAVIOR_TREE_CORE__RESOURCE_TYPE_NAME__BEHAVIOR, p, content, &base_path)) {
212 for (const auto & line :
213 auto_apms_util::splitString(content, _AUTO_APMS_BEHAVIOR_TREE_CORE__RESOURCE_MARKER_FILE_LINE_SEP)) {
214 const std::vector<std::string> parts = auto_apms_util::splitString(
215 line, _AUTO_APMS_BEHAVIOR_TREE_CORE__RESOURCE_MARKER_FILE_FIELD_PER_LINE_SEP, false);
216 if (parts.size() != 6) continue;
217
218 const std::string & found_category = parts[0];
219 const std::string & found_alias = parts[1];
220
221 // Only consider tree-category resources
222 if (found_category != _AUTO_APMS_BEHAVIOR_TREE_CORE__DEFAULT_BEHAVIOR_CATEGORY__TREE) continue;
223
224 // Split alias into file_stem and tree_name
225 const std::vector<std::string> alias_tokens =
226 auto_apms_util::splitString(found_alias, _AUTO_APMS_BEHAVIOR_TREE_CORE__RESOURCE_IDENTITY_ALIAS_SEP, false);
227 if (alias_tokens.size() != 2) continue;
228
229 if (alias_tokens[0] == file_stem) {
230 matching_count++;
231 match.category_name = found_category;
232 match.package_name = p;
233 match.behavior_alias = found_alias;
234 match.file_stem = alias_tokens[0];
235 match.tree_name = alias_tokens[1];
236 }
237 }
238 }
239 }
240
241 if (matching_count == 0) {
242 throw auto_apms_util::exceptions::ResourceError(
243 "No tree resource with file stem '" + file_stem + "' was found" +
244 (package_name.empty() ? "." : (" in package '" + package_name + "'.")));
245 }
246 if (matching_count > 1) {
247 throw auto_apms_util::exceptions::ResourceError(
248 "File stem '" + file_stem + "' is ambiguous (" + std::to_string(matching_count) +
249 " matches). You must be more precise.");
250 }
251
252 return TreeResource(match);
253}
254
255} // namespace auto_apms_behavior_tree::core
std::string getRootTreeName() const
Get the name of this document's root tree.
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::vector< std::string > getAllTreeNames() const
Get the names of all behavior trees inside this document.
bool hasRootTreeName() const
Determine if this document specifies which of its trees is the root tree.
TreeResourceIdentity createIdentityForTree(const std::string &tree_name="") const
Create a valid identity string for a specific behavior tree of this resource.
std::string getRootTreeName() const
Get the name of the root tree of this behavior tree resource.
static TreeResource findByTreeName(const std::string &tree_name, const std::string &package_name="")
Find an installed behavior tree resource using a specific behavior tree name.
static TreeResource findByFileStem(const std::string &file_stem, const std::string &package_name="")
Find an installed behavior tree resource using an XML file stem.
TreeResource(const TreeResourceIdentity &search_identity)
Assemble a behavior tree resource using a TreeResourceIdentity.
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
std::set< std::string > getPackagesWithResourceType(const std::string &resource_type, const std::set< std::string > &exclude_packages={})
Get a list of all package names that register a certain type of ament_index resources.
Definition resource.cpp:28
Core API for AutoAPMS's behavior tree implementation.
Definition behavior.hpp:32
std::string category_name
Name of the category this behavior resource belongs to.
Definition behavior.hpp:134
BehaviorResourceIdentity(const std::string &identity)
Constructor of a behavior resource identity object.
Definition behavior.cpp:28
std::string behavior_alias
Alias for a single registered behavior.
Definition behavior.hpp:138
std::string package_name
Name of the package that registers the behavior resource.
Definition behavior.hpp:136
Struct that encapsulates the identity string for a registered behavior tree.
std::string tree_name
Name of a specific tree inside the resource's tree document.
std::string file_stem
Name of the file (without extension) that contains the resource's tree document.
TreeResourceIdentity()=default
Constructor of an empty behavior tree resource identity object.
TreeResourceIdentity(const std::string &identity)
Constructor of a tree resource identity object.