-
Notifications
You must be signed in to change notification settings - Fork 1.3k
/
Copy pathlcm_py.cc
185 lines (161 loc) · 7.25 KB
/
lcm_py.cc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
#include <cstring>
#include "pybind11/eval.h"
#include "pybind11/pybind11.h"
#include "drake/bindings/pydrake/documentation_pybind.h"
#include "drake/bindings/pydrake/pydrake_pybind.h"
#include "drake/bindings/pydrake/systems/lcm_py_bind_cpp_serializers.h"
#include "drake/lcm/drake_lcm.h"
#include "drake/lcm/drake_lcm_interface.h"
#include "drake/systems/lcm/connect_lcm_scope.h"
#include "drake/systems/lcm/lcm_interface_system.h"
#include "drake/systems/lcm/lcm_publisher_system.h"
#include "drake/systems/lcm/lcm_subscriber_system.h"
#include "drake/systems/lcm/serializer.h"
namespace drake {
namespace pydrake {
using lcm::DrakeLcm;
using lcm::DrakeLcmInterface;
using pysystems::pylcm::BindCppSerializers;
using systems::lcm::SerializerInterface;
namespace {
// pybind11 trampoline class to permit overriding virtual functions in
// Python.
class PySerializerInterface : public py::wrapper<SerializerInterface> {
public:
using Base = py::wrapper<SerializerInterface>;
PySerializerInterface() : Base() {}
// The following methods are for the pybind11 trampoline class to permit C++
// to call the correct Python override. This code path is only activated for
// Python implementations of the class (whose inheritance will pass through
// `PySerializerInterface`). C++ implementations will use the bindings on the
// interface below.
std::unique_ptr<AbstractValue> CreateDefaultValue() const override {
PYBIND11_OVERLOAD_PURE(std::unique_ptr<AbstractValue>, SerializerInterface,
CreateDefaultValue);
}
void Deserialize(const void* message_bytes, int message_length,
AbstractValue* abstract_value) const override {
py::bytes buffer(
reinterpret_cast<const char*>(message_bytes), message_length);
PYBIND11_OVERLOAD_PURE(
void, SerializerInterface, Deserialize, buffer, abstract_value);
}
void Serialize(const AbstractValue& abstract_value,
std::vector<uint8_t>* message_bytes) const override {
auto wrapped = [&]() -> py::bytes {
// N.B. We must pass `abstract_value` as a pointer to prevent `pybind11`
// from copying it.
PYBIND11_OVERLOAD_PURE(
py::bytes, SerializerInterface, Serialize, &abstract_value);
};
std::string str = wrapped();
message_bytes->resize(str.size());
std::copy(str.data(), str.data() + str.size(), message_bytes->data());
}
};
// Define warning related to lack of multiple inheritance with our fork of
// pybind11. This will be sufficient for workflows in pure Python, but will
// not readily support intermingling of C++-constructed LcmInterfaceSystem
// classes which are then passed to Python.
constexpr char kLcmInterfaceSystemClassWarning[] = R"""(
Warning:
In C++, this class inherits both LeafSystem and DrakeLcmInterface. However,
in Python, this only inherits from LeafSystem since our fork of pybind11
does not support multiple inheritance. Additionally, it only exposes the
constructor accepting a DrakeLcmInterface, so that it can still be used in
interface code.
)""";
} // namespace
PYBIND11_MODULE(lcm, m) {
PYDRAKE_PREVENT_PYTHON3_MODULE_REIMPORT(m);
// NOLINTNEXTLINE(build/namespaces): Emulate placement in namespace.
using namespace drake::systems;
// NOLINTNEXTLINE(build/namespaces): Emulate placement in namespace.
using namespace drake::systems::lcm;
constexpr auto& doc = pydrake_doc.drake.systems.lcm;
py::module::import("pydrake.lcm");
py::module::import("pydrake.systems.framework");
{
using Class = LcmInterfaceSystem;
constexpr auto& cls_doc = doc.LcmInterfaceSystem;
py::class_<Class, LeafSystem<double>>(m, "LcmInterfaceSystem",
(std::string(cls_doc.doc) + kLcmInterfaceSystemClassWarning).c_str())
.def(py::init<DrakeLcmInterface*>(),
// Keep alive, reference: `self` keeps `lcm` alive.
py::keep_alive<1, 2>(), py::arg("lcm"), cls_doc.ctor.doc_1args_lcm);
}
{
using Class = SerializerInterface;
constexpr auto& cls_doc = doc.SerializerInterface;
py::class_<Class, PySerializerInterface> cls(m, "SerializerInterface");
cls // BR
// Adding a constructor permits implementing this interface in Python.
.def(py::init(
[]() { return std::make_unique<PySerializerInterface>(); }),
cls_doc.ctor.doc);
// The following bindings are present to allow Python to call C++
// implementations of this interface. Python implementations of the
// interface will call the trampoline implementation methods above.
cls // BR
.def("CreateDefaultValue", &Class::CreateDefaultValue,
cls_doc.CreateDefaultValue.doc)
.def(
"Deserialize",
[](const Class& self, py::bytes message_bytes,
AbstractValue* abstract_value) {
std::string str = message_bytes;
self.Deserialize(str.data(), str.size(), abstract_value);
},
py::arg("message_bytes"), py::arg("abstract_value"),
cls_doc.Deserialize.doc)
.def(
"Serialize",
[](const Class& self, const AbstractValue& abstract_value) {
std::vector<uint8_t> message_bytes;
self.Serialize(abstract_value, &message_bytes);
return py::bytes(
reinterpret_cast<const char*>(message_bytes.data()),
message_bytes.size());
},
py::arg("abstract_value"), cls_doc.Serialize.doc);
}
{
using Class = LcmPublisherSystem;
constexpr auto& cls_doc = doc.LcmPublisherSystem;
py::class_<Class, LeafSystem<double>> cls(m, "LcmPublisherSystem");
cls // BR
.def(py::init<const std::string&, std::unique_ptr<SerializerInterface>,
DrakeLcmInterface*, double>(),
py::arg("channel"), py::arg("serializer"), py::arg("lcm"),
py::arg("publish_period") = 0.0,
// Keep alive, ownership: `serializer` keeps `self` alive.
py::keep_alive<3, 1>(),
// Keep alive, reference: `self` keeps `lcm` alive.
py::keep_alive<1, 4>(), cls_doc.ctor.doc_4args);
}
{
using Class = LcmSubscriberSystem;
constexpr auto& cls_doc = doc.LcmSubscriberSystem;
py::class_<Class, LeafSystem<double>>(m, "LcmSubscriberSystem")
.def(py::init<const std::string&, std::unique_ptr<SerializerInterface>,
DrakeLcmInterface*>(),
py::arg("channel"), py::arg("serializer"), py::arg("lcm"),
// Keep alive, ownership: `serializer` keeps `self` alive.
py::keep_alive<3, 1>(),
// Keep alive, reference: `self` keeps `lcm` alive.
py::keep_alive<1, 4>(), doc.LcmSubscriberSystem.ctor.doc)
.def("WaitForMessage", &Class::WaitForMessage,
py::arg("old_message_count"), py::arg("message") = nullptr,
py::arg("timeout") = -1, cls_doc.WaitForMessage.doc);
}
m.def("ConnectLcmScope", &ConnectLcmScope, py::arg("src"), py::arg("channel"),
py::arg("builder"), py::arg("lcm") = nullptr,
py::arg("publish_period") = 0.0, py::keep_alive<0, 2>(),
// See #11531 for why `py_rvp::reference` is needed.
py_rvp::reference, doc.ConnectLcmScope.doc);
// Bind C++ serializers.
BindCppSerializers();
ExecuteExtraPythonCode(m);
}
} // namespace pydrake
} // namespace drake