AutoAPMS
Resilient Robot Mission Management
Loading...
Searching...
No Matches
node_manifest.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/node/node_manifest.hpp"
16
17#include <fstream>
18
19#include "auto_apms_behavior_tree_core/exceptions.hpp"
20#include "auto_apms_util/resource.hpp"
21#include "auto_apms_util/string.hpp"
22
24{
25
27
28NodeManifest NodeManifest::fromFiles(const std::vector<std::string> & paths)
29{
30 NodeManifest manifest;
31 for (const auto & path : paths) {
32 try {
33 manifest.merge(fromFile(path));
34 } catch (const std::exception & e) {
35 throw exceptions::NodeManifestError("Error creating node manifest from multiple files: " + std::string(e.what()));
36 }
37 }
38 return manifest;
39}
40
42{
43 const auto tokens = auto_apms_util::splitString(identity, "::", false);
44 std::string package_name = "";
45 std::string file_stem;
46 switch (tokens.size()) {
47 case 1:
48 file_stem = tokens[0];
49 break;
50 case 2:
51 package_name = tokens[0];
52 file_stem = tokens[1];
53 break;
54 default:
55 throw auto_apms_util::exceptions::ResourceIdentityFormatError(
56 "Node manifest resource identity string '" + identity +
57 "' has wrong format. Must be '<package_name>::<file_stem>'.");
58 }
59
60 std::set<std::string> search_packages;
61 if (!package_name.empty()) {
62 search_packages.insert(package_name);
63 } else {
64 search_packages =
65 auto_apms_util::getPackagesWithResourceType(_AUTO_APMS_BEHAVIOR_TREE_CORE__RESOURCE_TYPE_NAME__NODE_MANIFEST);
66 }
67
68 std::vector<std::string> matching_file_paths;
69 for (const auto & p : search_packages) {
70 std::string content;
71 std::string base_path;
72 if (ament_index_cpp::get_resource(
73 _AUTO_APMS_BEHAVIOR_TREE_CORE__RESOURCE_TYPE_NAME__NODE_MANIFEST, p, content, &base_path)) {
74 std::vector<std::string> lines = auto_apms_util::splitString(content, "\n");
75 for (const std::string & line : lines) {
76 std::vector<std::string> parts = auto_apms_util::splitString(line, "|", false);
77 if (parts.size() != 2) {
78 throw auto_apms_util::exceptions::ResourceError(
79 "Invalid node manifest resource file (Package: '" + p + "'). Invalid line: " + line + ".");
80 }
81 if (parts[0] == file_stem) {
82 matching_file_paths.push_back(base_path + "/" + parts[1]);
83 }
84 }
85 }
86 }
87
88 if (matching_file_paths.empty()) {
89 throw auto_apms_util::exceptions::ResourceError(
90 "No node manifest resource was found using identity '" + identity + "'.");
91 }
92 if (matching_file_paths.size() > 1) {
93 throw auto_apms_util::exceptions::ResourceError(
94 "There are multiple node manifest resources with metadata ID '" + file_stem + "'.");
95 }
96 return fromFile(matching_file_paths[0]);
97}
98
99void NodeManifest::toFile(const std::string & file_path) const
100{
101 std::ofstream out_stream(file_path);
102 if (out_stream.is_open()) {
103 out_stream << this->encode();
104 out_stream.close();
105 } else {
106 throw exceptions::NodeManifestError("Error opening node manifest output file '" + file_path + "'.");
107 }
108}
109
110bool NodeManifest::contains(const std::string & node_name) const { return map_.find(node_name) != map_.end(); }
111
112NodeManifest::RegistrationOptions & NodeManifest::operator[](const std::string & node_name)
113{
114 if (contains(node_name)) return map_[node_name];
115 throw std::out_of_range{
116 "Node '" + node_name + "' doesn't exist in node manifest (Size: " + std::to_string(map_.size()) +
117 "). Use the add() method to add an entry."};
118}
119
120const NodeManifest::RegistrationOptions & NodeManifest::operator[](const std::string & node_name) const
121{
122 if (contains(node_name)) return map_.at(node_name);
123 throw std::out_of_range{
124 "Node '" + node_name + "' doesn't exist in node manifest (Size: " + std::to_string(map_.size()) + ")."};
125}
126
127NodeManifest & NodeManifest::add(const std::string & node_name, const RegistrationOptions & opt)
128{
129 if (contains(node_name)) {
130 throw exceptions::NodeManifestError{
131 "Node '" + node_name + "' already exists in node manifest (Size: " + std::to_string(map_.size()) + ")."};
132 }
133 if (!opt.valid()) {
134 throw exceptions::NodeManifestError(
135 "Cannot add node '" + node_name + "' to manifest. Parameter class_name must not be empty.");
136 }
137 map_[node_name] = opt;
138 return *this;
139}
140
141NodeManifest & NodeManifest::remove(const std::string & node_name)
142{
143 if (!contains(node_name)) {
144 throw std::out_of_range{
145 "Node '" + node_name + "' doesn't exist in node manifest, so the corresponding entry cannot be removed."};
146 }
147 map_.erase(node_name);
148 return *this;
149}
150
151NodeManifest & NodeManifest::merge(const NodeManifest & other, bool replace)
152{
153 for (const auto & [node_name, params] : other.map()) {
154 if (contains(node_name)) {
155 if (replace) {
156 map_.erase(node_name);
157 } else {
158 throw exceptions::NodeManifestError(
159 "Cannot merge node manifests, because node '" + node_name +
160 "' already exists in other and argument replace is false (Won't replace existing entries).");
161 }
162 }
163 add(node_name, params);
164 }
165 return *this;
166}
167
168std::vector<std::string> NodeManifest::getNodeNames()
169{
170 std::vector<std::string> names;
171 names.reserve(map_.size());
172 for (const auto & [name, _] : map_) names.push_back(name);
173 return names;
174}
175
176size_t NodeManifest::size() const { return map_.size(); }
177
178bool NodeManifest::empty() const { return map_.empty(); }
179
180const NodeManifest::Map & NodeManifest::map() const { return map_; }
181
182} // namespace auto_apms_behavior_tree::core
std::map< std::string, NodeRegistrationOptions > Map
Mapping of a node's name and its registration parameters.
static NodeManifest fromFiles(const std::vector< std::string > &paths)
Create a node plugin manifest from multiple files. They are loaded in the given order.
size_t size() const
Get the number of behavior tree nodes this manifest holds registration options for.
NodeManifest & add(const std::string &node_name, const RegistrationOptions &opt)
Add registration options for a behavior tree node to the manifest.
bool contains(const std::string &node_name) const
Determine if a behavior tree node has been added to the manifest.
bool empty() const
Determine whether any node registration options have been added to the manifest.
NodeManifest & merge(const NodeManifest &other, bool replace=false)
Merges another NodeManifest with this one.
static NodeManifest fromResourceIdentity(const std::string &identity)
Create a node manifest from an installed resource.
const Map & map() const
Get a view of the internal map.
std::vector< std::string > getNodeNames()
Get all names of the behavior tree nodes specified by the manifest.
RegistrationOptions & operator[](const std::string &node_name)
Access the node manifest and retrieve registration options for a specific behavior tree node.
void toFile(const std::string &file_path) const
Write the node manifest to a file.
NodeManifest(const Map &map={})
Constructor of a NodeManifest data structure.
NodeManifest & remove(const std::string &node_name)
Remove registration options for a behavior tree node.
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 builder.hpp:30
bool valid() const
Verify that the options are valid (e.g. all required values are set).