diff --git a/Doxyfile b/Doxyfile index 7c24f87b6..353afcde5 100644 --- a/Doxyfile +++ b/Doxyfile @@ -58,7 +58,7 @@ PROJECT_LOGO = # entered, it will be relative to the location where doxygen was started. If # left blank the current directory will be used. -OUTPUT_DIRECTORY = /home/davide.faconti/ws_behavior_tree/src/Behavior-Tree/doc +OUTPUT_DIRECTORY = ./doc # If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- # directories (in 2 levels) under the output directory of each output format and @@ -781,7 +781,7 @@ WARN_LOGFILE = # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # Note: If this tag is empty the current directory is searched. -INPUT = /home/davide.faconti/ws_behavior_tree/src/Behavior-Tree/include +INPUT = ./include # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses @@ -863,8 +863,8 @@ RECURSIVE = YES # Note that relative paths are relative to the directory from which doxygen is # run. -EXCLUDE = /home/davide.faconti/ws_behavior_tree/src/Behavior-Tree/3rdparty \ - /home/davide.faconti/ws_behavior_tree/src/Behavior-Tree/gtest +EXCLUDE = ./3rdparty \ + ./gtest # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded diff --git a/include/behaviortree_cpp/basic_types.h b/include/behaviortree_cpp/basic_types.h index 244f4b2a3..bceef4fd9 100644 --- a/include/behaviortree_cpp/basic_types.h +++ b/include/behaviortree_cpp/basic_types.h @@ -342,6 +342,8 @@ struct Timestamp [[nodiscard]] bool IsAllowedPortName(StringView str); +[[nodiscard]] bool IsReservedAttribute(StringView str); + class TypeInfo { public: diff --git a/include/behaviortree_cpp/tree_node.h b/include/behaviortree_cpp/tree_node.h index edb580dfc..323c8d393 100644 --- a/include/behaviortree_cpp/tree_node.h +++ b/include/behaviortree_cpp/tree_node.h @@ -41,6 +41,7 @@ struct TreeNodeManifest }; using PortsRemapping = std::unordered_map; +using NonPortAttributes = std::unordered_map; enum class PreCond { @@ -53,6 +54,10 @@ enum class PreCond COUNT_ }; +static const std::array PreCondNames = { // + "_failureIf", "_successIf", "_skipIf", "_while" +}; + enum class PostCond { // order of the enums also tell us the execution order @@ -63,11 +68,15 @@ enum class PostCond COUNT_ }; +static const std::array PostCondNames = { // + "_onHalted", "_onFailure", "_onSuccess", "_post" +}; + template <> -[[nodiscard]] std::string toStr(const BT::PostCond& status); +[[nodiscard]] std::string toStr(const BT::PostCond& cond); template <> -[[nodiscard]] std::string toStr(const BT::PreCond& status); +[[nodiscard]] std::string toStr(const BT::PreCond& cond); using ScriptingEnumsRegistry = std::unordered_map; @@ -85,6 +94,10 @@ struct NodeConfig // output ports PortsRemapping output_ports; + // Any other attributes found in the xml that are not parsed as ports + // or built-in identifier (e.g. anything with a leading '_') + NonPortAttributes other_attributes; + const TreeNodeManifest* manifest = nullptr; // Numberic unique identifier diff --git a/src/basic_types.cpp b/src/basic_types.cpp index 088a11015..6cc4bc668 100644 --- a/src/basic_types.cpp +++ b/src/basic_types.cpp @@ -1,4 +1,5 @@ #include "behaviortree_cpp/basic_types.h" +#include "behaviortree_cpp/tree_node.h" #include "behaviortree_cpp/json_export.h" #include @@ -420,10 +421,6 @@ const std::string& PortInfo::defaultValueString() const bool IsAllowedPortName(StringView str) { - if(str == "_autoremap") - { - return true; - } if(str.empty()) { return false; @@ -433,11 +430,26 @@ bool IsAllowedPortName(StringView str) { return false; } - if(str == "name" || str == "ID") + return !IsReservedAttribute(str); +} + +bool IsReservedAttribute(StringView str) +{ + for(const auto& name : PreCondNames) { - return false; + if(name == str) + { + return true; + } + } + for(const auto& name : PostCondNames) + { + if(name == str) + { + return true; + } } - return true; + return str == "name" || str == "ID" || str == "_autoremap"; } Any convertFromJSON(StringView json_text, std::type_index type) diff --git a/src/decorators/retry_node.cpp b/src/decorators/retry_node.cpp index 81b2bb6ec..1a40247dd 100644 --- a/src/decorators/retry_node.cpp +++ b/src/decorators/retry_node.cpp @@ -67,6 +67,8 @@ NodeStatus RetryNode::tick() case NodeStatus::FAILURE: { try_count_++; + // Refresh max_attempts_ in case it changed in one of the child nodes + getInput(NUM_ATTEMPTS, max_attempts_); do_loop = try_count_ < max_attempts_ || max_attempts_ == -1; resetChild(); diff --git a/src/tree_node.cpp b/src/tree_node.cpp index d009fb131..baa62dd7a 100644 --- a/src/tree_node.cpp +++ b/src/tree_node.cpp @@ -468,41 +468,23 @@ void TreeNode::modifyPortsRemapping(const PortsRemapping& new_remapping) } template <> -std::string toStr(const PreCond& pre) +std::string toStr(const PreCond& cond) { - switch(pre) + if(cond < PreCond::COUNT_) { - case PreCond::SUCCESS_IF: - return "_successIf"; - case PreCond::FAILURE_IF: - return "_failureIf"; - case PreCond::SKIP_IF: - return "_skipIf"; - case PreCond::WHILE_TRUE: - return "_while"; - case PreCond::ON_START: - return "_onStart"; - default: - return "Undefined"; + return BT::PreCondNames[static_cast(cond)]; } + return "Undefined"; } template <> -std::string toStr(const PostCond& pre) +std::string toStr(const PostCond& cond) { - switch(pre) + if(cond < BT::PostCond::COUNT_) { - case PostCond::ON_SUCCESS: - return "_onSuccess"; - case PostCond::ON_FAILURE: - return "_onFailure"; - case PostCond::ALWAYS: - return "_post"; - case PostCond::ON_HALTED: - return "_onHalted"; - default: - return "Undefined"; + return BT::PostCondNames[static_cast(cond)]; } + return "Undefined"; } AnyPtrLocked BT::TreeNode::getLockedPortContent(const std::string& key) diff --git a/src/xml_parsing.cpp b/src/xml_parsing.cpp index 31e2f28f7..1286b48b9 100644 --- a/src/xml_parsing.cpp +++ b/src/xml_parsing.cpp @@ -18,6 +18,7 @@ #include #include #include +#include "behaviortree_cpp/basic_types.h" #if defined(_MSVC_LANG) && !defined(__clang__) #define __bt_cplusplus (_MSC_VER == 1900 ? 201103L : _MSVC_LANG) @@ -677,9 +678,13 @@ TreeNode::Ptr XMLParser::PImpl::createNodeFromXML(const XMLElement* element, } PortsRemapping port_remap; + NonPortAttributes other_attributes; + for(const XMLAttribute* att = element->FirstAttribute(); att; att = att->Next()) { - if(IsAllowedPortName(att->Name())) + const std::string port_name = att->Name(); + const std::string port_value = att->Value(); + if(IsAllowedPortName(port_name)) { const std::string port_name = att->Name(); const std::string port_value = att->Value(); @@ -721,6 +726,10 @@ TreeNode::Ptr XMLParser::PImpl::createNodeFromXML(const XMLElement* element, port_remap[port_name] = port_value; } + else if(!IsReservedAttribute(port_name)) + { + other_attributes[port_name] = port_value; + } } NodeConfig config; @@ -738,6 +747,7 @@ TreeNode::Ptr XMLParser::PImpl::createNodeFromXML(const XMLElement* element, if(auto script = element->Attribute(attr_name)) { conditions.insert({ ID, std::string(script) }); + other_attributes.erase(attr_name); } }; @@ -752,6 +762,7 @@ TreeNode::Ptr XMLParser::PImpl::createNodeFromXML(const XMLElement* element, AddCondition(config.post_conditions, toStr(post).c_str(), post); } + config.other_attributes = other_attributes; //--------------------------------------------- TreeNode::Ptr new_node; diff --git a/tests/gtest_ports.cpp b/tests/gtest_ports.cpp index 8ba750919..69c351594 100644 --- a/tests/gtest_ports.cpp +++ b/tests/gtest_ports.cpp @@ -1,4 +1,5 @@ #include +#include "behaviortree_cpp/basic_types.h" #include "behaviortree_cpp/bt_factory.h" #include "behaviortree_cpp/xml_parsing.h" #include "behaviortree_cpp/json_export.h" @@ -129,6 +130,30 @@ TEST(PortTest, Descriptions) ASSERT_EQ(status, NodeStatus::FAILURE); // failure because in_port_B="99" } +TEST(PortsTest, NonPorts) +{ + std::string xml_txt = + R"( + + + + + )"; + + BehaviorTreeFactory factory; + factory.registerNodeType("NodeWithPorts"); + + auto tree = factory.createTreeFromText(xml_txt); + + const TreeNode* root = tree.rootNode(); + ASSERT_NE(root, nullptr); + ASSERT_EQ(root->type(), NodeType::ACTION); + + EXPECT_EQ(root->config().other_attributes.size(), 1); + ASSERT_TRUE(root->config().other_attributes.contains("_not_da_port")); + EXPECT_EQ(root->config().other_attributes.at("_not_da_port"), "whateva"); +} + struct MyType { std::string value;