diff --git a/include/behaviortree_cpp/basic_types.h b/include/behaviortree_cpp/basic_types.h index 244f4b2a3..9dc98a902 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 IsNodeNameAttribute(StringView str); + class TypeInfo { public: diff --git a/include/behaviortree_cpp/tree_node.h b/include/behaviortree_cpp/tree_node.h index 597572ea4..64cf722d1 100644 --- a/include/behaviortree_cpp/tree_node.h +++ b/include/behaviortree_cpp/tree_node.h @@ -40,6 +40,7 @@ struct TreeNodeManifest }; using PortsRemapping = std::unordered_map; +using OtherAttributes = std::unordered_map; enum class PreCond { @@ -83,6 +84,9 @@ struct NodeConfig // output ports PortsRemapping output_ports; + // Any other attributes found in the xml that are not parsed as ports (e.g. anything with a leading '_') + OtherAttributes other_attributes; + const TreeNodeManifest* manifest = nullptr; // Numberic unique identifier diff --git a/src/basic_types.cpp b/src/basic_types.cpp index 088a11015..8cb38cb96 100644 --- a/src/basic_types.cpp +++ b/src/basic_types.cpp @@ -433,11 +433,12 @@ bool IsAllowedPortName(StringView str) { return false; } - if(str == "name" || str == "ID") - { - return false; - } - return true; + return !IsNodeNameAttribute(str); +} + +bool IsNodeNameAttribute(StringView str) +{ + return str == "name" || str == "ID"; } Any convertFromJSON(StringView json_text, std::type_index type) diff --git a/src/xml_parsing.cpp b/src/xml_parsing.cpp index 83a94ee12..7e83ae3de 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) @@ -654,13 +655,13 @@ TreeNode::Ptr XMLParser::PImpl::createNodeFromXML(const XMLElement* element, } PortsRemapping port_remap; + OtherAttributes 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(); - if(manifest) { auto port_model_it = manifest->ports.find(port_name); @@ -696,6 +697,10 @@ TreeNode::Ptr XMLParser::PImpl::createNodeFromXML(const XMLElement* element, port_remap[port_name] = port_value; } + else if(!IsNodeNameAttribute(port_name)) + { + other_attributes[port_name] = port_value; + } } NodeConfig config; @@ -713,6 +718,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); } }; @@ -727,6 +733,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 24a5dbd85..637adfe01 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;