1111#include < behaviortree_cpp/loggers/groot2_publisher.h>
1212#include < behaviortree_cpp/tree_node.h>
1313#include < fmt/args.h>
14+ #include < py_binding_tools/ros_msg_typecasters.h>
1415#include < pybind11/chrono.h>
1516#include < pybind11/functional.h>
1617#include < pybind11/pybind11.h>
2223
2324namespace py = pybind11;
2425
26+ // Mention that it's copied from MTC
27+ namespace {
28+
29+ /* * In order to assign new property values in Python, we need to convert the
30+ *Python object to a boost::any instance of the correct type. As the C++ type
31+ *cannot be inferred from the Python type, we can support this assignment only
32+ *for a few basic types (see fromPython()) as well as ROS message types. For
33+ *other types, a generic assignment via stage.properties["property"] = value is
34+ *not possible. Instead, use the .property<Type> declaration on the stage to
35+ *allow for direct assignment like this: stage.property = value
36+ **/
37+ class PropertyConverterRegistry {
38+ using to_python_converter_function = pybind11::object (*)(const BT::Any&);
39+ using from_python_converter_function = BT::Any (*)(const pybind11::object&);
40+
41+ struct Entry {
42+ to_python_converter_function to_;
43+ from_python_converter_function from_;
44+ };
45+
46+ // map from type_index to corresponding converter functions
47+ typedef std::map<std::type_index, Entry> RegistryMap;
48+ RegistryMap types_;
49+ // map from ros-msg-names to entry in types_
50+ using RosMsgTypeNameMap = std::map<std::string, RegistryMap::iterator>;
51+ RosMsgTypeNameMap msg_names_;
52+
53+ public:
54+ PropertyConverterRegistry ();
55+
56+ inline bool insert (const std::type_index& type_index,
57+ const std::string& ros_msg_name,
58+ to_python_converter_function to,
59+ from_python_converter_function from);
60+
61+ static py::object toPython (const BT::Any& value);
62+
63+ static BT::Any fromPython (const py::object& bpo);
64+ };
65+
66+ inline constexpr static PropertyConverterRegistry REGISTRY_SINGLETON;
67+
68+ // / utility class to register C++ / Python converters for a property of type T
69+ template <typename T>
70+ class PropertyConverter {
71+ public:
72+ PropertyConverter () { REGISTRY_SINGLETON.insert (typeid (T), rosMsgName<T>(), &toPython, &fromPython); }
73+
74+ private:
75+ static pybind11::object toPython (const BT::Any& value) { return pybind11::cast (value.cast <T>()); }
76+
77+ static BT::Any fromPython (const pybind11::object& po) { return BT::Any (pybind11::cast<T>(po)); }
78+
79+ template <class Q = T>
80+ typename std::enable_if<rosidl_generator_traits::is_message<Q>::value, std::string>::type rosMsgName () {
81+ return rosidl_generator_traits::name<Q>();
82+ }
83+
84+ template <class Q = T>
85+ typename std::enable_if<!rosidl_generator_traits::is_message<Q>::value, std::string>::type rosMsgName () {
86+ return std::string ();
87+ }
88+ };
89+
90+ PropertyConverterRegistry::PropertyConverterRegistry () {
91+ // register property converters
92+ PropertyConverter<bool >();
93+ PropertyConverter<int >();
94+ PropertyConverter<unsigned int >();
95+ PropertyConverter<long >();
96+ PropertyConverter<float >();
97+ PropertyConverter<double >();
98+ PropertyConverter<std::string>();
99+ PropertyConverter<std::set<std::string>>();
100+ PropertyConverter<std::map<std::string, double >>();
101+ }
102+
103+ bool PropertyConverterRegistry::insert (const std::type_index& type_index,
104+ const std::string& ros_msg_name,
105+ to_python_converter_function to,
106+ from_python_converter_function from) {
107+ auto it_inserted = types_.insert (std::make_pair (type_index, Entry{to, from}));
108+ if (!it_inserted.second ) return false ;
109+
110+ if (!ros_msg_name.empty ()) // is this a ROS msg type?
111+ msg_names_.insert (std::make_pair (ros_msg_name, it_inserted.first ));
112+
113+ return true ;
114+ }
115+
116+ py::object PropertyConverterRegistry::toPython (const BT::Any& value) {
117+ if (value.empty ()) return py::object ();
118+ for (const auto & [name, entry] : REGISTRY_SINGLETON.msg_names_ ) {
119+ std::cout << name << " " << BT::demangle (entry->first ) << std::endl;
120+ }
121+
122+ auto it = REGISTRY_SINGLETON.types_ .find (value.type ());
123+ if (it == REGISTRY_SINGLETON.types_ .end ()) {
124+ std::string name = BT::demangle (value.type ());
125+ throw py::type_error (" No Python -> C++ conversion for: " + name);
126+ }
127+
128+ return it->second .to_ (value);
129+ }
130+
131+ std::string rosMsgName (PyObject* object) {
132+ py::object o = py::reinterpret_borrow<py::object>(object);
133+ auto cls = o.attr (" __class__" );
134+ auto name = cls.attr (" __name__" ).cast <std::string>();
135+ auto module = cls.attr (" __module__" ).cast <std::string>();
136+ auto pos = module .find (" .msg" );
137+ if (pos == std::string::npos)
138+ // object is not a ROS message type, return it's class name instead
139+ return module + " ." + name;
140+ else
141+ return module .substr (0 , pos) + " /msg/" + name;
142+ }
143+
144+ BT::Any PropertyConverterRegistry::fromPython (const py::object& po) {
145+ PyObject* o = po.ptr ();
146+
147+ if (PyBool_Check (o)) return BT::Any ((o == Py_True));
148+ if (PyLong_Check (o)) return BT::Any (PyLong_AS_LONG (o));
149+ if (PyFloat_Check (o)) return BT::Any (PyFloat_AS_DOUBLE (o));
150+ if (PyUnicode_Check (o)) return BT::Any (py::cast<std::string>(o));
151+
152+ const std::string& ros_msg_name = rosMsgName (o);
153+ auto it = REGISTRY_SINGLETON.msg_names_ .find (ros_msg_name);
154+ if (it == REGISTRY_SINGLETON.msg_names_ .end ())
155+ throw py::type_error (" No C++ conversion available for (property) type: " + ros_msg_name);
156+
157+ return it->second ->second .from_ (po);
158+ }
159+
160+ } // end anonymous namespace
161+
162+ // WTF??
163+ namespace BT {
164+ template <>
165+ inline BT::Any convertFromString<BT::Any>(BT::StringView str) {
166+ return BT::Any (str);
167+ }
168+ } // namespace BT
169+
25170PYBIND11_MODULE (behaviortree_py, m) {
26171 m.doc () = " Python wrapper for BehaviorTree.CPP" ;
27172
@@ -48,7 +193,14 @@ PYBIND11_MODULE(behaviortree_py, m) {
48193 py::class_<BT::TreeNode, std::shared_ptr<BT::TreeNode>>(m, " TreeNode" )
49194 .def (" name" , &BT::TreeNode::name)
50195 .def (" status" , &BT::TreeNode::status)
51- .def (" type" , &BT::TreeNode::type);
196+ .def (" type" , &BT::TreeNode::type)
197+ .def (" get_input" ,
198+ [](BT::TreeNode* self, const std::string& key) {
199+ return PropertyConverterRegistry::toPython (self->getInput <BT::Any>(key).value ());
200+ })
201+ .def (" set_output" , [](BT::TreeNode* self, const std::string& key, py::object value) {
202+ return self->setOutput (key, PropertyConverterRegistry::fromPython (value));
203+ });
52204
53205 py::class_<BT::BehaviorTreeFactory>(m, " BehaviorTreeFactory" )
54206 .def (py::init<>())
@@ -123,8 +275,14 @@ PYBIND11_MODULE(behaviortree_py, m) {
123275 },
124276 py::arg (" root_tree" ));
125277
126- bind_port_methods<int >(m, " int" );
127- bind_port_methods<double >(m, " double" );
128- bind_port_methods<std::string>(m, " string" );
129- bind_port_methods<bool >(m, " bool" );
278+ m.def (
279+ " input_port" ,
280+ [](BT::StringView name, BT::StringView description) { return BT::InputPort<BT::Any>(name, description); },
281+ py::arg (" name" ),
282+ py::arg (" description" ) = " " )
283+ .def (
284+ " output_port" ,
285+ [](BT::StringView name, BT::StringView description) { return BT::OutputPort<BT::Any>(name, description); },
286+ py::arg (" name" ),
287+ py::arg (" description" ) = " " );
130288}
0 commit comments