15#include "auto_apms_behavior_tree_core/tree/tree_document.hpp"
21#include "ament_index_cpp/get_package_share_directory.hpp"
22#include "auto_apms_behavior_tree_core/builder.hpp"
23#include "auto_apms_behavior_tree_core/exceptions.hpp"
24#include "auto_apms_behavior_tree_core/node/node_model_type.hpp"
25#include "auto_apms_behavior_tree_core/tree/tree_resource.hpp"
26#include "auto_apms_util/container.hpp"
27#include "auto_apms_util/logging.hpp"
28#include "auto_apms_util/string.hpp"
29#include "behaviortree_cpp/xml_parsing.h"
34std::vector<std::string> getAllTreeNamesImpl(
const tinyxml2::XMLDocument & doc)
36 std::vector<std::string> names;
37 if (
const tinyxml2::XMLElement * root = doc.RootElement()) {
38 if (strcmp(root->Name(), TreeDocument::ROOT_ELEMENT_NAME) == 0) {
40 for (
const tinyxml2::XMLElement * child = root->FirstChildElement(); child !=
nullptr;
41 child = child->NextSiblingElement()) {
42 if (strcmp(TreeDocument::TREE_ELEMENT_NAME, child->Name()) == 0) {
43 if (
const char * name = child->Attribute(TreeDocument::TREE_NAME_ATTRIBUTE_NAME)) {
44 names.push_back(name);
46 throw exceptions::TreeDocumentError(
47 "Cannot get tree name, because required attribute '" +
48 std::string(TreeDocument::TREE_NAME_ATTRIBUTE_NAME) +
"' is missing.");
52 }
else if (strcmp(root->Name(), TreeDocument::TREE_ELEMENT_NAME) == 0) {
54 if (
const char * name = root->Attribute(TreeDocument::TREE_NAME_ATTRIBUTE_NAME)) names.push_back(name);
64 throw exceptions::TreeDocumentError(
"Cannot create an instance of NodeElement with doc_ptr=nullptr.");
67 throw exceptions::TreeDocumentError(
"Cannot create an instance of NodeElement with ele_ptr=nullptr.");
70 NodeModelMap::const_iterator it =
model.find(
ele_ptr_->Name());
71 std::vector<NodePortInfo> port_infos_str_vec;
72 if (it !=
model.end()) port_infos_str_vec = it->second.port_infos;
79 if (!port_infos_str_vec.empty()) {
80 for (const NodePortInfo & port_info : port_infos_str_vec) {
81 port_names_.push_back(port_info.port_name);
82 if (!port_info.port_default.empty()) port_default_values_[port_info.port_name] = port_info.port_default;
88 for (
const auto & [name, val] : port_default_values_) {
89 if (existing_port_values.find(name) == existing_port_values.end()) {
90 ele_ptr_->SetAttribute(name.c_str(), val.c_str());
98 XMLElement * other_ele = other.
ele_ptr_;
101 other_ele = other_ele->DeepClone(
doc_ptr_)->ToElement();
103 XMLElement * prev =
ele_ptr_->PreviousSiblingElement();
105 ele_ptr_->Parent()->InsertAfterChild(
ele_ptr_->PreviousSibling(), other_ele);
107 ele_ptr_->Parent()->InsertFirstChild(other_ele);
114 port_names_ = other.port_names_;
115 port_default_values_ = other.port_default_values_;
121bool TreeDocument::NodeElement::operator!=(
const NodeElement & other)
const {
return !this->operator==(other); }
124 const std::string & name,
const NodeElement * before_this)
126 if (
const std::set<std::string> names =
doc_ptr_->getRegisteredNodeNames(
true); names.find(name) == names.end()) {
127 throw exceptions::TreeDocumentError(
128 "Cannot insert unknown node <" + name +
129 ">. Before inserting a new node, the associated document must register the corresponding behavior tree "
130 "node. Consider using a signature of insertNode() that does this automatically.");
132 XMLElement * ele =
doc_ptr_->NewElement(name.c_str());
133 return insertBeforeImpl(before_this, ele);
141 if (
const std::set<std::string> names =
doc_ptr_->getRegisteredNodeNames(
false); names.find(name) == names.end()) {
148 const std::string & tree_name,
const NodeElement * before_this)
150 if (!
doc_ptr_->hasTreeName(tree_name)) {
151 throw exceptions::TreeDocumentError(
152 "Cannot insert subtree node for tree '" + tree_name +
"' because no tree with that name exists.");
155 ele.
ele_ptr_->SetAttribute(TREE_NAME_ATTRIBUTE_NAME, tree_name.c_str());
161 const std::string tree_name = tree.
getName();
162 if (
doc_ptr_->hasTreeName(tree_name)) {
169 if (existing_tree != tree) {
170 throw exceptions::TreeDocumentError(
171 "Cannot insert subtree node using the provided tree element, because another tree with name '" + tree_name +
172 "' already exists.");
176 const std::string existing_xml = existing_tree.
writeToString();
178 if (existing_xml != incoming_xml) {
179 throw exceptions::TreeDocumentError(
180 "Cannot insert subtree node using the provided tree element, because another tree with name '" + tree_name +
181 "' already exists with different content.");
195 const XMLElement * root_child = tree.
ele_ptr_->FirstChildElement();
197 throw exceptions::TreeDocumentError(
200 if (root_child->NextSibling()) {
201 throw exceptions::TreeDocumentError(
202 "Cannot insert tree element '" + tree.
getFullyQualifiedName() +
"' because it has more than one child node.");
208 const TreeDocument & doc,
const std::string & tree_name,
const NodeElement * before_this,
bool auto_register_nodes)
210 const std::vector<std::string> other_tree_names = doc.
getAllTreeNames();
211 if (other_tree_names.empty()) {
212 throw exceptions::TreeDocumentError(
213 "Cannot insert tree '" + tree_name +
"' because document has no <" + TREE_ELEMENT_NAME +
"> elements.");
216 throw exceptions::TreeDocumentError(
217 "Cannot insert tree '" + tree_name +
"' because document doesn't specify a tree with that name.");
220 if (auto_register_nodes) {
228 std::set<std::string> required_tree_names = {tree_name};
234 doc.DeepCopy(&temp_doc);
235 const TreeElement other_tree(&temp_doc, temp_doc.getXMLElementForTreeWithName(tree_name));
237 collect_dependency_tree_names = [&required_tree_names, &collect_dependency_tree_names](
const NodeElement & ele) {
238 if (ele.getRegistrationName() == SUBTREE_ELEMENT_NAME) {
239 if (
const char * name = ele.ele_ptr_->Attribute(TREE_NAME_ATTRIBUTE_NAME)) {
241 required_tree_names.insert(name);
244 ele.doc_ptr_->getTree(name).deepApplyConst(collect_dependency_tree_names);
246 throw exceptions::TreeDocumentError(
"Subtree element has no name attribute.");
256 return available_names.find(ele.getRegistrationName()) == available_names.end();
258 for (
const std::string & name : required_tree_names) {
259 const NodeElement ele(&temp_doc, temp_doc.getXMLElementForTreeWithName(name)->FirstChildElement());
260 if (
const std::vector<NodeElement> found = ele.
deepApplyConst(apply); !found.empty()) {
261 std::vector<std::string> names;
263 throw exceptions::TreeDocumentError(
264 "Cannot insert tree '" + tree_name +
"' because the following nodes found in tree '" + name +
265 "' are unknown to the builder:\n\t- " + auto_apms_util::join(names,
"\n\t- "));
271 required_tree_names.erase(tree_name);
272 for (
const std::string & name : other_tree_names) {
273 if (required_tree_names.find(name) == required_tree_names.end()) {
278 doc_ptr_->mergeTreeDocument(
static_cast<const XMLDocument &
>(temp_doc),
false);
281 return insertBeforeImpl(
282 before_this, doc.getXMLElementForTreeWithName(tree_name)->FirstChildElement()->DeepClone(
doc_ptr_)->ToElement());
292 const std::string & tree_str,
const std::string & tree_name,
const NodeElement * before_this)
300 const std::string & tree_str,
const NodeElement * before_this)
308 const std::string & path,
const std::string & tree_name,
const NodeElement * before_this)
316 const std::string & path,
const NodeElement * before_this)
325 bool auto_register_nodes)
328 insert_doc.
mergeFile(resource.build_request_file_path_);
341 const std::string & registration_name,
const std::string & instance_name,
bool deep_search)
const
346 if (registration_name.empty())
return ele.getName() == instance_name;
347 if (instance_name.empty())
return ele.getRegistrationName() == registration_name;
348 return ele.getRegistrationName() == registration_name && ele.getName() == instance_name;
352 if (
const std::vector<NodeElement> found =
deepApplyConst(apply); !found.empty())
return found[0];
354 for (
auto child : *
this) {
355 if (apply(child))
return child;
360 throw exceptions::TreeDocumentError(
361 "Cannot find a child node that matches the search arguments (registration_name: '" + registration_name +
366 const std::string & registration_name,
const std::string & instance_name,
bool deep_search)
368 XMLElement * found =
getFirstNode(registration_name, instance_name, deep_search).ele_ptr_;
369 found->Parent()->DeleteChild(found);
384 for (
const tinyxml2::XMLAttribute * attr =
ele_ptr_->FirstAttribute(); attr !=
nullptr; attr = attr->Next()) {
386 values[attr_name] = attr->Value();
395 std::vector<std::string> unknown_keys;
396 for (
const auto & [key, _] : port_values) {
399 if (!unknown_keys.empty()) {
400 throw exceptions::TreeDocumentError(
401 "Cannot set ports. According to the node model, the following ports are not implemented by '" +
402 std::string(
ele_ptr_->Name()) +
"': [ " + auto_apms_util::join(unknown_keys,
", ") +
" ].");
406 for (
const auto & [key, val] : port_values) {
407 ele_ptr_->SetAttribute(key.c_str(), val.c_str());
414 for (
const std::string & name : port_names_) {
415 PortValues::const_iterator it = port_default_values_.find(name);
416 if (it != port_default_values_.end()) {
417 ele_ptr_->SetAttribute(it->first.c_str(), it->second.c_str());
419 ele_ptr_->DeleteAttribute(name.c_str());
427 ele_ptr_->SetAttribute(BT::toStr(type).c_str(), script.
str().c_str());
433 ele_ptr_->SetAttribute(BT::toStr(type).c_str(), script.
str().c_str());
439 ele_ptr_->SetAttribute(NODE_INSTANCE_NAME_ATTRIBUTE_NAME, instance_name.c_str());
447 if (
const char * name =
ele_ptr_->Attribute(NODE_INSTANCE_NAME_ATTRIBUTE_NAME))
return name;
454 std::string instance_name =
getName();
455 if (registration_name == instance_name)
return registration_name;
456 return instance_name +
" (" + registration_name +
")";
466 std::vector<NodeElement> found;
467 deepApplyImpl(*
this, apply_callback, found);
473 std::vector<NodeElement> found;
474 deepApplyImpl(*
this, apply_callback, found);
479 const NodeElement * before_this, XMLElement * add_this)
482 XMLElement * prev =
nullptr;
483 XMLElement * curr = ele_ptr_->FirstChildElement();
489 if (curr == before_this->
ele_ptr_) {
494 curr = curr->NextSiblingElement();
498 ele_ptr_->InsertAfterChild(prev, add_this);
500 throw exceptions::TreeDocumentError(
502 getFullyQualifiedName() +
".");
505 ele_ptr_->InsertFirstChild(add_this);
508 ele_ptr_->InsertEndChild(add_this);
513void TreeDocument::NodeElement::deepApplyImpl(
514 const NodeElement & parent, ConstDeepApplyCallback apply_callback, std::vector<NodeElement> & vec)
516 for (XMLElement * child = parent.ele_ptr_->FirstChildElement(); child !=
nullptr;
517 child = child->NextSiblingElement()) {
518 const NodeElement child_ele(parent.doc_ptr_, child);
521 if (apply_callback(child_ele)) vec.push_back(child_ele);
524 deepApplyImpl(child_ele, apply_callback, vec);
528void TreeDocument::NodeElement::deepApplyImpl(
529 NodeElement & parent, DeepApplyCallback apply_callback, std::vector<NodeElement> & vec)
531 for (XMLElement * child = parent.ele_ptr_->FirstChildElement(); child !=
nullptr;
532 child = child->NextSiblingElement()) {
536 if (apply_callback(child_ele)) vec.push_back(child_ele);
539 deepApplyImpl(child_ele, apply_callback, vec);
546:
doc_ptr_(doc_ptr), current_(current)
557 if (current_) current_ = current_->NextSiblingElement();
568bool TreeDocument::NodeElement::ChildIterator::operator==(
const ChildIterator & other)
const
570 return current_ == other.current_;
573bool TreeDocument::NodeElement::ChildIterator::operator!=(
const ChildIterator & other)
const
575 return current_ != other.current_;
587 if (!ele_ptr->Attribute(TREE_NAME_ATTRIBUTE_NAME)) {
588 throw exceptions::TreeDocumentError(
589 "Cannot create tree element without a '" + std::string(TREE_NAME_ATTRIBUTE_NAME) +
"' attribute.");
595 const std::string other_tree_name = other.
getName();
599 throw exceptions::TreeDocumentError(
600 "Cannot copy tree '" + other.
getName() +
"' because another tree with this name already exists.");
608 ele_ptr_->SetAttribute(TREE_NAME_ATTRIBUTE_NAME, tree_name.c_str());
614 if (
const char * name =
ele_ptr_->Attribute(TREE_NAME_ATTRIBUTE_NAME))
return name;
629 const bool is_native_node =
doc_ptr_->native_node_names_.find(name) !=
doc_ptr_->native_node_names_.end();
630 if (!is_native_node && !m.
contains(name)) {
631 if (!
doc_ptr_->registered_nodes_manifest_.contains(name)) {
632 throw exceptions::NodeManifestError(
633 "Cannot assemble the required node manifest for tree '" +
getName() +
634 "' since there are no registration options for node '" + name +
"'.");
636 m.
add(name,
doc_ptr_->registered_nodes_manifest_[name]);
645 TreeDocument doc(
doc_ptr_->format_version_,
doc_ptr_->tree_node_loader_ptr_);
651 XMLDocument tree_doc;
652 tree_doc.InsertEndChild(
ele_ptr_->DeepClone(&tree_doc));
653 tinyxml2::XMLPrinter printer(
nullptr, remove_whitespace);
654 tree_doc.Print(&printer);
655 return printer.CStr();
659 const std::string & registration_name,
const std::string & instance_name,
bool deep_search)
674: XMLDocument(true, tinyxml2::PRESERVE_WHITESPACE),
676 native_node_names_(BT::BehaviorTreeFactory().builtinNodes()),
677 format_version_(format_version),
678 tree_node_loader_ptr_(tree_node_loader),
679 registered_nodes_manifest_(),
681 logger_(rclcpp::get_logger(LOGGER_NAME))
688 std::set<std::string> include_stack;
689 return mergeTreeDocumentImpl(other, adopt_root_tree, include_stack);
693 const XMLDocument & other,
bool adopt_root_tree, std::set<std::string> & include_stack)
695 const XMLElement * other_root = other.RootElement();
697 throw exceptions::TreeDocumentError(
"Cannot merge tree documents: other_root is nullptr.");
700 auto verify_tree_structure = [](
const XMLElement * tree_ele) {
701 const char * tree_id = tree_ele->Attribute(TREE_NAME_ATTRIBUTE_NAME);
703 throw exceptions::TreeDocumentError(
704 "Cannot merge tree document: Found a <" + std::string(TREE_ELEMENT_NAME) +
705 "> element that doesn't specify the required attribute '" + TREE_NAME_ATTRIBUTE_NAME +
"'.");
707 const XMLElement * tree_root_child = tree_ele->FirstChildElement();
708 if (!tree_root_child) {
709 throw exceptions::TreeDocumentError(
710 "Cannot merge tree document: Tree '" + std::string(tree_id) +
"' has no child nodes.");
712 if (tree_root_child->NextSibling()) {
713 throw exceptions::TreeDocumentError(
714 "Cannot merge tree document: Tree '" + std::string(tree_id) +
"' has more than one child node.");
719 auto check_duplicates = [
this](
const std::vector<std::string> & new_tree_names,
const std::string & source) {
721 if (!common.empty()) {
722 throw exceptions::TreeDocumentError(
723 "Cannot merge tree document: The following trees from " + source +
" are already defined: [ " +
724 auto_apms_util::join(common,
", ") +
" ].");
729 const std::vector<std::string> other_tree_names = getAllTreeNamesImpl(other);
731 if (strcmp(other_root->Name(), ROOT_ELEMENT_NAME) == 0) {
733 if (
const char * ver = other_root->Attribute(BTCPP_FORMAT_ATTRIBUTE_NAME)) {
734 if (std::string(ver) != format_version_) {
735 throw exceptions::TreeDocumentError(
736 "Cannot merge tree document: Format of other document (" + std::string(BTCPP_FORMAT_ATTRIBUTE_NAME) +
": " +
737 ver +
") is not compatible with this document (" + std::string(BTCPP_FORMAT_ATTRIBUTE_NAME) +
": " +
738 format_version_ +
").");
741 throw exceptions::TreeDocumentError(
742 "Cannot merge tree document: Root element of other document doesn't have required attribute '" +
743 std::string(BTCPP_FORMAT_ATTRIBUTE_NAME) +
"'.");
747 TreeDocument include_doc(format_version_, tree_node_loader_ptr_);
748 for (
const XMLElement * include_ele = other_root->FirstChildElement(INCLUDE_ELEMENT_NAME); include_ele !=
nullptr;
749 include_ele = include_ele->NextSiblingElement(INCLUDE_ELEMENT_NAME)) {
750 if (
const char * path = include_ele->Attribute(INCLUDE_PATH_ATTRIBUTE_NAME)) {
751 if (std::string(path).empty()) {
752 throw exceptions::TreeDocumentError(
753 "Cannot merge tree document: Found an <" + std::string(INCLUDE_ELEMENT_NAME) +
754 "> element that specifies an empty path in attribute '" + INCLUDE_PATH_ATTRIBUTE_NAME +
"'.");
756 std::string absolute_path = path;
757 if (
const char * ros_pkg = include_ele->Attribute(INCLUDE_ROS_PKG_ATTRIBUTE_NAME)) {
758 if (std::string(ros_pkg).empty()) {
759 throw exceptions::TreeDocumentError(
760 "Cannot merge tree document: Found an <" + std::string(INCLUDE_ELEMENT_NAME) +
761 "> element that specifies an empty ROS package name in attribute '" + INCLUDE_ROS_PKG_ATTRIBUTE_NAME +
764 if (std::filesystem::path(path).is_absolute()) {
765 throw exceptions::TreeDocumentError(
766 "Cannot merge tree document: Found an <" + std::string(INCLUDE_ELEMENT_NAME) +
767 "> element that specifies an absolute path '" + std::string(path) +
"' in attribute '" +
768 INCLUDE_PATH_ATTRIBUTE_NAME +
"' together with a ROS package name in attribute '" +
769 INCLUDE_ROS_PKG_ATTRIBUTE_NAME +
"'. Please remove the attribute " + INCLUDE_ROS_PKG_ATTRIBUTE_NAME +
770 " or use a relative path if you want to refer to a location "
771 "relative to the package's share directory.");
775 (std::filesystem::path(ament_index_cpp::get_package_share_directory(ros_pkg)) / path).
string();
776 }
catch (
const ament_index_cpp::PackageNotFoundError & e) {
777 throw exceptions::TreeDocumentError(
778 "Cannot merge tree document: Found an <" + std::string(INCLUDE_ELEMENT_NAME) +
779 "> element that specifies a non-existing ROS package '" + std::string(ros_pkg) +
"' in attribute '" +
780 INCLUDE_ROS_PKG_ATTRIBUTE_NAME +
"'.");
786 include_doc.mergeFileImpl(absolute_path,
false, include_stack);
787 }
catch (
const std::exception & e) {
788 throw exceptions::TreeDocumentError(
789 "Cannot merge tree document: Failed to include file '" + absolute_path +
"': " + e.what());
792 throw exceptions::TreeDocumentError(
793 "Cannot merge tree document: Found an <" + std::string(INCLUDE_ELEMENT_NAME) +
794 "> element that doesn't specify the required attribute '" + INCLUDE_PATH_ATTRIBUTE_NAME +
"'.");
799 check_duplicates(include_doc.getAllTreeNames(),
"included files");
802 for (
const XMLElement * child = include_doc.RootElement()->FirstChildElement(TREE_ELEMENT_NAME); child !=
nullptr;
803 child = child->NextSiblingElement(TREE_ELEMENT_NAME)) {
808 RootElement()->InsertEndChild(child->DeepClone(
this));
812 check_duplicates(other_tree_names,
"the merged document");
815 for (
const XMLElement * child = other_root->FirstChildElement(TREE_ELEMENT_NAME); child !=
nullptr;
816 child = child->NextSiblingElement(TREE_ELEMENT_NAME)) {
817 verify_tree_structure(child);
820 RootElement()->InsertEndChild(child->DeepClone(
this));
823 if (adopt_root_tree) {
826 if (
const char * name = other_root->Attribute(ROOT_TREE_ATTRIBUTE_NAME))
setRootTreeName(name);
828 }
else if (strcmp(other_root->Name(), TREE_ELEMENT_NAME) == 0) {
831 verify_tree_structure(other_root);
834 check_duplicates(other_tree_names,
"the merged document");
837 RootElement()->InsertEndChild(other_root->DeepClone(
this));
839 throw exceptions::TreeDocumentError(
840 "Cannot merge tree document: Root element of other document must either be <" + std::string(ROOT_ELEMENT_NAME) +
841 "> or <" + TREE_ELEMENT_NAME +
">.");
844 if (adopt_root_tree && other_tree_names.size() == 1) {
854 const TreeDocument & other,
bool adopt_root_tree,
bool auto_register_nodes)
856 if (auto_register_nodes) {
862 std::set<std::string> include_stack;
863 return mergeTreeDocumentImpl(
static_cast<const XMLDocument &
>(other), adopt_root_tree, include_stack);
868 XMLDocument other_doc;
869 if (other_doc.Parse(tree_str.c_str()) != tinyxml2::XMLError::XML_SUCCESS) {
870 throw exceptions::TreeDocumentError(
"Cannot merge tree document from string: " + std::string(other_doc.ErrorStr()));
872 std::set<std::string> include_stack;
873 return mergeTreeDocumentImpl(other_doc, adopt_root_tree, include_stack);
878 std::set<std::string> include_stack;
879 return mergeFileImpl(path, adopt_root_tree, include_stack);
883 const std::string & path,
bool adopt_root_tree, std::set<std::string> & include_stack)
886 std::string canonical_path;
888 canonical_path = std::filesystem::canonical(path).string();
889 }
catch (
const std::filesystem::filesystem_error &) {
892 canonical_path = path;
896 if (include_stack.count(canonical_path) > 0) {
897 throw exceptions::TreeDocumentError(
898 "Cannot merge tree document: Circular include detected for file '" + path +
"'.");
902 include_stack.insert(canonical_path);
904 XMLDocument other_doc;
905 if (other_doc.LoadFile(path.c_str()) != tinyxml2::XMLError::XML_SUCCESS) {
906 throw exceptions::TreeDocumentError(
"Cannot create tree document from file " + path +
": " + other_doc.ErrorStr());
909 mergeTreeDocumentImpl(other_doc, adopt_root_tree, include_stack);
912 include_stack.erase(canonical_path);
917 const TreeResource & resource,
bool adopt_root_tree,
bool auto_register_nodes)
919 if (auto_register_nodes) {
925 return mergeFile(resource.build_request_file_path_, adopt_root_tree);
930 XMLDocument tree_doc;
931 tree_doc.InsertEndChild(tree.
ele_ptr_->DeepClone(&tree_doc));
932 if (auto_register_nodes) {
944 if (tree_name.empty()) {
945 throw exceptions::TreeDocumentError(
"Cannot create a new tree with an empty name");
948 throw exceptions::TreeDocumentError(
949 "Cannot create a new tree with name '" + tree_name +
"' because it already exists.");
951 TreeDocument::XMLElement * new_ele = RootElement()->InsertNewChildElement(TreeDocument::TREE_ELEMENT_NAME);
952 new_ele->SetAttribute(TreeDocument::TREE_NAME_ATTRIBUTE_NAME, tree_name.c_str());
962 const TreeDocument & other,
const std::string & tree_name,
bool auto_register_nodes)
964 std::string name(tree_name);
968 }
else if (
const std::vector<std::string> names = other.
getAllTreeNames(); names.size() == 1) {
971 throw exceptions::TreeDocumentError(
972 "Failed to create new tree element from another document because argument tree_name was omitted and it was not "
973 "possible to determine the root tree automatically.");
983 TreeDocument new_doc(format_version_, tree_node_loader_ptr_);
990 TreeDocument new_doc(format_version_, tree_node_loader_ptr_);
996 const TreeResource & resource,
const std::string & tree_name,
bool auto_register_nodes)
998 TreeDocument new_doc(format_version_, tree_node_loader_ptr_);
999 new_doc.
mergeFile(resource.build_request_file_path_);
1001 if (auto_register_nodes) {
1018 return TreeElement(
this, getXMLElementForTreeWithName(tree_name));
1023 if (tree_name.empty()) {
1024 throw exceptions::TreeDocumentError(
"Cannot set root tree name with empty string.");
1027 throw exceptions::TreeDocumentError(
1028 "Cannot make tree with name '" + tree_name +
"' the root tree because it doesn't exist.");
1030 RootElement()->SetAttribute(ROOT_TREE_ATTRIBUTE_NAME, tree_name.c_str());
1036 if (RootElement()->Attribute(ROOT_TREE_ATTRIBUTE_NAME))
return true;
1042 if (
const auto tree_name = RootElement()->Attribute(ROOT_TREE_ATTRIBUTE_NAME))
return tree_name;
1043 throw exceptions::TreeDocumentError(
1044 "Cannot get root tree name because the document's root element has no attribute '" +
1045 std::string(ROOT_TREE_ATTRIBUTE_NAME) +
"'.");
1052 RootElement()->DeleteChild(getXMLElementForTreeWithName(tree_name));
1063 std::set<std::string> old_node_names;
1064 for (
const auto & [name, _] : registered_nodes_manifest_.map()) {
1065 old_node_names.insert(name);
1072 auto rename_recursive = [&old_node_names, &node_namespace, &sep](XMLElement * parent,
auto & self) ->
void {
1073 for (XMLElement * child = parent->FirstChildElement(); child !=
nullptr; child = child->NextSiblingElement()) {
1074 const char * name = child->Name();
1076 if (name && old_node_names.count(name) > 0) {
1077 child->SetName((node_namespace + sep + name).c_str());
1084 for (XMLElement * tree_ele = RootElement()->FirstChildElement(TREE_ELEMENT_NAME); tree_ele !=
nullptr;
1085 tree_ele = tree_ele->NextSiblingElement(TREE_ELEMENT_NAME)) {
1086 rename_recursive(tree_ele, rename_recursive);
1093 std::vector<std::pair<std::string, std::pair<BT::TreeNodeManifest, BT::NodeBuilder>>> registrations_to_update;
1094 const auto & factory_manifests = factory_.manifests();
1095 const auto & factory_builders = factory_.builders();
1096 for (
const auto & old_name : old_node_names) {
1097 const auto manifest_it = factory_manifests.find(old_name);
1098 const auto builder_it = factory_builders.find(old_name);
1099 if (manifest_it == factory_manifests.end() || builder_it == factory_builders.end()) {
1101 throw exceptions::TreeDocumentError(
1102 "Internal error while applying node namespace: Node '" + old_name +
1103 "' not found in BehaviorTreeFactory registrations.");
1106 registrations_to_update.emplace_back(old_name, std::make_pair(manifest_it->second, builder_it->second));
1110 for (
const auto & [old_name, manifest_builder_pair] : registrations_to_update) {
1111 factory_.unregisterBuilder(old_name);
1112 BT::TreeNodeManifest new_manifest = manifest_builder_pair.first;
1113 new_manifest.registration_ID = node_namespace + sep + old_name;
1114 factory_.registerBuilder(new_manifest, manifest_builder_pair.second);
1123 std::set<std::string> all_registration_names;
1124 for (
const auto & [name, _] : tree_node_manifest.
map()) all_registration_names.insert(name);
1125 if (
const std::set<std::string> common =
1128 throw exceptions::TreeDocumentError(
1129 "Found reserved node registration names in the node manifest. The following names are not allowed, because "
1130 "they refer to native behavior tree nodes: [ " +
1131 auto_apms_util::join(std::vector<std::string>(common.begin(), common.end()),
", ") +
" ].");
1134 for (
const auto & [node_name, params] : tree_node_manifest.
map()) {
1139 if (registered_nodes_manifest_.contains(node_name)) {
1142 factory_.unregisterBuilder(node_name);
1143 registered_nodes_manifest_.remove(node_name);
1146 throw exceptions::TreeDocumentError(
1147 "Tried to register node '" + node_name +
"' (Class: " + params.class_name +
1148 ") which is already known. You must make sure that the registration names are unique or explicitly allow "
1149 "overriding previously registered nodes with the same name by setting override=true.");
1154 if (!tree_node_loader_ptr_->isClassAvailable(params.class_name)) {
1155 if (all_node_classes_package_map_.find(params.class_name) == all_node_classes_package_map_.end()) {
1156 throw exceptions::TreeDocumentError(
1157 "Node '" + node_name +
" (" + params.class_name +
1158 ")' cannot be registered, because the class name is not known to the class loader. "
1159 "Make sure that it's spelled correctly and registered by calling "
1160 "auto_apms_behavior_tree_register_nodes() in the CMakeLists.txt of the "
1161 "corresponding package.");
1163 throw exceptions::TreeDocumentError(
1164 "Node '" + node_name +
" (" + params.class_name +
1165 ")' cannot be registered, because the corresponding resource belongs to excluded package '" +
1166 all_node_classes_package_map_.at(params.class_name) +
"'.");
1169 pluginlib::UniquePtr<NodeRegistrationInterface> plugin_instance;
1171 plugin_instance = tree_node_loader_ptr_->createUniqueInstance(params.class_name);
1172 }
catch (
const pluginlib::CreateClassException & e) {
1173 throw pluginlib::CreateClassException(
1174 "Failed to create an instance of node '" + node_name +
" (" + params.class_name +
1175 ")'. Remember that the AUTO_APMS_BEHAVIOR_TREE_REGISTER_NODE "
1176 "macro must be called in the source file for the node class to be discoverable. "
1182 if (plugin_instance->requiresRosNodeContext()) {
1183 if (only_non_ros_nodes_) {
1184 throw exceptions::NodeRegistrationError(
1185 "Node '" + node_name +
1186 "' relies on ROS 2 functionality but this instance only allows to use non-ROS nodes.");
1189 ros_node_wptr_.lock(), tree_node_waitables_callback_group_wptr_.lock(),
1190 tree_node_waitables_executor_wptr_.lock(), params);
1191 plugin_instance->registerWithBehaviorTreeFactory(factory_, node_name, &ros_node_context);
1194 RosNodeContext ros_node_context(
nullptr,
nullptr,
nullptr, params);
1195 plugin_instance->registerWithBehaviorTreeFactory(factory_, node_name, &ros_node_context);
1197 }
catch (
const std::exception & e) {
1198 throw exceptions::NodeRegistrationError(
1199 "Cannot register node '" + node_name +
" (" + params.class_name +
")': " + e.what() +
".");
1201 registered_nodes_manifest_.add(node_name, params);
1208 std::set<std::string> names;
1209 if (include_native) names = native_node_names_;
1210 for (
const auto & [name, _] : registered_nodes_manifest_.map()) names.insert(name);
1218 std::vector<std::string> tree_names_to_process =
1219 tree_name.empty() ?
getAllTreeNames() : std::vector<std::string>{tree_name};
1220 for (
const std::string & tree_name : tree_names_to_process) {
1221 XMLElement * ptr =
const_cast<XMLElement *
>(getXMLElementForTreeWithName(tree_name));
1231 tinyxml2::XMLElement * model_root = RootElement()->FirstChildElement(TREE_NODE_MODEL_ELEMENT_NAME);
1235 model_root = NewElement(TREE_NODE_MODEL_ELEMENT_NAME);
1236 RootElement()->InsertEndChild(model_root);
1240 for (
const auto & [node_name,
model] : model_map) {
1242 tinyxml2::XMLElement * node_element = NewElement(BT::toStr(
model.type).c_str());
1243 node_element->SetAttribute(
"ID", node_name.c_str());
1246 for (
const auto & port_info :
model.port_infos) {
1247 tinyxml2::XMLElement * port_element =
nullptr;
1250 switch (port_info.port_direction) {
1251 case BT::PortDirection::INPUT:
1252 port_element = NewElement(
"input_port");
1254 case BT::PortDirection::OUTPUT:
1255 port_element = NewElement(
"output_port");
1257 case BT::PortDirection::INOUT:
1258 port_element = NewElement(
"inout_port");
1263 port_element->SetAttribute(
"name", port_info.port_name.c_str());
1265 if (!port_info.port_type.empty()) {
1266 port_element->SetAttribute(
"type", port_info.port_type.c_str());
1269 if (port_info.port_has_default) {
1270 port_element->SetAttribute(
"default", port_info.port_default.c_str());
1274 if (!port_info.port_description.empty()) {
1275 port_element->SetText(port_info.port_description.c_str());
1278 node_element->InsertEndChild(port_element);
1281 model_root->InsertEndChild(node_element);
1289 const tinyxml2::XMLElement * root = doc.RootElement();
1291 throw exceptions::TreeDocumentError(
"Node model document has no root element.");
1293 if (
const char * ver = root->Attribute(TreeDocument::BTCPP_FORMAT_ATTRIBUTE_NAME)) {
1294 const std::string expected_format = TreeDocument::BTCPP_FORMAT_DEFAULT_VERSION;
1295 if (std::string(ver) != expected_format) {
1296 throw exceptions::TreeDocumentError(
1297 "Cannot parse node model document: Format of model document (" +
1298 std::string(TreeDocument::BTCPP_FORMAT_ATTRIBUTE_NAME) +
": " + ver +
1299 ") doesn't comply with the expected format (" + std::string(TreeDocument::BTCPP_FORMAT_ATTRIBUTE_NAME) +
": " +
1300 expected_format +
").");
1303 throw exceptions::TreeDocumentError(
1304 "Cannot parse node model document: Root element of model document doesn't have required attribute '" +
1305 std::string(TreeDocument::BTCPP_FORMAT_ATTRIBUTE_NAME) +
"'.");
1307 tinyxml2::XMLElement * model_ele = doc.RootElement()->FirstChildElement(TreeDocument::TREE_NODE_MODEL_ELEMENT_NAME);
1309 throw exceptions::TreeDocumentError(
1310 "Element <" + std::string(TreeDocument::TREE_NODE_MODEL_ELEMENT_NAME) +
1311 "> doesn't exist in node model document.");
1315 for (tinyxml2::XMLElement * ele = model_ele->FirstChildElement(); ele !=
nullptr; ele = ele->NextSiblingElement()) {
1316 const char * node_name = ele->Attribute(
"ID");
1318 throw exceptions::TreeDocumentError(
1319 "Element '" + std::string(ele->Name()) +
"' in node model document is missing the required attribute 'ID'");
1322 model.type = BT::convertFromString<BT::NodeType>(ele->Name());
1323 for (
const tinyxml2::XMLElement * port_ele = ele->FirstChildElement(); port_ele !=
nullptr;
1324 port_ele = port_ele->NextSiblingElement()) {
1325 const std::string direction = port_ele->Name();
1327 if (direction ==
"input_port") {
1329 }
else if (direction ==
"output_port") {
1331 }
else if (direction ==
"inout_port") {
1334 throw exceptions::TreeDocumentError(
1335 "Unknown port direction in node model for '" + std::string(node_name) +
"': " + direction);
1337 if (
const char * c = port_ele->Attribute(
"name")) {
1340 if (
const char * c = port_ele->Attribute(
"type")) {
1343 if (
const char * c = port_ele->Attribute(
"default")) {
1349 if (
const char * c = port_ele->GetText()) {
1352 model.port_infos.push_back(std::move(port_info));
1358 std::map<std::string, std::vector<std::string>> hidden_ports;
1359 for (
const auto & [node_name, registration_options] : manifest.
map()) {
1361 for (
const std::string & port_name : registration_options.hidden_ports) {
1362 hidden_ports[node_name].push_back(port_name);
1366 for (
const auto & [port_name, _] : registration_options.port_alias) {
1367 hidden_ports[node_name].push_back(port_name);
1372 for (
const auto & [node_name, ports_to_hide] : hidden_ports) {
1373 auto it = model_map.find(node_name);
1374 if (it == model_map.end()) {
1378 model.port_infos.erase(
1380 model.port_infos.begin(),
model.port_infos.end(),
1382 return std::find(ports_to_hide.begin(), ports_to_hide.end(), port_info.port_name) != ports_to_hide.end();
1384 model.port_infos.end());
1393 std::string model_xml;
1395 model_xml = BT::writeTreeNodesModelXML(factory_, include_native);
1396 }
catch (
const std::exception & e) {
1397 throw exceptions::TreeDocumentError(
"Error generating node model XML from factory: " + std::string(e.what()));
1401 tinyxml2::XMLDocument model_doc;
1402 if (model_doc.Parse(model_xml.c_str()) != tinyxml2::XMLError::XML_SUCCESS) {
1403 throw exceptions::TreeDocumentError(
1404 "Error parsing the model of the currently registered nodes: " + std::string(model_doc.ErrorStr()));
1408 return getNodeModel(model_doc, registered_nodes_manifest_);
1414 std::unordered_map<std::string, BT::NodeType> registered_nodes;
1415 for (
const auto & [node_name,
model] : model_map) {
1416 registered_nodes[node_name] =
model.type;
1420 }
catch (
const BT::RuntimeError & e) {
1421 return nonstd::make_unexpected(e.what());
1428 tinyxml2::XMLPrinter printer(
nullptr, remove_whitespace);
1430 return printer.CStr();
1436 FILE * f = std::fopen(path.c_str(),
"w");
1438 throw exceptions::TreeDocumentError(
"Failed to open file for writing: " + path);
1440 const std::size_t written = std::fwrite(xml.data(), 1, xml.size(), f);
1441 const int close_err = std::fclose(f);
1442 if (written != xml.size() || close_err != 0) {
1443 throw exceptions::TreeDocumentError(
"Failed to write tree document to file: " + path);
1451 tinyxml2::XMLElement * root_ele = NewElement(TreeDocument::ROOT_ELEMENT_NAME);
1452 root_ele->SetAttribute(TreeDocument::BTCPP_FORMAT_ATTRIBUTE_NAME, format_version_.c_str());
1453 InsertFirstChild(root_ele);
1456 if (unregister_nodes) {
1458 factory_.unregisterBuilder(node_name);
1465const TreeDocument::XMLElement * TreeDocument::getXMLElementForTreeWithName(
const std::string & tree_name)
const
1467 return getXMLElementForTreeWithNameImpl<const XMLElement *>(*
this, tree_name);
1470TreeDocument::XMLElement * TreeDocument::getXMLElementForTreeWithName(
const std::string & tree_name)
1472 return getXMLElementForTreeWithNameImpl<XMLElement *>(*
this, tree_name);
1475template <
typename ReturnT,
typename DocumentT>
1476ReturnT TreeDocument::getXMLElementForTreeWithNameImpl(DocumentT & doc,
const std::string & tree_name)
1478 if (tree_name.empty()) {
1479 throw exceptions::TreeDocumentError(
"Cannot get tree with an empty name.");
1481 if (!doc.hasTreeName(tree_name)) {
1482 throw exceptions::TreeDocumentError(
"Cannot get tree with name '" + tree_name +
"' because it doesn't exist.");
1484 auto child = doc.RootElement()->FirstChildElement(TreeDocument::TREE_ELEMENT_NAME);
1485 while (child && !child->Attribute(TreeDocument::TREE_NAME_ATTRIBUTE_NAME, tree_name.c_str())) {
1486 child = child->NextSiblingElement();
1489 throw std::logic_error(
1490 "Unexpected error trying to get tree element with name '" + tree_name +
1491 "'. Since hasTreeName() returned true, there MUST be a corresponding element.");
const NodeManifest & getNodeManifest() const
Get the node manifest associated with this resource.
Data structure for information about which behavior tree node plugin to load and how to configure the...
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.
NodeManifest & merge(const NodeManifest &other, bool replace=false)
Merges another NodeManifest with this one.
const Map & map() const
Get a view of the internal map.
A pluginlib::ClassLoader specifically for loading installed behavior tree node plugins.
Additional parameters specific to ROS 2 determined at runtime by TreeBuilder.
Class that encapsulates behavior tree script expressions.
std::string str() const
Concatenate all expressions of this instance to a single string.
Forward iterator for traversing the first-level children of a node.
ChildIterator & operator++()
Pre-increment: advance to the next sibling element.
ChildIterator()
Create a past-the-end iterator.
value_type operator*() const
Dereference the iterator to obtain a NodeElement handle for the current child.
Handle for a single node of a TreeDocument.
const TreeDocument & getParentDocument() const
Get a const view of this node's parent tree document.
const std::vector< std::string > & getPortNames() const
Get the names of the data ports implemented by the node represented by this element.
virtual std::string getRegistrationName() const
Get the name of this node given during registration representing its dynamic type.
XMLElement * getXMLElement()
Get a pointer to the underlying tinyxml2::XMLElement of this node.
std::function< bool(const NodeElement &)> ConstDeepApplyCallback
Callback invoked for every node found under another node. It cannot modify the current node.
NodeElement & resetPorts()
Delete all currently specified port values and reset with the defaults.
std::function< bool(NodeElement &)> DeepApplyCallback
Callback invoked for every node found under another node. It may modify the current node.
NodeElement & setPorts(const PortValues &port_values)
Populate the the node's data ports.
virtual NodeElement & setName(const std::string &instance_name)
Assign a name for this specific node instance.
NodeElement insertTreeFromResource(const TreeResource &resource, const std::string &tree_name, const NodeElement *before_this=nullptr, bool auto_register_nodes=true)
Concatenate a tree from one of the installed package's behavior tree resources and add its first chil...
NodeElement(TreeDocument *doc_ptr, XMLElement *ele_ptr)
Protected constructor intended for internal use only.
NodeElement insertTreeFromDocument(const TreeDocument &doc, const std::string &tree_name, const NodeElement *before_this=nullptr, bool auto_register_nodes=true)
Concatenate a tree from a document and add its first child node to the children of this node.
std::map< std::string, std::string > PortValues
Mapping of port names and its respective value encoded as string.
const std::vector< NodeElement > deepApplyConst(ConstDeepApplyCallback apply_callback) const
Recursively apply a callback to this node's children.
TreeDocument * doc_ptr_
Pointer to the tree document that created the tree this node belongs to.
ChildIterator end() const
Get a past-the-end iterator for the children of this node.
virtual std::string getName() const
Get the name of this node given to this specific instance by the developer.
PortValues getPorts() const
Assemble the values given to each data port implemented by this node.
NodeElement & setConditionalScript(BT::PreCond type, const Script &script)
Specify a script that is evaluated before this node is ticked.
NodeElement insertTreeFromString(const std::string &tree_str, const std::string &tree_name, const NodeElement *before_this=nullptr)
Concatenate a tree from a document created from a string and add its first child node to the children...
tinyxml2::XMLElement * ele_ptr_
Pointer to the corresponding XMLElement of the base document.
std::string getFullyQualifiedName() const
Create a string that uniquely identifies this node considering its registration and its instance name...
model::SubTree insertSubTreeNode(const std::string &tree_name, const NodeElement *before_this=nullptr)
Add a subtree node for a specific tree element to the children of this node.
bool hasChildren() const
Determine whether any children have been given to this node.
NodeElement insertTree(const TreeElement &tree, const NodeElement *before_this=nullptr, bool auto_register_nodes=true)
Concatenate a tree and add its first child node to the children of this node.
bool operator==(const NodeElement &other) const
Determine if two node elements refer to the same node.
NodeElement getFirstNode(const std::string ®istration_name="", const std::string &instance_name="", bool deep_search=false) const
Get the first node with a particular registration and instance name.
ChildIterator begin() const
Get an iterator to the first child of this node.
NodeElement insertNode(const std::string &name, const NodeElement *before_this=nullptr)
Add a new node to the children of this node.
NodeElement & removeFirstChild(const std::string ®istration_name="", const std::string &instance_name="", bool deep_search=false)
Remove the first node with a particular registration and instance name.
NodeElement insertTreeFromFile(const std::string &path, const std::string &tree_name, const NodeElement *before_this=nullptr)
Concatenate a tree from a document created from a file and add its first child node to the children o...
NodeElement & removeChildren()
Recursively remove all children of this node element.
NodeElement & operator=(const NodeElement &other)
Replace this node with another.
std::vector< NodeElement > deepApply(DeepApplyCallback apply_callback)
Recursively apply a callback to this node's children.
Handle for a single behavior tree of a TreeDocument.
TreeElement & removeFirstChild(const std::string ®istration_name="", const std::string &instance_name="", bool deep_search=false)
TreeElement & removeChildren()
TreeElement(TreeDocument *doc_ptr, XMLElement *ele_ptr)
Protected constructor intended to be used only by certain factory methods of TreeDocument.
std::string getName() const override
Get the name of the behavior tree.
std::string writeToString(bool remove_whitespace=false) const
Write this behavior tree to an XML encoded in a string.
BT::Result verify() const
Verify that this behavior tree is structured correctly and can be created successfully.
NodeManifest getRequiredNodeManifest() const
Assemble the node manifest that is required for successfully creating an instance of this tree.
TreeElement & makeRoot()
Set this behavior tree as the root tree of the parent document.
TreeElement & operator=(const TreeElement &other)
Replace the behavior tree represented by this element with another.
TreeElement & setName(const std::string &tree_name) override
Set the name of the behavior tree.
Document Object Model (DOM) for the behavior tree XML schema. This class offers a programmatic approa...
TreeElement getRootTree()
Get the corresponding behavior tree element for the root tree of this document.
std::string getRootTreeName() const
Get the name of this document's root tree.
TreeDocument & mergeTreeDocument(const XMLDocument &other, bool adopt_root_tree=false)
Merge another tree document with this one.
TreeDocument & mergeString(const std::string &tree_str, bool adopt_root_tree=false)
Create a tree document from a string and merge it with this one.
TreeElement newTreeFromFile(const std::string &path, const std::string &tree_name="")
Create a new behavior tree inside this document with the content of one found inside the XML file.
TreeDocument & applyNodeNamespace(const std::string &node_namespace, const std::string &sep=_AUTO_APMS_BEHAVIOR_TREE_CORE__NODE_NAMESPACE_DEFAULT_SEP)
Prepend a namespace to all nodes associated with this document.
std::string writeToString(bool remove_whitespace=false) const
Write the XML of this tree document to a string.
BT::Result verify() const
Verify that all behavior trees of this document are structured correctly and can be created successfu...
std::set< std::string > getRegisteredNodeNames(bool include_native=true) const
Get the names of all nodes that are known to this document.
TreeDocument & mergeResource(const TreeResource &resource, bool adopt_root_tree=false, bool auto_register_nodes=true)
Merge the behavior trees from one of the installed package's behavior tree resources.
TreeDocument & reset(bool unregister_nodes=true)
Clear this document and reset it to its initial state.
TreeElement newTreeFromString(const std::string &tree_str, const std::string &tree_name="")
Create a new behavior tree inside this document with the content of one found inside the XML string.
void writeToFile(const std::string &path, bool remove_whitespace=false) const
Write the XML of this tree document to a file.
TreeElement newTreeFromDocument(const TreeDocument &other, const std::string &tree_name="", bool auto_register_nodes=true)
Create a new behavior tree inside this document with the content of one found inside another tree doc...
TreeElement getTree(const std::string &tree_name)
Get the corresponding behavior tree element for a tree inside this document.
virtual TreeDocument & registerNodes(const NodeManifest &tree_node_manifest, bool override=false)
Load behavior tree node plugins and register them with the internal behavior tree factory.
TreeDocument & setRootTreeName(const std::string &tree_name)
Define the root tree of this document.
bool hasTreeName(const std::string &tree_name) const
Determine if this document contains a behavior tree with a particular name.
TreeDocument & mergeFile(const std::string &path, bool adopt_root_tree=false)
Create a tree document from a file and merge it with this one.
NodeManifest getRequiredNodeManifest(const std::string &tree_name="") const
Assemble the node manifest that is required for successfully creating an instance of any of the docum...
std::vector< std::string > getAllTreeNames() const
Get the names of all behavior trees inside this document.
TreeDocument & addNodeModel(NodeModelMap model_map)
Add a behavior tree node model element to the document by parsing the contents of model_map.
TreeElement newTreeFromResource(const TreeResource &resource, const std::string &tree_name="", bool auto_register_nodes=true)
Create a new behavior tree inside this document with the content of one the trees found inside a part...
TreeDocument & mergeTree(const TreeElement &tree, bool make_root_tree=false, bool auto_register_nodes=true)
Merge an existing behavior tree with this tree document.
bool hasRootTreeName() const
Determine if this document specifies which of its trees is the root tree.
TreeDocument & removeTree(const std::string &tree_name)
Remove a particular behavior tree from this document.
TreeDocument(const std::string &format_version=BTCPP_FORMAT_DEFAULT_VERSION, NodeRegistrationLoader::SharedPtr tree_node_loader=NodeRegistrationLoader::make_shared())
Create a an empty tree document.
static NodeModelMap getNodeModel(tinyxml2::XMLDocument &doc, const NodeManifest &manifest)
Convert a behavior tree node model document to the corresponding data structure.
TreeElement newTree(const std::string &tree_name)
Create a new behavior tree inside this document.
Class containing behavior tree resource data.
std::string getRootTreeName() const
Get the name of the root tree of this behavior tree resource.
bool hasRootTreeName() const
Determine if this behavior tree resource specifies a root tree.
Subtree behavior tree node model.
bool contains(const ContainerT< ValueT, AllocatorT > &c, const ValueT &val)
Check whether a particular container structure contains a value.
std::set< KeyT, CompareT, AllocatorT > getCommonElements(std::set< KeyT, CompareT, AllocatorT > c1, std::set< KeyT, CompareT, AllocatorT > c2)
Assemble common elements of two sets.
Core API for AutoAPMS's behavior tree implementation.
Models for all available behavior tree nodes.
Powerful tooling for incorporating behavior trees for task development.
std::map< std::string, NodeModel > NodeModelMap
Mapping of node registration names and their implementation details.
Data structure encapsulating the information of all ports implemented by a behavior tree node.
Implementation details of a single data port.
std::string port_default
Default value of the port encoded as string.
bool port_has_default
Flag whether the port implements a default value or not.
std::string port_description
Description of the port.
BT::PortDirection port_direction
Direction of the port.
std::string port_type
String representation of the C++ type given to the port.
std::string port_name
Name of the port.
Parameters for loading and registering a behavior tree node class from a shared library using e....