11
11
#include < behaviortree_cpp/loggers/groot2_publisher.h>
12
12
#include < behaviortree_cpp/tree_node.h>
13
13
#include < fmt/args.h>
14
+ #include < py_binding_tools/ros_msg_typecasters.h>
14
15
#include < pybind11/chrono.h>
15
16
#include < pybind11/functional.h>
16
17
#include < pybind11/pybind11.h>
22
23
23
24
namespace py = pybind11;
24
25
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
+
25
170
PYBIND11_MODULE (behaviortree_py, m) {
26
171
m.doc () = " Python wrapper for BehaviorTree.CPP" ;
27
172
@@ -48,7 +193,14 @@ PYBIND11_MODULE(behaviortree_py, m) {
48
193
py::class_<BT::TreeNode, std::shared_ptr<BT::TreeNode>>(m, " TreeNode" )
49
194
.def (" name" , &BT::TreeNode::name)
50
195
.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
+ });
52
204
53
205
py::class_<BT::BehaviorTreeFactory>(m, " BehaviorTreeFactory" )
54
206
.def (py::init<>())
@@ -123,8 +275,14 @@ PYBIND11_MODULE(behaviortree_py, m) {
123
275
},
124
276
py::arg (" root_tree" ));
125
277
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" ) = " " );
130
288
}
0 commit comments