xml_parsing.cpp
Go to the documentation of this file.
1 /* Copyright (C) 2018-2020 Davide Faconti, Eurecat - All Rights Reserved
2 *
3 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"),
4 * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
5 * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
9 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
10 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
11 */
12 
13 #include <functional>
14 #include <list>
15 
16 #if defined(__linux) || defined(__linux__)
17 #pragma GCC diagnostic push
18 #pragma GCC diagnostic ignored "-Wattributes"
19 #endif
20 
21 #ifdef _MSC_VER
22 #pragma warning(disable : 4996) // do not complain about sprintf
23 #endif
24 
26 #include "private/tinyxml2.h"
27 #include "filesystem/path.h"
28 
29 #ifdef USING_ROS
30 #include <ros/package.h>
31 #endif
32 
35 
36 namespace BT
37 {
38 using namespace BT_TinyXML2;
39 
40 auto StrEqual = [](const char* str1, const char* str2) -> bool {
41  return strcmp(str1, str2) == 0;
42 };
43 
44 
46 {
47  TreeNode::Ptr createNodeFromXML(const XMLElement* element,
48  const Blackboard::Ptr& blackboard,
49  const TreeNode::Ptr& node_parent);
50 
51  void recursivelyCreateTree(const std::string& tree_ID,
52  Tree& output_tree,
53  Blackboard::Ptr blackboard,
54  const TreeNode::Ptr& root_parent);
55 
56  void getPortsRecursively(const XMLElement* element, std::vector<std::string> &output_ports);
57 
58  void loadDocImpl(BT_TinyXML2::XMLDocument* doc);
59 
60  std::list<std::unique_ptr<BT_TinyXML2::XMLDocument> > opened_documents;
61  std::unordered_map<std::string,const XMLElement*> tree_roots;
62 
64 
65  filesystem::path current_path;
66 
68 
69  explicit Pimpl(const BehaviorTreeFactory &fact):
70  factory(fact),
71  current_path( filesystem::path::getcwd() ),
72  suffix_count(0)
73  {}
74 
75  void clear()
76  {
77  suffix_count = 0;
78  current_path = filesystem::path::getcwd();
79  opened_documents.clear();
80  tree_roots.clear();
81  }
82 
83 };
84 
85 #if defined(__linux) || defined(__linux__)
86 #pragma GCC diagnostic pop
87 #endif
88 
90  _p( new Pimpl(factory) )
91 {
92 }
93 
95 {
96  delete _p;
97 }
98 
99 void XMLParser::loadFromFile(const std::string& filename)
100 {
101  _p->opened_documents.emplace_back(new BT_TinyXML2::XMLDocument());
102 
103  BT_TinyXML2::XMLDocument* doc = _p->opened_documents.back().get();
104  doc->LoadFile(filename.c_str());
105 
106  filesystem::path file_path( filename );
107  _p->current_path = file_path.parent_path().make_absolute();
108 
109  _p->loadDocImpl( doc );
110 }
111 
112 void XMLParser::loadFromText(const std::string& xml_text)
113 {
114  _p->opened_documents.emplace_back(new BT_TinyXML2::XMLDocument());
115 
116  BT_TinyXML2::XMLDocument* doc = _p->opened_documents.back().get();
117  doc->Parse(xml_text.c_str(), xml_text.size());
118 
119  _p->loadDocImpl( doc );
120 }
121 
123 {
124  if (doc->Error())
125  {
126  char buffer[200];
127  sprintf(buffer, "Error parsing the XML: %s", doc->ErrorName() );
128  throw RuntimeError(buffer);
129  }
130 
131  const XMLElement* xml_root = doc->RootElement();
132 
133  // recursively include other files
134  for (auto include_node = xml_root->FirstChildElement("include");
135  include_node != nullptr;
136  include_node = include_node->NextSiblingElement("include"))
137  {
138 
139  filesystem::path file_path( include_node->Attribute("path") );
140 
141  if( include_node->Attribute("ros_pkg") )
142  {
143 #ifdef USING_ROS
144  if( file_path.is_absolute() )
145  {
146  std::cout << "WARNING: <include path=\"...\"> containes an absolute path.\n"
147  << "Attribute [ros_pkg] will be ignored."<< std::endl;
148  }
149  else {
150  auto ros_pkg_path = ros::package::getPath( include_node->Attribute("ros_pkg") );
151  file_path = filesystem::path( ros_pkg_path ) / file_path;
152  }
153 #else
154  throw RuntimeError("Using attribute [ros_pkg] in <include>, but this library was compiled "
155  "without ROS support. Recompile the BehaviorTree.CPP using catkin");
156 #endif
157  }
158 
159  if( !file_path.is_absolute() )
160  {
161  file_path = current_path / file_path;
162  }
163 
164  opened_documents.emplace_back(new BT_TinyXML2::XMLDocument());
165  BT_TinyXML2::XMLDocument* next_doc = opened_documents.back().get();
166  next_doc->LoadFile(file_path.str().c_str());
167  loadDocImpl(next_doc);
168  }
169 
170  for (auto bt_node = xml_root->FirstChildElement("BehaviorTree");
171  bt_node != nullptr;
172  bt_node = bt_node->NextSiblingElement("BehaviorTree"))
173  {
174  std::string tree_name;
175  if (bt_node->Attribute("ID"))
176  {
177  tree_name = bt_node->Attribute("ID");
178  }
179  else{
180  tree_name = "BehaviorTree_" + std::to_string( suffix_count++ );
181  }
182  tree_roots.insert( {tree_name, bt_node} );
183  }
184 
185  std::set<std::string> registered_nodes;
186  XMLPrinter printer;
187  doc->Print(&printer);
188  auto xml_text = std::string(printer.CStr(), size_t(printer.CStrSize() - 1));
189 
190  for( const auto& it: factory.manifests())
191  {
192  registered_nodes.insert( it.first );
193  }
194  for( const auto& it: tree_roots)
195  {
196  registered_nodes.insert( it.first );
197  }
198 
199  VerifyXML(xml_text, registered_nodes);
200 }
201 
202 void VerifyXML(const std::string& xml_text,
203  const std::set<std::string>& registered_nodes)
204 {
205 
207  auto xml_error = doc.Parse( xml_text.c_str(), xml_text.size());
208  if (xml_error)
209  {
210  char buffer[200];
211  sprintf(buffer, "Error parsing the XML: %s", doc.ErrorName() );
212  throw RuntimeError( buffer );
213  }
214 
215  //-------- Helper functions (lambdas) -----------------
216  auto ThrowError = [&](int line_num, const std::string& text) {
217  char buffer[256];
218  sprintf(buffer, "Error at line %d: -> %s", line_num, text.c_str());
219  throw RuntimeError( buffer );
220  };
221 
222  auto ChildrenCount = [](const XMLElement* parent_node) {
223  int count = 0;
224  for (auto node = parent_node->FirstChildElement(); node != nullptr;
225  node = node->NextSiblingElement())
226  {
227  count++;
228  }
229  return count;
230  };
231  //-----------------------------
232 
233  const XMLElement* xml_root = doc.RootElement();
234 
235  if (!xml_root || !StrEqual(xml_root->Name(), "root"))
236  {
237  throw RuntimeError("The XML must have a root node called <root>");
238  }
239  //-------------------------------------------------
240  auto models_root = xml_root->FirstChildElement("TreeNodesModel");
241  auto meta_sibling = models_root ? models_root->NextSiblingElement("TreeNodesModel") : nullptr;
242 
243  if (meta_sibling)
244  {
245  ThrowError(meta_sibling->GetLineNum(),
246  " Only a single node <TreeNodesModel> is supported");
247  }
248  if (models_root)
249  {
250  // not having a MetaModel is not an error. But consider that the
251  // Graphical editor needs it.
252  for (auto node = xml_root->FirstChildElement(); node != nullptr;
253  node = node->NextSiblingElement())
254  {
255  const char* name = node->Name();
256  if (StrEqual(name, "Action") || StrEqual(name, "Decorator") ||
257  StrEqual(name, "SubTree") || StrEqual(name, "Condition") || StrEqual(name, "Control"))
258  {
259  const char* ID = node->Attribute("ID");
260  if (!ID)
261  {
262  ThrowError(node->GetLineNum(),
263  "Error at line %d: -> The attribute [ID] is mandatory");
264  }
265  }
266  }
267  }
268  //-------------------------------------------------
269 
270  // function to be called recursively
271  std::function<void(const XMLElement*)> recursiveStep;
272 
273  recursiveStep = [&](const XMLElement* node) {
274  const int children_count = ChildrenCount(node);
275  const char* name = node->Name();
276  if (StrEqual(name, "Decorator"))
277  {
278  if (children_count != 1)
279  {
280  ThrowError(node->GetLineNum(),
281  "The node <Decorator> must have exactly 1 child");
282  }
283  if (!node->Attribute("ID"))
284  {
285  ThrowError(node->GetLineNum(),
286  "The node <Decorator> must have the attribute [ID]");
287  }
288  }
289  else if (StrEqual(name, "Action"))
290  {
291  if (children_count != 0)
292  {
293  ThrowError(node->GetLineNum(),
294  "The node <Action> must not have any child");
295  }
296  if (!node->Attribute("ID"))
297  {
298  ThrowError(node->GetLineNum(),
299  "The node <Action> must have the attribute [ID]");
300  }
301  }
302  else if (StrEqual(name, "Condition"))
303  {
304  if (children_count != 0)
305  {
306  ThrowError(node->GetLineNum(),
307  "The node <Condition> must not have any child");
308  }
309  if (!node->Attribute("ID"))
310  {
311  ThrowError(node->GetLineNum(),
312  "The node <Condition> must have the attribute [ID]");
313  }
314  }
315  else if (StrEqual(name, "Control"))
316  {
317  if (children_count == 0)
318  {
319  ThrowError(node->GetLineNum(),
320  "The node <Control> must have at least 1 child");
321  }
322  if (!node->Attribute("ID"))
323  {
324  ThrowError(node->GetLineNum(),
325  "The node <Control> must have the attribute [ID]");
326  }
327  }
328  else if (StrEqual(name, "Sequence") ||
329  StrEqual(name, "SequenceStar") ||
330  StrEqual(name, "Fallback") )
331  {
332  if (children_count == 0)
333  {
334  ThrowError(node->GetLineNum(),
335  "A Control node must have at least 1 child");
336  }
337  }
338  else if (StrEqual(name, "SubTree"))
339  {
340  auto child = node->FirstChildElement();
341 
342  if (child)
343  {
344  if (StrEqual(child->Name(), "remap"))
345  {
346  ThrowError(node->GetLineNum(), "<remap> was deprecated");
347  }
348  else{
349  ThrowError(node->GetLineNum(), "<SubTree> should not have any child");
350  }
351  }
352 
353  if (!node->Attribute("ID"))
354  {
355  ThrowError(node->GetLineNum(),
356  "The node <SubTree> must have the attribute [ID]");
357  }
358  }
359  else
360  {
361  // search in the factory and the list of subtrees
362  bool found = ( registered_nodes.find(name) != registered_nodes.end() );
363  if (!found)
364  {
365  ThrowError(node->GetLineNum(),
366  std::string("Node not recognized: ") + name);
367  }
368  }
369  //recursion
370  if (StrEqual(name, "SubTree") == false)
371  {
372  for (auto child = node->FirstChildElement(); child != nullptr;
373  child = child->NextSiblingElement())
374  {
375  recursiveStep(child);
376  }
377  }
378  };
379 
380  std::vector<std::string> tree_names;
381  int tree_count = 0;
382 
383  for (auto bt_root = xml_root->FirstChildElement("BehaviorTree"); bt_root != nullptr;
384  bt_root = bt_root->NextSiblingElement("BehaviorTree"))
385  {
386  tree_count++;
387  if (bt_root->Attribute("ID"))
388  {
389  tree_names.emplace_back(bt_root->Attribute("ID"));
390  }
391  if (ChildrenCount(bt_root) != 1)
392  {
393  ThrowError(bt_root->GetLineNum(),
394  "The node <BehaviorTree> must have exactly 1 child");
395  }
396  else
397  {
398  recursiveStep(bt_root->FirstChildElement());
399  }
400  }
401 
402  if (xml_root->Attribute("main_tree_to_execute"))
403  {
404  std::string main_tree = xml_root->Attribute("main_tree_to_execute");
405  if (std::find(tree_names.begin(), tree_names.end(), main_tree) == tree_names.end())
406  {
407  throw RuntimeError("The tree specified in [main_tree_to_execute] can't be found");
408  }
409  }
410  else
411  {
412  if (tree_count != 1)
413  {
414  throw RuntimeError("If you don't specify the attribute [main_tree_to_execute], "
415  "Your file must contain a single BehaviorTree");
416  }
417  }
418 }
419 
421 {
422  Tree output_tree;
423 
424  XMLElement* xml_root = _p->opened_documents.front()->RootElement();
425 
426  std::string main_tree_ID;
427  if (xml_root->Attribute("main_tree_to_execute"))
428  {
429  main_tree_ID = xml_root->Attribute("main_tree_to_execute");
430  }
431  else if( _p->tree_roots.size() == 1)
432  {
433  main_tree_ID = _p->tree_roots.begin()->first;
434  }
435  else{
436  throw RuntimeError("[main_tree_to_execute] was not specified correctly");
437  }
438  //--------------------------------------
439  if( !root_blackboard )
440  {
441  throw RuntimeError("XMLParser::instantiateTree needs a non-empty root_blackboard");
442  }
443  // first blackboard
444  output_tree.blackboard_stack.push_back( root_blackboard );
445 
446  _p->recursivelyCreateTree(main_tree_ID,
447  output_tree,
448  root_blackboard,
449  TreeNode::Ptr() );
450  return output_tree;
451 }
452 
454  const Blackboard::Ptr &blackboard,
455  const TreeNode::Ptr &node_parent)
456 {
457  const std::string element_name = element->Name();
458  std::string ID;
459  std::string instance_name;
460 
461  // Actions and Decorators have their own ID
462  if (element_name == "Action" || element_name == "Decorator" ||
463  element_name == "Condition" || element_name == "Control")
464  {
465  ID = element->Attribute("ID");
466  }
467  else
468  {
469  ID = element_name;
470  }
471 
472  const char* attr_alias = element->Attribute("name");
473  if (attr_alias)
474  {
475  instance_name = attr_alias;
476  }
477  else
478  {
479  instance_name = ID;
480  }
481 
482  PortsRemapping port_remap;
483 
484  if (element_name == "SubTree" ||
485  element_name == "SubTreePlus" )
486  {
487  instance_name = element->Attribute("ID");
488  }
489  else{
490  // do this only if it NOT a Subtree
491  for (const XMLAttribute* att = element->FirstAttribute(); att; att = att->Next())
492  {
493  const std::string attribute_name = att->Name();
494  if (attribute_name != "ID" && attribute_name != "name")
495  {
496  port_remap[attribute_name] = att->Value();
497  }
498  }
499  }
500  NodeConfiguration config;
501  config.blackboard = blackboard;
502 
503  //---------------------------------------------
504  TreeNode::Ptr child_node;
505 
506  if( factory.builders().count(ID) != 0)
507  {
508  const auto& manifest = factory.manifests().at(ID);
509 
510  //Check that name in remapping can be found in the manifest
511  for(const auto& remap_it: port_remap)
512  {
513  if( manifest.ports.count( remap_it.first ) == 0 )
514  {
515  throw RuntimeError("Possible typo? In the XML, you tried to remap port \"",
516  remap_it.first, "\" in node [", ID," / ", instance_name,
517  "], but the manifest of this node does not contain a port with this name.");
518  }
519  }
520 
521  // Initialize the ports in the BB to set the type
522  for(const auto& port_it: manifest.ports)
523  {
524  const std::string& port_name = port_it.first;
525  const auto& port_info = port_it.second;
526 
527  auto remap_it = port_remap.find(port_name);
528  if( remap_it == port_remap.end())
529  {
530  continue;
531  }
532  StringView param_value = remap_it->second;
533  auto param_res = TreeNode::getRemappedKey(port_name, param_value);
534  if( param_res )
535  {
536  const auto port_key = static_cast<std::string>(param_res.value());
537 
538  auto prev_info = blackboard->portInfo( port_key );
539  if( !prev_info )
540  {
541  // not found, insert for the first time.
542  blackboard->setPortInfo( port_key, port_info );
543  }
544  else{
545  // found. check consistency
546  if( prev_info->type() && port_info.type() && // null type means that everything is valid
547  *prev_info->type() != *port_info.type())
548  {
549  blackboard->debugMessage();
550 
551  throw RuntimeError( "The creation of the tree failed because the port [", port_key,
552  "] was initially created with type [", demangle( prev_info->type() ),
553  "] and, later type [", demangle( port_info.type() ),
554  "] was used somewhere else." );
555  }
556  }
557  }
558  }
559 
560  // use manifest to initialize NodeConfiguration
561  for(const auto& remap_it: port_remap)
562  {
563  const auto& port_name = remap_it.first;
564  auto port_it = manifest.ports.find( port_name );
565  if( port_it != manifest.ports.end() )
566  {
567  auto direction = port_it->second.direction();
568  if( direction != PortDirection::OUTPUT )
569  {
570  config.input_ports.insert( remap_it );
571  }
572  if( direction != PortDirection::INPUT )
573  {
574  config.output_ports.insert( remap_it );
575  }
576  }
577  }
578 
579  // use default value if available for empty ports. Only inputs
580  for (const auto& port_it: manifest.ports)
581  {
582  const std::string& port_name = port_it.first;
583  const PortInfo& port_info = port_it.second;
584 
585  auto direction = port_info.direction();
586  if( direction != PortDirection::OUTPUT &&
587  config.input_ports.count(port_name) == 0 &&
588  port_info.defaultValue().empty() == false)
589  {
590  config.input_ports.insert( { port_name, port_info.defaultValue() } );
591  }
592  }
593 
594  child_node = factory.instantiateTreeNode(instance_name, ID, config);
595  }
596  else if( tree_roots.count(ID) != 0) {
597  child_node = std::make_unique<SubtreeNode>( instance_name );
598  }
599  else{
600  throw RuntimeError( ID, " is not a registered node, nor a Subtree");
601  }
602 
603  if (node_parent)
604  {
605  if (auto control_parent = dynamic_cast<ControlNode*>(node_parent.get()))
606  {
607  control_parent->addChild(child_node.get());
608  }
609  if (auto decorator_parent = dynamic_cast<DecoratorNode*>(node_parent.get()))
610  {
611  decorator_parent->setChild(child_node.get());
612  }
613  }
614  return child_node;
615 }
616 
617 void BT::XMLParser::Pimpl::recursivelyCreateTree(const std::string& tree_ID,
618  Tree& output_tree,
619  Blackboard::Ptr blackboard,
620  const TreeNode::Ptr& root_parent)
621 {
622  std::function<void(const TreeNode::Ptr&, const XMLElement*)> recursiveStep;
623 
624  recursiveStep = [&](const TreeNode::Ptr& parent,
625  const XMLElement* element)
626  {
627  // create the node
628  auto node = createNodeFromXML(element, blackboard, parent);
629  output_tree.nodes.push_back(node);
630 
631  if( node->type() == NodeType::SUBTREE )
632  {
633  if( dynamic_cast<const SubtreeNode*>(node.get()) )
634  {
635  bool is_isolated = true;
636 
637  for (const XMLAttribute* attr = element->FirstAttribute(); attr != nullptr; attr = attr->Next())
638  {
639  if( strcmp(attr->Name(), "__shared_blackboard") == 0 &&
640  convertFromString<bool>(attr->Value()) == true )
641  {
642  is_isolated = false;
643  }
644  }
645 
646  if( !is_isolated )
647  {
648  recursivelyCreateTree( node->name(), output_tree, blackboard, node );
649  }
650  else{
651  // Creating an isolated
652  auto new_bb = Blackboard::create(blackboard);
653 
654  for (const XMLAttribute* attr = element->FirstAttribute(); attr != nullptr; attr = attr->Next())
655  {
656  if( strcmp(attr->Name(), "ID") == 0 )
657  {
658  continue;
659  }
660  new_bb->addSubtreeRemapping( attr->Name(), attr->Value() );
661  }
662  output_tree.blackboard_stack.emplace_back(new_bb);
663  recursivelyCreateTree( node->name(), output_tree, new_bb, node );
664  }
665  }
666  else if( dynamic_cast<const SubtreePlusNode*>(node.get()) )
667  {
668  auto new_bb = Blackboard::create(blackboard);
669  output_tree.blackboard_stack.emplace_back(new_bb);
670  std::set<StringView> mapped_keys;
671 
672  bool do_autoremap = false;
673 
674  for (const XMLAttribute* attr = element->FirstAttribute(); attr != nullptr; attr = attr->Next())
675  {
676  const char* attr_name = attr->Name();
677  const char* attr_value = attr->Value();
678 
679  if( StrEqual(attr_name, "ID") )
680  {
681  continue;
682  }
683  if( StrEqual(attr_name, "__autoremap") )
684  {
685  do_autoremap = convertFromString<bool>(attr_value);
686  continue;
687  }
688 
689  if( TreeNode::isBlackboardPointer(attr_value))
690  {
691  // do remapping
692  StringView port_name = TreeNode::stripBlackboardPointer(attr_value);
693  new_bb->addSubtreeRemapping( attr_name, port_name );
694  mapped_keys.insert(attr_name);
695  }
696  else{
697  // constant string: just set that constant value into the BB
698  new_bb->set(attr_name, static_cast<std::string>(attr_value) );
699  mapped_keys.insert(attr_name);
700  }
701  }
702 
703  if( do_autoremap )
704  {
705  std::vector<std::string> remapped_ports;
706  auto new_root_element = tree_roots[node->name()]->FirstChildElement();
707 
708  getPortsRecursively( new_root_element, remapped_ports );
709  for( const auto& port: remapped_ports)
710  {
711  if( mapped_keys.count(port) == 0)
712  {
713  new_bb->addSubtreeRemapping( port, port );
714  }
715  }
716  }
717 
718  recursivelyCreateTree( node->name(), output_tree, new_bb, node );
719  }
720  }
721  else
722  {
723  for (auto child_element = element->FirstChildElement(); child_element;
724  child_element = child_element->NextSiblingElement())
725  {
726  recursiveStep(node, child_element);
727  }
728  }
729  };
730 
731  auto root_element = tree_roots[tree_ID]->FirstChildElement();
732 
733  // start recursion
734  recursiveStep(root_parent, root_element);
735 }
736 
738  std::vector<std::string>& output_ports)
739 {
740  for (const XMLAttribute* attr = element->FirstAttribute(); attr != nullptr; attr = attr->Next())
741  {
742  const char* attr_name = attr->Name();
743  const char* attr_value = attr->Value();
744  if( !StrEqual(attr_name, "ID") &&
745  !StrEqual(attr_name, "name") &&
746  TreeNode::isBlackboardPointer(attr_value) )
747  {
748  auto port_name = TreeNode::stripBlackboardPointer(attr_value);
749  output_ports.push_back( static_cast<std::string>(port_name) );
750  }
751  }
752 
753  for (auto child_element = element->FirstChildElement(); child_element;
754  child_element = child_element->NextSiblingElement())
755  {
756  getPortsRecursively(child_element, output_ports);
757  }
758 }
759 
760 
761 std::string writeTreeNodesModelXML(const BehaviorTreeFactory& factory)
762 {
763  using namespace BT_TinyXML2;
764 
766 
767  XMLElement* rootXML = doc.NewElement("root");
768  doc.InsertFirstChild(rootXML);
769 
770  XMLElement* model_root = doc.NewElement("TreeNodesModel");
771  rootXML->InsertEndChild(model_root);
772 
773  for (auto& model_it : factory.manifests())
774  {
775  const auto& registration_ID = model_it.first;
776  const auto& model = model_it.second;
777 
778  if( factory.builtinNodes().count( registration_ID ) != 0)
779  {
780  continue;
781  }
782 
783  if (model.type == NodeType::CONTROL)
784  {
785  continue;
786  }
787  XMLElement* element = doc.NewElement( toStr(model.type).c_str() );
788  element->SetAttribute("ID", model.registration_ID.c_str());
789 
790  for (auto& port : model.ports)
791  {
792  const auto& port_name = port.first;
793  const auto& port_info = port.second;
794 
795  XMLElement* port_element = nullptr;
796  switch( port_info.direction() )
797  {
798  case PortDirection::INPUT: port_element = doc.NewElement("input_port"); break;
799  case PortDirection::OUTPUT: port_element = doc.NewElement("output_port"); break;
800  case PortDirection::INOUT: port_element = doc.NewElement("inout_port"); break;
801  }
802 
803  port_element->SetAttribute("name", port_name.c_str() );
804  if( port_info.type() )
805  {
806  port_element->SetAttribute("type", BT::demangle( port_info.type() ).c_str() );
807  }
808  if( !port_info.defaultValue().empty() )
809  {
810  port_element->SetAttribute("default", port_info.defaultValue().c_str() );
811  }
812 
813  if( !port_info.description().empty() )
814  {
815  port_element->SetText( port_info.description().c_str() );
816  }
817 
818  element->InsertEndChild(port_element);
819  }
820 
821  model_root->InsertEndChild(element);
822  }
823 
824  XMLPrinter printer;
825  doc.Print(&printer);
826  return std::string(printer.CStr(), size_t(printer.CStrSize() - 1));
827 }
828 
829 Tree buildTreeFromText(const BehaviorTreeFactory& factory, const std::string& text,
830  const Blackboard::Ptr& blackboard)
831 {
832  XMLParser parser(factory);
833  parser.loadFromText(text);
834  return parser.instantiateTree(blackboard);
835 }
836 
837 Tree buildTreeFromFile(const BehaviorTreeFactory& factory, const std::string& filename,
838  const Blackboard::Ptr& blackboard)
839 {
840  XMLParser parser(factory);
841  parser.loadFromFile(filename);
842  return parser.instantiateTree(blackboard);
843 }
844 
845 }
Tree buildTreeFromText(const BehaviorTreeFactory &factory, const std::string &text, const Blackboard::Ptr &blackboard)
void SetAttribute(const char *name, const char *value)
Sets the named attribute to value.
Definition: tinyxml2.h:1429
Pimpl(const BehaviorTreeFactory &fact)
Definition: xml_parsing.cpp:69
static Blackboard::Ptr create(Blackboard::Ptr parent={})
Definition: blackboard.h:38
manifest
const XMLElement * NextSiblingElement(const char *name=0) const
Get the next (right) sibling element of this node, with an optionally supplied name.
Definition: tinyxml2.cpp:964
PortDirection direction() const
void recursivelyCreateTree(const std::string &tree_ID, Tree &output_tree, Blackboard::Ptr blackboard, const TreeNode::Ptr &root_parent)
static raw_event_t * buffer
Definition: minitrace.cpp:54
Pimpl * _p
Definition: xml_parsing.h:32
std::string toStr(T value)
Definition: basic_types.h:136
const XMLAttribute * Next() const
The next attribute in the list.
Definition: tinyxml2.h:1147
std::unordered_map< std::string, const XMLElement * > tree_roots
Definition: xml_parsing.cpp:61
std::vector< Blackboard::Ptr > blackboard_stack
Definition: bt_factory.h:135
filesystem::path current_path
Definition: xml_parsing.cpp:65
XMLNode * InsertFirstChild(XMLNode *addThis)
Definition: tinyxml2.cpp:871
void loadDocImpl(BT_TinyXML2::XMLDocument *doc)
const XMLElement * FirstChildElement(const char *name=0) const
Definition: tinyxml2.cpp:940
std::shared_ptr< Blackboard > Ptr
Definition: blackboard.h:26
const std::set< std::string > & builtinNodes() const
List of builtin IDs.
Definition: bt_factory.cpp:244
Blackboard::Ptr blackboard
Definition: tree_node.h:47
XMLElement * NewElement(const char *name)
Definition: tinyxml2.cpp:2094
void SetText(const char *inText)
Definition: tinyxml2.cpp:1589
XMLError LoadFile(const char *filename)
Definition: tinyxml2.cpp:2167
std::string demangle(char const *name)
Definition: demangle_util.h:78
auto StrEqual
Definition: xml_parsing.cpp:40
XMLElement * RootElement()
Definition: tinyxml2.h:1747
bool Error() const
Return true if there was an error parsing the document.
Definition: tinyxml2.h:1819
The BehaviorTreeFactory is used to create instances of a TreeNode at run-time.
Definition: bt_factory.h:207
const std::string & defaultValue() const
const char * CStr() const
Definition: tinyxml2.h:2241
nonstd::string_view StringView
Definition: basic_types.h:50
void Print(XMLPrinter *streamer=0) const
Definition: tinyxml2.cpp:2316
const BehaviorTreeFactory & factory
Definition: xml_parsing.cpp:63
const XMLAttribute * FirstAttribute() const
Return the first attribute in the list.
Definition: tinyxml2.h:1472
bool convertFromString< bool >(StringView str)
XMLError Parse(const char *xml, size_t nBytes=(size_t)(-1))
Definition: tinyxml2.cpp:2285
PortsRemapping output_ports
Definition: tree_node.h:49
Struct used to store a tree. If this object goes out of scope, the tree is destroyed.
Definition: bt_factory.h:130
Tree instantiateTree(const Blackboard::Ptr &root_blackboard) override
Simple class for manipulating paths on Linux/Windows/Mac OS.
Definition: path.h:42
ROSLIB_DECL std::string getPath(const std::string &package_name)
std::vector< TreeNode::Ptr > nodes
Definition: bt_factory.h:134
static volatile int count
Definition: minitrace.cpp:55
static StringView stripBlackboardPointer(StringView str)
Definition: tree_node.cpp:132
const std::unordered_map< std::string, TreeNodeManifest > & manifests() const
Manifests of all the registered TreeNodes.
Definition: bt_factory.cpp:239
void getPortsRecursively(const XMLElement *element, std::vector< std::string > &output_ports)
std::string writeTreeNodesModelXML(const BehaviorTreeFactory &factory)
The XMLParser is a class used to read the model of a BehaviorTree from file or text and instantiate t...
Definition: xml_parsing.h:14
int CStrSize() const
Definition: tinyxml2.h:2249
static bool isBlackboardPointer(StringView str)
Definition: tree_node.cpp:117
static const char * xml_text
void loadFromText(const std::string &xml_text) override
const char * ErrorName() const
Definition: tinyxml2.cpp:2375
std::list< std::unique_ptr< BT_TinyXML2::XMLDocument > > opened_documents
Definition: xml_parsing.cpp:60
XMLParser(const BehaviorTreeFactory &factory)
Definition: xml_parsing.cpp:89
const char * Name() const
Get the name of an element (which is the Value() of the node.)
Definition: tinyxml2.h:1252
std::shared_ptr< TreeNode > Ptr
Definition: tree_node.h:56
void VerifyXML(const std::string &xml_text, const std::set< std::string > &registered_nodes)
const char * Attribute(const char *name, const char *value=0) const
Definition: tinyxml2.cpp:1526
TreeNode::Ptr createNodeFromXML(const XMLElement *element, const Blackboard::Ptr &blackboard, const TreeNode::Ptr &node_parent)
void loadFromFile(const std::string &filename) override
Definition: xml_parsing.cpp:99
std::basic_string< CharT, Traits > to_string(basic_string_view< CharT, Traits > v)
PortsRemapping input_ports
Definition: tree_node.h:48
Tree buildTreeFromFile(const BehaviorTreeFactory &factory, const std::string &filename, const Blackboard::Ptr &blackboard)
XMLNode * InsertEndChild(XMLNode *addThis)
Definition: tinyxml2.cpp:841
static Optional< StringView > getRemappedKey(StringView port_name, StringView remapping_value)
Definition: tree_node.cpp:147
std::unordered_map< std::string, std::string > PortsRemapping
Definition: tree_node.h:39


behaviotree_cpp_v3
Author(s): Michele Colledanchise, Davide Faconti
autogenerated on Tue May 4 2021 02:56:25