AutoAPMS
Streamlining behaviors in ROS 2
Loading...
Searching...
No Matches
tree_document.hpp
1// Copyright 2024 Robin Müller
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#pragma once
16
17#include <tinyxml2.h>
18
19#include <functional>
20#include <set>
21#include <string>
22#include <type_traits>
23#include <vector>
24
25#include "auto_apms_behavior_tree_core/definitions.hpp"
26#include "auto_apms_behavior_tree_core/node/node_manifest.hpp"
27#include "auto_apms_behavior_tree_core/node/node_registration_loader.hpp"
28#include "auto_apms_behavior_tree_core/node/node_registration_options.hpp"
29#include "auto_apms_behavior_tree_core/tree/script.hpp"
30#include "behaviortree_cpp/bt_factory.h"
31#include "behaviortree_cpp/tree_node.h"
32#include "rclcpp/rclcpp.hpp"
33
35{
36class SubTree;
37}
38
40{
41
42class TreeResource;
43class NodeModelType;
44
139class TreeDocument : private tinyxml2::XMLDocument
140{
141 using XMLElement = tinyxml2::XMLElement;
142
143 inline static const std::string LOGGER_NAME = "tree_document";
144
145public:
146 static inline const char BTCPP_FORMAT_ATTRIBUTE_NAME[] = "BTCPP_format";
147 static inline const char BTCPP_FORMAT_DEFAULT_VERSION[] = "4";
148 static inline const char ROOT_ELEMENT_NAME[] = "root";
149 static inline const char ROOT_TREE_ATTRIBUTE_NAME[] = "main_tree_to_execute";
150 static inline const char TREE_ELEMENT_NAME[] = "BehaviorTree";
151 static inline const char SUBTREE_ELEMENT_NAME[] = "SubTree";
152 static inline const char TREE_NAME_ATTRIBUTE_NAME[] = "ID";
153 static inline const char TREE_NODE_MODEL_ELEMENT_NAME[] = "TreeNodesModel";
154 static inline const char NODE_INSTANCE_NAME_ATTRIBUTE_NAME[] = "name";
155 static inline const char INCLUDE_ELEMENT_NAME[] = "include";
156 static inline const char INCLUDE_PATH_ATTRIBUTE_NAME[] = "path";
157 static inline const char INCLUDE_ROS_PKG_ATTRIBUTE_NAME[] = "ros_pkg";
158
159 class TreeElement;
160
174 {
175 public:
177 using PortValues = std::map<std::string, std::string>;
179 using DeepApplyCallback = std::function<bool(NodeElement &)>;
181 using ConstDeepApplyCallback = std::function<bool(const NodeElement &)>;
182
183 protected:
189 explicit NodeElement(TreeDocument * doc_ptr, XMLElement * ele_ptr);
190
191 public:
196 NodeElement(const NodeElement & ele) = default;
197
209 NodeElement & operator=(const NodeElement & other);
210
219 bool operator==(const NodeElement & other) const;
220
221 bool operator!=(const NodeElement & other) const;
222
237 NodeElement insertNode(const std::string & name, const NodeElement * before_this = nullptr);
238
253 const std::string & name, const NodeRegistrationOptions & registration_options,
254 const NodeElement * before_this = nullptr);
255
269 template <class T>
270 typename std::enable_if_t<std::is_base_of_v<NodeModelType, T> && !std::is_same_v<model::SubTree, T>, T> insertNode(
271 const NodeElement * before_this = nullptr);
272
288 template <class SubTreeT>
289 typename std::enable_if_t<std::is_same_v<model::SubTree, SubTreeT>, model::SubTree> insertNode(
290 const std::string & tree_name, const NodeElement * before_this = nullptr);
291
310 template <class SubTreeT>
311 typename std::enable_if_t<std::is_same_v<model::SubTree, SubTreeT>, model::SubTree> insertNode(
312 const TreeElement & tree, const NodeElement * before_this = nullptr);
313
326 model::SubTree insertSubTreeNode(const std::string & tree_name, const NodeElement * before_this = nullptr);
327
343 model::SubTree insertSubTreeNode(const TreeElement & tree, const NodeElement * before_this = nullptr);
344
360 NodeElement insertTree(const TreeElement & tree, const NodeElement * before_this = nullptr);
361
380 const TreeDocument & doc, const std::string & tree_name, const NodeElement * before_this = nullptr);
381
400 NodeElement insertTreeFromDocument(const TreeDocument & doc, const NodeElement * before_this = nullptr);
401
422 const std::string & tree_str, const std::string & tree_name, const NodeElement * before_this = nullptr);
423
443 NodeElement insertTreeFromString(const std::string & tree_str, const NodeElement * before_this = nullptr);
444
465 const std::string & path, const std::string & tree_name, const NodeElement * before_this = nullptr);
466
486 NodeElement insertTreeFromFile(const std::string & path, const NodeElement * before_this = nullptr);
487
514 const TreeResource & resource, const std::string & tree_name, const NodeElement * before_this = nullptr);
515
542 NodeElement insertTreeFromResource(const TreeResource & resource, const NodeElement * before_this = nullptr);
543
548 bool hasChildren() const;
549
550 // clang-format off
568 NodeElement getFirstNode(const std::string & registration_name = "", const std::string & instance_name = "") const;
569
588 template <class T>
589 typename std::enable_if_t<std::is_base_of_v<NodeModelType, T>, T> getFirstNode(
590 const std::string & instance_name = "") const;
591
609 NodeElement & removeFirstChild(const std::string & registration_name = "", const std::string & instance_name = "");
610
629 template <class T>
630 typename std::enable_if_t<std::is_base_of_v<NodeModelType, T>, NodeElement &> removeFirstChild(
631 const std::string & instance_name = "");
632 // clang-format on
633
639
644 const std::vector<std::string> & getPortNames() const;
645
651 PortValues getPorts() const;
652
664 NodeElement & setPorts(const PortValues & port_values);
665
671
682 NodeElement & setConditionalScript(BT::PreCond type, const Script & script);
683
690 NodeElement & setConditionalScript(BT::PostCond type, const Script & script);
691
697 virtual NodeElement & setName(const std::string & instance_name);
698
703 virtual std::string getRegistrationName() const;
704
709 virtual std::string getName() const;
710
715 std::string getFullyQualifiedName() const;
716
725 const TreeDocument & getParentDocument() const;
726
744 const std::vector<NodeElement> deepApplyConst(ConstDeepApplyCallback apply_callback) const;
745
761 std::vector<NodeElement> deepApply(DeepApplyCallback apply_callback);
762
763 private:
764 NodeElement insertBeforeImpl(const NodeElement * before_this, XMLElement * add_this);
765
766 static void deepApplyImpl(
767 const NodeElement & parent, ConstDeepApplyCallback apply_callback, std::vector<NodeElement> & vec);
768
769 static void deepApplyImpl(NodeElement & parent, DeepApplyCallback apply_callback, std::vector<NodeElement> & vec);
770
771 protected:
775 tinyxml2::XMLElement * ele_ptr_;
776
777 private:
778 std::vector<std::string> port_names_;
779 PortValues port_default_values_;
780 };
781
791 {
792 friend class TreeDocument;
793
794 protected:
800 explicit TreeElement(TreeDocument * doc_ptr, XMLElement * ele_ptr);
801
802 public:
807 TreeElement(const TreeElement & ele) = default;
808
818 TreeElement & operator=(const TreeElement & other);
819
825 TreeElement & setName(const std::string & tree_name) override;
826
831 std::string getName() const override;
832
841
853
859 BT::Result verify() const;
860
868 std::string writeToString() const;
869
874 TreeElement & removeFirstChild(const std::string & registration_name = "", const std::string & instance_name = "");
875
880 template <class T>
881 typename std::enable_if_t<std::is_base_of_v<NodeModelType, T>, TreeElement &> removeFirstChild(
882 const std::string & instance_name = "");
883
889
890 /* Not supported methods for TreeElement instances */
891
892 const std::vector<std::string> & getPortNames() = delete;
893 PortValues getPorts() = delete;
894 NodeElement & setPorts() = delete;
895 NodeElement & resetPorts() = delete;
896 NodeElement & setConditionalScript() = delete;
897 };
898
910 const std::string & format_version = BTCPP_FORMAT_DEFAULT_VERSION,
911 NodeRegistrationLoader::SharedPtr tree_node_loader = NodeRegistrationLoader::make_shared());
912
913 TreeDocument(const TreeDocument & other) = delete;
914
915 virtual ~TreeDocument() = default;
916
940 TreeDocument & mergeTreeDocument(const XMLDocument & other, bool adopt_root_tree = false);
941
961 TreeDocument & mergeTreeDocument(const TreeDocument & other, bool adopt_root_tree = false);
962
986 TreeDocument & mergeString(const std::string & tree_str, bool adopt_root_tree = false);
987
1012 TreeDocument & mergeFile(const std::string & path, bool adopt_root_tree = false);
1013
1040 TreeDocument & mergeResource(const TreeResource & resource, bool adopt_root_tree = false);
1041
1058 TreeDocument & mergeTree(const TreeElement & tree, bool make_root_tree = false);
1059
1067 TreeElement newTree(const std::string & tree_name);
1068
1082 TreeElement newTree(const TreeElement & other_tree);
1083
1101 TreeElement newTreeFromDocument(const TreeDocument & other, const std::string & tree_name = "");
1102
1120 TreeElement newTreeFromString(const std::string & tree_str, const std::string & tree_name = "");
1121
1139 TreeElement newTreeFromFile(const std::string & path, const std::string & tree_name = "");
1140
1164 TreeElement newTreeFromResource(const TreeResource & resource, const std::string & tree_name = "");
1165
1171 bool hasTreeName(const std::string & tree_name) const;
1172
1180 TreeElement getTree(const std::string & tree_name);
1181
1189 TreeDocument & setRootTreeName(const std::string & tree_name);
1190
1195 bool hasRootTreeName() const;
1196
1203 std::string getRootTreeName() const;
1204
1212
1226 TreeDocument & removeTree(const std::string & tree_name);
1227
1240 TreeDocument & removeTree(const TreeElement & tree);
1241
1246 std::vector<std::string> getAllTreeNames() const;
1247
1258 virtual TreeDocument & registerNodes(const NodeManifest & tree_node_manifest, bool override = false);
1259
1266 std::set<std::string> getRegisteredNodeNames(bool include_native = true) const;
1267
1276
1285
1293 static NodeModelMap getNodeModel(tinyxml2::XMLDocument & doc, const NodeManifest & manifest);
1294
1304 NodeModelMap getNodeModel(bool include_native = false) const;
1305
1311 BT::Result verify() const;
1312
1317 std::string writeToString() const;
1318
1323 void writeToFile(const std::string & path) const;
1324
1329 TreeDocument & reset();
1330
1331private:
1332 template <typename ReturnT, typename DocumentT>
1333 static ReturnT getXMLElementForTreeWithNameImpl(DocumentT & doc, const std::string & tree_name);
1334
1335 const XMLElement * getXMLElementForTreeWithName(const std::string & tree_name) const;
1336
1337 XMLElement * getXMLElementForTreeWithName(const std::string & tree_name);
1338
1346 TreeDocument & mergeFileImpl(const std::string & path, bool adopt_root_tree, std::set<std::string> & include_stack);
1347
1355 TreeDocument & mergeTreeDocumentImpl(
1356 const XMLDocument & other, bool adopt_root_tree, std::set<std::string> & include_stack);
1357
1358 const std::map<std::string, std::string> all_node_classes_package_map_;
1359 const std::set<std::string> native_node_names_;
1360 std::string format_version_;
1361 NodeRegistrationLoader::SharedPtr tree_node_loader_ptr_;
1362 NodeManifest registered_nodes_manifest_;
1363
1364protected:
1365 BT::BehaviorTreeFactory factory_;
1366 rclcpp::Logger logger_;
1367 rclcpp::Node::WeakPtr ros_node_wptr_;
1368 rclcpp::CallbackGroup::WeakPtr tree_node_waitables_callback_group_wptr_;
1369 rclcpp::executors::SingleThreadedExecutor::WeakPtr tree_node_waitables_executor_wptr_;
1370 bool only_non_ros_nodes_ = false;
1371};
1372
1373// #####################################################################################################################
1374// ################################ DEFINITIONS ##############################################
1375// #####################################################################################################################
1376
1378template <typename T, typename = void>
1379struct has_static_method_registrationOptions : std::false_type
1380{
1381};
1382
1383template <typename T>
1384struct has_static_method_registrationOptions<
1385 T, typename std::enable_if_t<std::is_same_v<decltype(T::registrationOptions()), NodeRegistrationOptions>>>
1386: std::true_type
1387{
1388};
1390
1391template <class T>
1392inline typename std::enable_if_t<std::is_base_of_v<NodeModelType, T> && !std::is_same_v<model::SubTree, T>, T>
1394{
1395 // Determine if overload for automatically trying to register the node can be used. Models for native nodes don't
1396 // implement the required static method, thus the other overload must be used.
1397 if constexpr (has_static_method_registrationOptions<T>::value) {
1398 NodeElement ele = insertNode(T::name(), T::registrationOptions(), before_this);
1399 return T(ele.doc_ptr_, ele.ele_ptr_);
1400 } else {
1401 NodeElement ele = insertNode(T::name(), before_this);
1402 return T(ele.doc_ptr_, ele.ele_ptr_);
1403 }
1404}
1405
1406template <class T>
1407inline typename std::enable_if_t<std::is_same_v<model::SubTree, T>, model::SubTree>
1408TreeDocument::NodeElement::insertNode(const std::string & tree_name, const NodeElement * before_this)
1409{
1410 return insertSubTreeNode(tree_name, before_this);
1411}
1412
1413template <class T>
1414inline typename std::enable_if_t<std::is_same_v<model::SubTree, T>, model::SubTree>
1415TreeDocument::NodeElement::insertNode(const TreeElement & tree, const NodeElement * before_this)
1416{
1417 return insertSubTreeNode(tree, before_this);
1418}
1419
1420template <class T>
1421inline typename std::enable_if_t<std::is_base_of_v<NodeModelType, T>, T> core::TreeDocument::NodeElement::getFirstNode(
1422 const std::string & instance_name) const
1423{
1424 const NodeElement ele = getFirstNode(T::name(), instance_name);
1425 return T(ele.doc_ptr_, ele.ele_ptr_);
1426}
1427
1428template <class T>
1429inline typename std::enable_if_t<std::is_base_of_v<NodeModelType, T>, TreeDocument::NodeElement &>
1430core::TreeDocument::NodeElement::removeFirstChild(const std::string & instance_name)
1431{
1432 return removeFirstChild(T::name(), instance_name);
1433}
1434
1435template <class T>
1436inline typename std::enable_if_t<std::is_base_of_v<NodeModelType, T>, TreeDocument::TreeElement &>
1437TreeDocument::TreeElement::removeFirstChild(const std::string & instance_name)
1438{
1439 NodeElement::removeFirstChild<T>(instance_name);
1440 return *this;
1441}
1442
1443} // namespace auto_apms_behavior_tree::core
Data structure for information about which behavior tree node plugin to load and how to configure the...
Abstract base for all automatically generated behavior tree node model classes to be used in interact...
Class that encapsulates behavior tree script expressions.
Definition script.hpp:30
Handle for a single node of a TreeDocument.
const TreeDocument & getParentDocument() const
Get a const view of this node's parent tree document.
NodeElement insertTreeFromResource(const TreeResource &resource, const std::string &tree_name, const NodeElement *before_this=nullptr)
Concatenate a tree from one of the installed package's behavior tree resources and add its first chil...
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.
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.
NodeElement(const NodeElement &ele)=default
Copy constructor for a node element.
std::function< bool(NodeElement &)> DeepApplyCallback
Callback invoked for every node found under another node. It may modify the current node.
NodeElement & removeFirstChild(const std::string &registration_name="", const std::string &instance_name="")
Recursively visit this node's children in execution order and remove the first node with a particular...
NodeElement & setPorts(const PortValues &port_values)
Populate the the node's data ports.
std::enable_if_t< std::is_base_of_v< NodeModelType, T >, T > getFirstNode(const std::string &instance_name="") const
Recursively visit this node's children in execution order and get the first node with a particular in...
std::enable_if_t< std::is_same_v< model::SubTree, SubTreeT >, model::SubTree > insertNode(const TreeElement &tree, const NodeElement *before_this=nullptr)
Add a subtree node for a specific tree element to the children of this node.
virtual NodeElement & setName(const std::string &instance_name)
Assign a name for this specific node instance.
NodeElement getFirstNode(const std::string &registration_name="", const std::string &instance_name="") const
Recursively visit this node's children in execution order and get the first node with a particular re...
NodeElement(TreeDocument *doc_ptr, XMLElement *ele_ptr)
Protected constructor intended for internal use only.
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.
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.
NodeElement insertTree(const TreeElement &tree, const NodeElement *before_this=nullptr)
Concatenate a tree and add its first child node to the children of this node.
std::string getFullyQualifiedName() const
Create a string that uniquely identifies this node considering its registration and its instance name...
NodeElement insertTreeFromDocument(const TreeDocument &doc, const std::string &tree_name, const NodeElement *before_this=nullptr)
Concatenate a tree from a document and add its first child node to the children of this node.
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.
std::enable_if_t< std::is_same_v< model::SubTree, SubTreeT >, model::SubTree > insertNode(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 operator==(const NodeElement &other) const
Determine if two node elements refer to the same node.
NodeElement insertNode(const std::string &name, const NodeElement *before_this=nullptr)
Add a new node to the children of this node.
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...
std::enable_if_t< std::is_base_of_v< NodeModelType, T >, NodeElement & > removeFirstChild(const std::string &instance_name="")
Recursively visit this node's children in execution order and remove the first node with a particular...
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(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.
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 & removeFirstChild(const std::string &registration_name="", const std::string &instance_name="")
TreeElement & operator=(const TreeElement &other)
Replace the behavior tree represented by this element with another.
std::enable_if_t< std::is_base_of_v< NodeModelType, T >, TreeElement & > removeFirstChild(const std::string &instance_name="")
TreeElement & setName(const std::string &tree_name) override
Set the name of the behavior tree.
std::string writeToString() const
Write this behavior tree to an XML encoded in a string.
TreeElement(const TreeElement &ele)=default
Copy constructor for a tree element.
TreeElement getRootTree()
Get the corresponding behavior tree element for the root tree of this document.
TreeDocument & mergeResource(const TreeResource &resource, bool adopt_root_tree=false)
Merge the behavior trees from one of the installed package's behavior tree resources.
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 newTreeFromDocument(const TreeDocument &other, const std::string &tree_name="")
Create a new behavior tree inside this document with the content of one found inside another tree doc...
void writeToFile(const std::string &path) const
Write the XML of this tree document to a file.
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.
BT::Result verify() const
Verify that all behavior trees of this document are structured correctly and can be created successfu...
NodeManifest getRequiredNodeManifest() const
Assemble the node manifest that is required for successfully creating an instance of any of the docum...
std::set< std::string > getRegisteredNodeNames(bool include_native=true) const
Get the names of all nodes that are known to this document.
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.
TreeElement newTreeFromResource(const TreeResource &resource, const std::string &tree_name="")
Create a new behavior tree inside this document with the content of one the trees found inside a part...
TreeDocument & reset()
Clear this document and reset it to its initial state.
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.
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.
TreeDocument & mergeTree(const TreeElement &tree, bool make_root_tree=false)
Merge an existing behavior tree with this tree document.
std::string writeToString() const
Write the XML of this tree document to a string.
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.
Subtree behavior tree node model.
Core API for AutoAPMS's behavior tree implementation.
Definition behavior.hpp:32
Models for all available behavior tree nodes.
std::map< std::string, NodeModel > NodeModelMap
Mapping of node registration names and their implementation details.
Parameters for loading and registering a behavior tree node class from a shared library using e....