22 #if defined(_MSVC_LANG) && !defined(__clang__)
23 #define __bt_cplusplus (_MSC_VER == 1900 ? 201103L : _MSVC_LANG)
25 #define __bt_cplusplus __cplusplus
28 #if defined(__linux) || defined(__linux__)
29 #pragma GCC diagnostic push
30 #pragma GCC diagnostic ignored "-Wattributes"
43 #include <ament_index_cpp/get_package_share_directory.hpp>
52 std::string xsdAttributeType(
const BT::PortInfo& port_info)
56 return "blackboardType";
58 const auto& type_info = port_info.
type();
59 if((type_info ==
typeid(
int)) or (type_info ==
typeid(
unsigned int)))
61 return "integerOrBlackboardType";
63 else if(type_info ==
typeid(
double))
65 return "decimalOrBlackboardType";
67 else if(type_info ==
typeid(
bool))
69 return "booleanOrBlackboardType";
71 else if(type_info ==
typeid(std::string))
73 return "stringOrBlackboardType";
85 auto StrEqual = [](
const char* str1,
const char* str2) ->
bool {
86 return strcmp(str1, str2) == 0;
91 std::unordered_map<std::string, BT::PortInfo>
ports;
99 const std::string& prefix_path,
Tree& output_tree);
101 void recursivelyCreateSubtree(
const std::string& tree_ID,
const std::string& tree_path,
102 const std::string& prefix_path,
Tree& output_tree,
106 void getPortsRecursively(
const XMLElement* element,
107 std::vector<std::string>& output_ports);
109 void loadDocImpl(
XMLDocument* doc,
bool add_includes);
122 : factory(fact), current_path(
std::filesystem::current_path()), suffix_count(0)
128 current_path = std::filesystem::current_path();
129 opened_documents.clear();
134 void loadSubtreeModel(
const XMLElement* xml_root);
137 #if defined(__linux) || defined(__linux__)
138 #pragma GCC diagnostic pop
146 this->_p = std::move(other._p);
151 this->_p = std::move(other._p);
163 doc->
LoadFile(filepath.string().c_str());
165 _p->current_path = std::filesystem::absolute(filepath.parent_path());
167 _p->loadDocImpl(doc, add_includes);
177 _p->loadDocImpl(doc, add_includes);
182 std::vector<std::string> out;
183 for(
const auto& it :
_p->tree_roots)
185 out.push_back(it.first);
196 for(
auto sub_node = models_node->FirstChildElement(
"SubTree"); sub_node !=
nullptr;
197 sub_node = sub_node->NextSiblingElement(
"SubTree"))
199 auto subtree_id = sub_node->Attribute(
"ID");
200 auto& subtree_model = subtree_models[subtree_id];
202 std::pair<const char*, BT::PortDirection> port_types[3] = {
208 for(
const auto& [name, direction] : port_types)
210 for(
auto port_node = sub_node->FirstChildElement(name); port_node !=
nullptr;
211 port_node = port_node->NextSiblingElement(name))
214 auto name = port_node->Attribute(
"name");
217 throw RuntimeError(
"Missing attribute [name] in port (SubTree model)");
219 if(
auto default_value = port_node->Attribute(
"default"))
223 if(
auto description = port_node->Attribute(
"description"))
227 subtree_model.ports[name] = std::move(port);
245 auto format = xml_root->
Attribute(
"BTCPP_format");
248 std::cout <<
"Warnings: The first tag of the XML (<root>) should contain the "
249 "attribute [BTCPP_format=\"4\"]\n"
250 <<
"Please check if your XML is compatible with version 4.x of BT.CPP"
255 for(
auto incl_node = xml_root->
FirstChildElement(
"include"); incl_node !=
nullptr;
263 #if __bt_cplusplus >= 202002L
264 auto file_path(std::filesystem::path(incl_node->Attribute(
"path")));
266 auto file_path(std::filesystem::u8path(incl_node->Attribute(
"path")));
269 const char* ros_pkg_relative_path = incl_node->Attribute(
"ros_pkg");
271 if(ros_pkg_relative_path)
273 if(file_path.is_absolute())
275 std::cout <<
"WARNING: <include path=\"...\"> contains an absolute path.\n"
276 <<
"Attribute [ros_pkg] will be ignored." << std::endl;
280 std::string ros_pkg_path;
283 #elif defined USING_ROS2
285 ament_index_cpp::get_package_share_directory(ros_pkg_relative_path);
287 throw RuntimeError(
"Using attribute [ros_pkg] in <include>, but this library was "
288 "compiled without ROS support. Recompile the BehaviorTree.CPP "
291 file_path = std::filesystem::path(ros_pkg_path) / file_path;
295 if(!file_path.is_absolute())
305 current_path = std::filesystem::absolute(file_path.parent_path());
307 next_doc->
LoadFile(file_path.string().c_str());
315 std::unordered_map<std::string, BT::NodeType> registered_nodes;
318 registered_nodes.insert({ it.first, it.second.type });
322 doc->
Print(&printer);
331 for(
auto bt_node = xml_root->
FirstChildElement(
"BehaviorTree"); bt_node !=
nullptr;
334 std::string tree_name;
335 if(bt_node->Attribute(
"ID"))
337 tree_name = bt_node->Attribute(
"ID");
349 const std::unordered_map<std::string, BT::NodeType>& registered_nodes)
361 auto ThrowError = [&](
int line_num,
const std::string& text) {
363 snprintf(
buffer,
sizeof buffer,
"Error at line %d: -> %s", line_num, text.c_str());
367 auto ChildrenCount = [](
const XMLElement* parent_node) {
369 for(
auto node = parent_node->FirstChildElement(); node !=
nullptr;
370 node = node->NextSiblingElement())
382 throw RuntimeError(
"The XML must have a root node called <root>");
391 ThrowError(meta_sibling->GetLineNum(),
" Only a single node <TreeNodesModel> is "
401 const std::string name = node->Name();
402 if(name ==
"Action" || name ==
"Decorator" || name ==
"SubTree" ||
403 name ==
"Condition" || name ==
"Control")
405 const char* ID = node->Attribute(
"ID");
408 ThrowError(node->GetLineNum(),
"Error at line %d: -> The attribute "
409 "[ID] is mandatory");
416 int behavior_tree_count = 0;
420 behavior_tree_count++;
427 const int children_count = ChildrenCount(node);
428 const std::string name = node->Name();
429 const std::string ID = node->Attribute(
"ID") ? node->Attribute(
"ID") :
"";
430 const int line_number = node->GetLineNum();
432 if(name ==
"Decorator")
434 if(children_count != 1)
436 ThrowError(line_number,
"The tag <Decorator> must have exactly 1 "
441 ThrowError(line_number,
"The tag <Decorator> must have the "
445 else if(name ==
"Action")
447 if(children_count != 0)
449 ThrowError(line_number,
"The tag <Action> must not have any "
454 ThrowError(line_number,
"The tag <Action> must have the "
458 else if(name ==
"Condition")
460 if(children_count != 0)
462 ThrowError(line_number,
"The tag <Condition> must not have any "
467 ThrowError(line_number,
"The tag <Condition> must have the "
471 else if(name ==
"Control")
473 if(children_count == 0)
475 ThrowError(line_number,
"The tag <Control> must have at least 1 "
480 ThrowError(line_number,
"The tag <Control> must have the "
484 else if(name ==
"SubTree")
486 if(children_count != 0)
488 ThrowError(line_number,
"<SubTree> should not have any child");
492 ThrowError(line_number,
"The tag <SubTree> must have the "
495 if(registered_nodes.count(ID) != 0)
497 ThrowError(line_number,
"The attribute [ID] of tag <SubTree> must "
498 "not use the name of a registered Node");
501 else if(name ==
"BehaviorTree")
503 if(ID.empty() && behavior_tree_count > 1)
505 ThrowError(line_number,
"The tag <BehaviorTree> must have the "
508 if(children_count != 1)
510 ThrowError(line_number,
"The tag <BehaviorTree> must have exactly 1 "
513 if(registered_nodes.count(ID) != 0)
515 ThrowError(line_number,
"The attribute [ID] of tag <BehaviorTree> "
516 "must not use the name of a registered Node");
522 const auto search = registered_nodes.find(name);
523 bool found = (search != registered_nodes.end());
526 ThrowError(line_number, std::string(
"Node not recognized: ") + name);
531 if(children_count != 1)
533 ThrowError(line_number,
534 std::string(
"The node <") + name +
"> must have exactly 1 child");
539 if(children_count == 0)
541 ThrowError(line_number,
542 std::string(
"The node <") + name +
"> must have 1 or more children");
544 if(name ==
"ReactiveSequence")
546 size_t async_count = 0;
547 for(
auto child = node->FirstChildElement();
child !=
nullptr;
550 const std::string child_name = node->FirstChildElement()->Name();
551 const auto child_search = registered_nodes.find(child_name);
552 const auto child_type = child_search->second;
554 ((child_name ==
"ThreadedAction") ||
555 (child_name ==
"StatefulActionNode") ||
556 (child_name ==
"CoroActionNode") || (child_name ==
"AsyncSequence")))
561 ThrowError(line_number, std::string(
"A ReactiveSequence cannot have more "
562 "than one async child."));
570 for(
auto child = node->FirstChildElement();
child !=
nullptr;
573 recursiveStep(
child);
577 for(
auto bt_root = xml_root->
FirstChildElement(
"BehaviorTree"); bt_root !=
nullptr;
580 recursiveStep(bt_root);
585 std::string main_tree_ID)
591 if(main_tree_ID.empty())
593 XMLElement* first_xml_root =
_p->opened_documents.front()->RootElement();
595 if(
auto main_tree_attribute = first_xml_root->
Attribute(
"main_tree_to_execute"))
597 main_tree_ID = main_tree_attribute;
599 else if(
_p->tree_roots.size() == 1)
602 main_tree_ID =
_p->tree_roots.begin()->first;
606 throw RuntimeError(
"[main_tree_to_execute] was not specified correctly");
613 throw RuntimeError(
"XMLParser::instantiateTree needs a non-empty "
617 _p->recursivelyCreateSubtree(main_tree_ID, {}, {}, output_tree, root_blackboard,
631 const std::string& prefix_path,
634 const auto element_name = element->
Name();
635 const auto element_ID = element->
Attribute(
"ID");
645 if(factory.builders().count(element_name) == 0)
647 throw RuntimeError(element_name,
" is not a registered node");
649 type_ID = element_name;
653 throw RuntimeError(
"Attribute [ID] is not allowed in <", type_ID,
">");
661 throw RuntimeError(
"Attribute [ID] is mandatory in <", type_ID,
">");
663 type_ID = element_ID;
668 const char* attr_name = element->
Attribute(
"name");
669 const std::string instance_name = (attr_name !=
nullptr) ? attr_name : type_ID;
673 auto manifest_it = factory.manifests().find(type_ID);
674 if(manifest_it != factory.manifests().end())
684 const std::string port_name = att->Name();
685 const std::string port_value = att->Value();
689 auto port_model_it =
manifest->ports.find(port_name);
690 if(port_model_it ==
manifest->ports.end())
693 "] is found in the XML (<", element->
Name(),
695 ") but not in the providedPorts() of its "
696 "registered node type."));
700 const auto& port_model = port_model_it->second;
701 bool is_blacbkboard = port_value.size() >= 3 && port_value.front() ==
'{' &&
702 port_value.back() ==
'}';
704 if(!is_blacbkboard && port_model.converter() && port_model.isStronglyTyped())
709 port_model.converter()(port_value);
711 catch(std::exception& ex)
713 auto msg =
StrCat(
"The port with name \"", port_name,
"\" and value \"",
714 port_value,
"\" can not be converted to ",
715 port_model.typeName());
722 port_remap[port_name] = port_value;
728 config.
path = prefix_path + instance_name;
732 if(type_ID == instance_name)
737 auto AddCondition = [&](
auto& conditions,
const char* attr_name,
auto ID) {
738 if(
auto script = element->
Attribute(attr_name))
740 conditions.insert({ ID, std::string(script) });
746 auto pre =
static_cast<PreCond>(i);
751 auto post =
static_cast<PostCond>(i);
763 auto subtree_node =
dynamic_cast<SubTreeNode*
>(new_node.get());
770 auto msg =
StrCat(
"Missing manifest for element_ID: ", element_ID,
771 ". It shouldn't happen. Please report this issue.");
776 for(
const auto& [name_in_subtree,
_] : port_remap)
778 if(
manifest->ports.count(name_in_subtree) == 0)
780 throw RuntimeError(
"Possible typo? In the XML, you tried to remap port \"",
781 name_in_subtree,
"\" in node [", config.
path,
"(type ",
783 ")], but the manifest/model of this node does not contain a "
790 for(
const auto& [port_name, port_info] :
manifest->ports)
792 auto remap_it = port_remap.find(port_name);
793 if(remap_it == port_remap.end())
802 const auto port_key =
static_cast<std::string
>(param_res.value());
805 if(
auto prev_info = blackboard->entryInfo(port_key))
808 bool const port_type_mismatch =
809 (prev_info->isStronglyTyped() && port_info.isStronglyTyped() &&
810 prev_info->type() != port_info.type());
813 bool const string_input = (prev_info->type() ==
typeid(std::string));
817 blackboard->debugMessage();
819 throw RuntimeError(
"The creation of the tree failed because the port [",
820 port_key,
"] was initially created with type [",
821 demangle(prev_info->type()),
"] and, later type [",
822 demangle(port_info.type()),
"] was used somewhere else.");
828 blackboard->createEntry(port_key, port_info);
834 for(
const auto& remap_it : port_remap)
836 const auto& port_name = remap_it.first;
837 auto port_it =
manifest->ports.find(port_name);
838 if(port_it !=
manifest->ports.end())
840 auto direction = port_it->second.direction();
853 for(
const auto& port_it :
manifest->ports)
855 const std::string& port_name = port_it.first;
856 const PortInfo& port_info = port_it.second;
858 const auto direction = port_info.
direction();
860 if(!default_string.empty())
864 config.
input_ports.insert({ port_name, default_string });
871 config.
output_ports.insert({ port_name, default_string });
876 new_node = factory.instantiateTreeNode(instance_name, type_ID, config);
880 if(node_parent !=
nullptr)
882 if(
auto control_parent =
dynamic_cast<ControlNode*
>(node_parent.get()))
884 control_parent->addChild(new_node.get());
886 else if(
auto decorator_parent =
dynamic_cast<DecoratorNode*
>(node_parent.get()))
888 decorator_parent->setChild(new_node.get());
896 const std::string& tree_path,
897 const std::string& prefix_path,
907 std::string prefix,
const XMLElement* element) {
909 auto node = createNodeFromXML(element, blackboard, parent_node, prefix, output_tree);
910 subtree->nodes.push_back(node);
915 for(
auto child_element = element->FirstChildElement(); child_element;
916 child_element = child_element->NextSiblingElement())
918 recursiveStep(node, subtree, prefix, child_element);
924 const std::string subtree_ID = element->Attribute(
"ID");
925 std::unordered_map<std::string, std::string> subtree_remapping;
926 bool do_autoremap =
false;
928 for(
auto attr = element->FirstAttribute(); attr !=
nullptr; attr = attr->Next())
930 std::string attr_name = attr->Name();
931 std::string attr_value = attr->Value();
932 if(attr_value ==
"{=}")
934 attr_value =
StrCat(
"{", attr_name,
"}");
937 if(attr_name ==
"_autoremap")
940 new_bb->enableAutoRemapping(do_autoremap);
947 subtree_remapping.insert({ attr_name, attr_value });
952 auto subtree_model_it = subtree_models.find(subtree_ID);
953 if(subtree_model_it != subtree_models.end())
955 const auto& subtree_model_ports = subtree_model_it->second.ports;
959 for(
const auto& [port_name, port_info] : subtree_model_ports)
961 auto it = subtree_remapping.find(port_name);
963 if(it == subtree_remapping.end() && !do_autoremap)
966 if(port_info.defaultValueString().empty())
968 auto msg =
StrCat(
"In the <TreeNodesModel> the <Subtree ID=\"", subtree_ID,
969 "\"> is defining a mandatory port called [", port_name,
970 "], but you are not remapping it");
975 subtree_remapping.insert({ port_name, port_info.defaultValueString() });
981 for(
const auto& [attr_name, attr_value] : subtree_remapping)
987 new_bb->addSubtreeRemapping(attr_name, port_name);
993 new_bb->enableAutoRemapping(
false);
994 new_bb->set(attr_name,
static_cast<std::string
>(attr_value));
995 new_bb->enableAutoRemapping(do_autoremap);
999 std::string subtree_path = subtree->instance_name;
1000 if(!subtree_path.empty())
1002 subtree_path +=
"/";
1004 if(
auto name = element->Attribute(
"name"))
1006 subtree_path += name;
1013 recursivelyCreateSubtree(subtree_ID,
1016 output_tree, new_bb, node);
1020 auto it = tree_roots.find(tree_ID);
1021 if(it == tree_roots.end())
1023 throw std::runtime_error(std::string(
"Can't find a tree with name: ") + tree_ID);
1026 auto root_element = it->second->FirstChildElement();
1031 auto new_tree = std::make_shared<Tree::Subtree>();
1032 new_tree->blackboard = blackboard;
1033 new_tree->instance_name = tree_path;
1034 new_tree->tree_ID = tree_ID;
1035 output_tree.
subtrees.push_back(new_tree);
1037 recursiveStep(root_node, new_tree, prefix_path, root_element);
1041 std::vector<std::string>& output_ports)
1044 attr = attr->
Next())
1046 const char* attr_name = attr->Name();
1047 const char* attr_value = attr->Value();
1051 output_ports.push_back(
static_cast<std::string
>(port_name));
1058 getPortsRecursively(child_element, output_ports);
1068 for(
const auto& [port_name, port_info] : model.
ports)
1071 switch(port_info.direction())
1077 port_element = doc.
NewElement(
"output_port");
1085 if(port_info.type() !=
typeid(
void))
1089 if(!port_info.defaultValue().empty())
1091 port_element->
SetAttribute(
"default", port_info.defaultValueString().c_str());
1094 if(!port_info.description().empty())
1096 port_element->
SetText(port_info.description().c_str());
1103 auto metadata_root = doc.
NewElement(
"MetadataFields");
1105 for(
const auto& [name, value] : model.
metadata)
1107 auto metadata_element = doc.
NewElement(
"Metadata");
1108 metadata_element->
SetAttribute(name.c_str(), value.c_str());
1109 metadata_root->InsertEndChild(metadata_element);
1119 bool add_metadata,
bool add_builtin_models)
1125 if(
auto subtree =
dynamic_cast<const SubTreeNode*
>(&node))
1128 elem->
SetAttribute(
"ID", subtree->subtreeID().c_str());
1131 elem->
SetAttribute(
"_fullpath", subtree->config().path.c_str());
1167 parent_elem->InsertEndChild(elem);
1173 addNode(*
child, elem);
1176 else if(
auto decorator =
dynamic_cast<const DecoratorNode*
>(&node))
1180 addNode(*decorator->child(), elem);
1185 for(
const auto& subtree : tree.
subtrees)
1188 subtree_elem->
SetAttribute(
"ID", subtree->tree_ID.c_str());
1189 subtree_elem->
SetAttribute(
"_fullpath", subtree->instance_name.c_str());
1191 addNode(*subtree->nodes.front(), subtree_elem);
1199 std::map<std::string, const TreeNodeManifest*> ordered_models;
1200 for(
const auto& [registration_ID, model] : tree.
manifests)
1202 if(add_builtin_models || !temp_factory.
builtinNodes().count(registration_ID))
1204 ordered_models.insert({ registration_ID, &model });
1208 for(
const auto& [registration_ID, model] : ordered_models)
1215 bool include_builtin)
1226 std::map<std::string, const TreeNodeManifest*> ordered_models;
1228 for(
const auto& [registration_ID, model] : factory.
manifests())
1230 if(include_builtin || factory.
builtinNodes().count(registration_ID) == 0)
1232 ordered_models.insert({ registration_ID, &model });
1236 for(
const auto& [registration_ID, model] : ordered_models)
1242 doc.
Print(&printer);
1243 return std::string(printer.
CStr(),
size_t(printer.
CStrSize() - 1));
1254 std::map<std::string, const TreeNodeManifest*> ordered_models;
1255 for(
const auto& [registration_id, model] : factory.
manifests())
1257 ordered_models.insert({ registration_id, &model });
1264 "encoding=\"UTF-8\"");
1273 schema_element->
SetAttribute(
"xmlns:xs",
"http://www.w3.org/2001/XMLSchema");
1274 schema_element->
SetAttribute(
"elementFormDefault",
"qualified");
1277 auto parse_and_insert = [&doc](
XMLElement* parent_elem,
const char* str) {
1282 std::cerr <<
"Internal error parsing existing XML: " << tmp_doc.
ErrorStr()
1298 parse_and_insert(schema_element, R
"(
1299 <xs:simpleType name="blackboardType">
1300 <xs:restriction base="xs:string">
1301 <xs:pattern value="\{.*\}"/>
1304 <xs:simpleType name="booleanOrBlackboardType">
1305 <xs:union memberTypes="xs:boolean blackboardType"/>
1307 <xs:simpleType name="integerOrBlackboardType">
1308 <xs:union memberTypes="xs:integer blackboardType"/>
1310 <xs:simpleType name="decimalOrBlackboardType">
1311 <xs:union memberTypes="xs:decimal blackboardType"/>
1313 <xs:simpleType name="stringOrBlackboardType">
1314 <xs:union memberTypes="xs:string blackboardType"/>
1316 <xs:simpleType name="descriptionType">
1317 <xs:restriction base="xs:string">
1318 <xs:whiteSpace value="preserve"/>
1321 <xs:complexType name="inputPortType">
1323 <xs:extension base="xs:string">
1324 <xs:attribute name="name" type="xs:string" use="required"/>
1325 <xs:attribute name="type" type="xs:string" use="optional"/>
1326 <xs:attribute name="default" type="xs:string" use="optional"/>
1330 <xs:complexType name="outputPortType">
1332 <xs:extension base="xs:string">
1333 <xs:attribute name="name" type="xs:string" use="required"/>
1334 <xs:attribute name="type" type="xs:string" use="optional"/>
1338 <xs:attributeGroup name="preconditionAttributeGroup">
1339 <xs:attribute name="_failureIf" type="xs:string" use="optional"/>
1340 <xs:attribute name="_skipIf" type="xs:string" use="optional"/>
1341 <xs:attribute name="_successIf" type="xs:string" use="optional"/>
1342 <xs:attribute name="_while" type="xs:string" use="optional"/>
1343 </xs:attributeGroup>
1344 <xs:attributeGroup name="postconditionAttributeGroup">
1345 <xs:attribute name="_onSuccess" type="xs:string" use="optional"/>
1346 <xs:attribute name="_onFailure" type="xs:string" use="optional"/>
1347 <xs:attribute name="_post" type="xs:string" use="optional"/>
1348 <xs:attribute name="_onHalted" type="xs:string" use="optional"/>
1349 </xs:attributeGroup>)");
1357 parse_and_insert(schema_element, R
"(
1358 <xs:attributeGroup name="commonAttributeGroup">
1359 <xs:attribute name="name" type="xs:string" use="optional"/>
1360 <xs:attributeGroup ref="preconditionAttributeGroup"/>
1361 <xs:attributeGroup ref="postconditionAttributeGroup"/>
1362 </xs:attributeGroup>)");
1365 parse_and_insert(schema_element, R
"(
1366 <xs:complexType name="treeNodesModelNodeType">
1368 <xs:choice minOccurs="0" maxOccurs="unbounded">
1369 <xs:element name="input_port" type="inputPortType"/>
1370 <xs:element name="output_port" type="outputPortType"/>
1372 <xs:element name="description" type="descriptionType" minOccurs="0" maxOccurs="1"/>
1374 <xs:attribute name="ID" type="xs:string" use="required"/>
1376 <xs:group name="treeNodesModelNodeGroup">
1378 <xs:element name="Action" type="treeNodesModelNodeType"/>
1379 <xs:element name="Condition" type="treeNodesModelNodeType"/>
1380 <xs:element name="Control" type="treeNodesModelNodeType"/>
1381 <xs:element name="Decorator" type="treeNodesModelNodeType"/>
1387 const auto root_element_xsd = R
"(
1388 <xs:element name="root">
1391 <xs:choice minOccurs="0" maxOccurs="unbounded">
1392 <xs:element ref="include"/>
1393 <xs:element ref="BehaviorTree"/>
1395 <xs:element ref="TreeNodesModel" minOccurs="0" maxOccurs="1"/>
1397 <xs:attribute name="BTCPP_format" type="xs:string" use="required"/>
1398 <xs:attribute name="main_tree_to_execute" type="xs:string" use="optional"/>
1402 parse_and_insert(schema_element, root_element_xsd);
1408 std::ostringstream xsd;
1409 xsd <<
"<xs:choice>";
1410 for(
const auto& [registration_id, model] : ordered_models)
1412 xsd <<
"<xs:element name=\"" << registration_id <<
"\" type=\"" << registration_id
1415 xsd <<
"</xs:choice>";
1416 parse_and_insert(one_node_group, xsd.str().c_str());
1421 parse_and_insert(schema_element, R
"(
1422 <xs:element name="include">
1424 <xs:attribute name="path" type="xs:string" use="required"/>
1425 <xs:attribute name="ros_pkg" type="xs:string" use="optional"/>
1431 parse_and_insert(schema_element, R
"(
1432 <xs:element name="BehaviorTree">
1434 <xs:group ref="oneNodeGroup"/>
1435 <xs:attribute name="ID" type="xs:string" use="required"/>
1441 parse_and_insert(schema_element, R
"(
1442 <xs:element name="TreeNodesModel">
1444 <xs:group ref="treeNodesModelNodeGroup" minOccurs="0" maxOccurs="unbounded"/>
1450 for(
const auto& [registration_id, model] : ordered_models)
1453 type->
SetAttribute(
"name", (model->registration_ID +
"Type").c_str());
1481 common_attr_group->
SetAttribute(
"ref",
"commonAttributeGroup");
1483 for(
const auto& [port_name, port_info] : model->ports)
1487 const auto xsd_attribute_type = xsdAttributeType(port_info);
1488 if(not xsd_attribute_type.empty())
1490 attr->
SetAttribute(
"type", xsd_attribute_type.c_str());
1492 if(not port_info.defaultValue().empty())
1494 attr->
SetAttribute(
"default", port_info.defaultValueString().c_str());
1502 if(model->registration_ID ==
"SubTree")
1504 parse_and_insert(type, R
"(
1505 <xs:attribute name="ID" type="xs:string" use="required"/>
1506 <xs:anyAttribute processContents="skip"/>
1513 doc.Print(&printer);
1514 return std::string(printer.
CStr(),
size_t(printer.
CStrSize() - 1));
1541 addTreeToXML(tree, doc, rootXML, add_metadata, add_builtin_models);
1544 doc.
Print(&printer);
1545 return std::string(printer.
CStr(),
size_t(printer.
CStrSize() - 1));