1 |
|
|
// Copyright 2010, Florent Lamiraux, Thomas Moulard, LAAS-CNRS. |
2 |
|
|
|
3 |
|
|
#include "dynamic-graph/python/dynamic-graph-py.hh" |
4 |
|
|
|
5 |
|
|
#include <dynamic-graph/command.h> |
6 |
|
|
#include <dynamic-graph/debug.h> |
7 |
|
|
#include <dynamic-graph/entity.h> |
8 |
|
|
#include <dynamic-graph/exception-factory.h> |
9 |
|
|
#include <dynamic-graph/factory.h> |
10 |
|
|
#include <dynamic-graph/pool.h> |
11 |
|
|
#include <dynamic-graph/signal-base.h> |
12 |
|
|
#include <dynamic-graph/signal-time-dependent.h> |
13 |
|
|
#include <dynamic-graph/signal.h> |
14 |
|
|
#include <dynamic-graph/tracer.h> |
15 |
|
|
|
16 |
|
|
#include <Eigen/Geometry> |
17 |
|
|
#include <boost/python.hpp> |
18 |
|
|
#include <boost/python/raw_function.hpp> |
19 |
|
|
#include <boost/python/suite/indexing/map_indexing_suite.hpp> |
20 |
|
|
#include <eigenpy/eigenpy.hpp> |
21 |
|
|
#include <eigenpy/geometry.hpp> |
22 |
|
|
#include <iostream> |
23 |
|
|
#include <sstream> |
24 |
|
|
|
25 |
|
|
#include "dynamic-graph/python/convert-dg-to-py.hh" |
26 |
|
|
#include "dynamic-graph/python/module.hh" |
27 |
|
|
#include "dynamic-graph/python/signal-wrapper.hh" |
28 |
|
|
|
29 |
|
|
namespace dynamicgraph { |
30 |
|
|
namespace python { |
31 |
|
|
|
32 |
|
|
/** |
33 |
|
|
\brief plug a signal into another one. |
34 |
|
|
*/ |
35 |
|
2 |
void plug(SignalBase<int>* signalOut, SignalBase<int>* signalIn) { |
36 |
|
2 |
signalIn->plug(signalOut); |
37 |
|
2 |
} |
38 |
|
|
|
39 |
|
|
void enableTrace(bool enable, const char* filename) { |
40 |
|
|
if (enable) |
41 |
|
|
DebugTrace::openFile(filename); |
42 |
|
|
else |
43 |
|
|
DebugTrace::closeFile(filename); |
44 |
|
|
} |
45 |
|
|
|
46 |
|
|
} // namespace python |
47 |
|
|
} // namespace dynamicgraph |
48 |
|
|
|
49 |
|
|
namespace bp = boost::python; |
50 |
|
|
namespace dg = dynamicgraph; |
51 |
|
|
|
52 |
|
|
typedef bp::return_value_policy<bp::manage_new_object> manage_new_object; |
53 |
|
|
typedef bp::return_value_policy<bp::reference_existing_object> |
54 |
|
|
reference_existing_object; |
55 |
|
|
|
56 |
|
|
typedef dg::PoolStorage::Entities MapOfEntities; |
57 |
|
|
|
58 |
|
|
struct MapOfEntitiesPairToPythonConverter { |
59 |
|
|
static PyObject* convert(const MapOfEntities::value_type& pair) { |
60 |
|
|
return bp::incref(bp::make_tuple(pair.first, bp::ptr(pair.second)).ptr()); |
61 |
|
|
} |
62 |
|
|
}; |
63 |
|
|
|
64 |
|
|
MapOfEntities* getEntityMap() { |
65 |
|
|
return const_cast<MapOfEntities*>( |
66 |
|
|
&dg::PoolStorage::getInstance()->getEntityMap()); |
67 |
|
|
} |
68 |
|
|
|
69 |
|
6 |
dg::SignalBase<int>* getSignal(dg::Entity& e, const std::string& name) { |
70 |
|
6 |
return &e.getSignal(name); |
71 |
|
|
} |
72 |
|
|
|
73 |
|
|
class PythonEntity : public dg::Entity { |
74 |
|
|
DYNAMIC_GRAPH_ENTITY_DECL(); |
75 |
|
|
|
76 |
|
|
public: |
77 |
|
|
using dg::Entity::Entity; |
78 |
|
|
|
79 |
|
|
void signalRegistration(dg::SignalBase<int>& signal) { |
80 |
|
|
dg::Entity::signalRegistration(signal); |
81 |
|
|
} |
82 |
|
|
void signalDeregistration(const std::string& name) { |
83 |
|
|
dg::Entity::signalDeregistration(name); |
84 |
|
|
} |
85 |
|
|
}; |
86 |
|
|
|
87 |
|
|
DYNAMICGRAPH_FACTORY_ENTITY_PLUGIN(PythonEntity, "PythonEntity"); |
88 |
|
|
|
89 |
|
2 |
void exposeEntityBase() { |
90 |
|
|
using namespace dynamicgraph; |
91 |
|
4 |
bp::enum_<LoggerVerbosity>("LoggerVerbosity") |
92 |
✓✗ |
2 |
.value("VERBOSITY_ALL", VERBOSITY_ALL) |
93 |
✓✗ |
2 |
.value("VERBOSITY_INFO_WARNING_ERROR", VERBOSITY_INFO_WARNING_ERROR) |
94 |
✓✗ |
2 |
.value("VERBOSITY_WARNING_ERROR", VERBOSITY_WARNING_ERROR) |
95 |
✓✗ |
2 |
.value("VERBOSITY_ERROR", VERBOSITY_ERROR) |
96 |
✓✗ |
2 |
.value("VERBOSITY_NONE", VERBOSITY_NONE) |
97 |
✓✗ |
2 |
.export_values(); |
98 |
|
|
|
99 |
|
2 |
bp::class_<Entity, boost::noncopyable>("Entity", bp::no_init) |
100 |
|
|
.add_property("name", |
101 |
✓✗ |
2 |
bp::make_function( |
102 |
|
|
&Entity::getName, |
103 |
✓✗ |
4 |
bp::return_value_policy<bp::copy_const_reference>())) |
104 |
|
|
.add_property("className", |
105 |
✓✗ |
2 |
bp::make_function( |
106 |
|
|
&Entity::getClassName, |
107 |
|
2 |
bp::return_value_policy<bp::copy_const_reference>()), |
108 |
✓✗ |
2 |
"the class name of the Entity") |
109 |
✓✗ |
2 |
.add_property("__doc__", &Entity::getDocString) |
110 |
|
|
|
111 |
✓✗ |
2 |
.def("setLoggerVerbosityLevel", &Entity::setLoggerVerbosityLevel) |
112 |
✓✗ |
2 |
.def("getLoggerVerbosityLevel", &Entity::getLoggerVerbosityLevel) |
113 |
|
|
.add_property("loggerVerbosityLevel", &Entity::setLoggerVerbosityLevel, |
114 |
|
|
&Entity::getLoggerVerbosityLevel, |
115 |
✓✗ |
2 |
"the verbosity level of the entity") |
116 |
✓✗ |
2 |
.def("setTimeSample", &Entity::setTimeSample) |
117 |
✓✗ |
2 |
.def("getTimeSample", &Entity::getTimeSample) |
118 |
|
|
.add_property("timeSample", &Entity::getTimeSample, |
119 |
|
|
&Entity::setTimeSample, |
120 |
✓✗ |
2 |
"the time sample for printing debugging information") |
121 |
✓✗ |
2 |
.def("setStreamPrintPeriod", &Entity::setStreamPrintPeriod) |
122 |
✓✗ |
2 |
.def("getStreamPrintPeriod", &Entity::getStreamPrintPeriod) |
123 |
|
|
.add_property("streamPrintPeriod", &Entity::getStreamPrintPeriod, |
124 |
|
|
&Entity::setStreamPrintPeriod, |
125 |
✓✗ |
2 |
"set the period at which debugging information are printed") |
126 |
|
|
|
127 |
|
|
.def( |
128 |
|
|
"__str__", |
129 |
|
|
+[](const Entity& e) -> std::string { |
130 |
|
|
std::ostringstream os; |
131 |
|
|
e.display(os); |
132 |
|
|
return os.str(); |
133 |
✓✗ |
2 |
}) |
134 |
|
|
.def( |
135 |
|
|
"signals", |
136 |
|
1 |
+[](const Entity& e) -> bp::list { |
137 |
|
1 |
bp::list ret; |
138 |
✓✗✓✓ ✓✗✓✗
|
3 |
for (auto& el : e.getSignalMap()) ret.append(bp::ptr(el.second)); |
139 |
|
1 |
return ret; |
140 |
|
|
}, |
141 |
✓✗ |
2 |
"Return the list of signals.") |
142 |
|
|
//.def("signal", +[](Entity& e, const std::string &name) { return |
143 |
|
|
//&e.getSignal(name); }, |
144 |
|
|
// reference_existing_object()) |
145 |
|
|
.def("signal", &getSignal, reference_existing_object(), |
146 |
✓✗✓✗
|
4 |
"get signal by name from an Entity", bp::arg("name")) |
147 |
|
|
.def("hasSignal", &Entity::hasSignal, |
148 |
✓✗ |
2 |
"return True if the entity has a signal with the given name") |
149 |
|
|
|
150 |
|
|
.def( |
151 |
|
|
"displaySignals", |
152 |
|
|
+[](const Entity& e) { |
153 |
|
|
Entity::SignalMap signals(e.getSignalMap()); |
154 |
|
|
std::cout << "--- <" << e.getName(); |
155 |
|
|
if (signals.empty()) |
156 |
|
|
std::cout << "> has no signal\n"; |
157 |
|
|
else |
158 |
|
|
std::cout << "> signal list:\n"; |
159 |
|
|
for (const auto& el : signals) |
160 |
|
|
el.second->display(std::cout << " |-- <") << '\n'; |
161 |
|
|
}, |
162 |
✓✗ |
2 |
"Print the list of signals into standard output: temporary.") |
163 |
|
|
|
164 |
|
|
/* |
165 |
|
|
.def("__getattr__", +[](Entity& e, const std::string &name) -> |
166 |
|
|
SignalBase<int>* { return &e.getSignal(name); }, |
167 |
|
|
reference_existing_object()) |
168 |
|
|
def __getattr__(self, name): |
169 |
|
|
try: |
170 |
|
|
return self.signal(name) |
171 |
|
|
except Exception: |
172 |
|
|
try: |
173 |
|
|
object.__getattr__(self, name) |
174 |
|
|
except AttributeError: |
175 |
|
|
raise AttributeError("'%s' entity has no attribute %s\n" % |
176 |
|
|
(self.name, name) + ' entity attributes are usually either\n' + ' - |
177 |
|
|
commands,\n' + ' - signals or,\n' + ' - user defined attributes') |
178 |
|
|
*/ |
179 |
|
|
/* |
180 |
|
|
.def("__setattr__", +[](bp::object self, const std::string &name, |
181 |
|
|
bp::object value) { Entity& e = bp::extract<Entity&> (self); if |
182 |
|
|
(e.hasSignal(name)) throw std::invalid_argument(name + " already |
183 |
|
|
designates a signal. " "It is not advised to set a new attribute of the |
184 |
|
|
same name."); |
185 |
|
|
// TODO How do you do that ? I am sure it is possible. |
186 |
|
|
//object.__setattr__(self, name, value) |
187 |
|
|
}) |
188 |
|
|
*/ |
189 |
|
|
|
190 |
|
|
/* TODO ? |
191 |
|
|
def boundNewCommand(self, cmdName): |
192 |
|
|
""" |
193 |
|
|
At construction, all existing commands are bound directly in the |
194 |
|
|
class. This method enables to bound new commands dynamically. These new |
195 |
|
|
bounds are not made with the class, but directly with the object instance. |
196 |
|
|
""" |
197 |
|
|
def boundAllNewCommands(self): |
198 |
|
|
""" |
199 |
|
|
For all commands that are not attribute of the object instance nor of |
200 |
|
|
the class, a new attribute of the instance is created to bound the |
201 |
|
|
command. |
202 |
|
|
""" |
203 |
|
|
*/ |
204 |
|
|
|
205 |
|
|
// For backward compat |
206 |
|
|
.add_static_property( |
207 |
|
|
"entities", |
208 |
✓✗✓✗
|
2 |
bp::make_function(&getEntityMap, reference_existing_object())); |
209 |
|
|
|
210 |
|
4 |
python::exposeEntity<PythonEntity, bp::bases<Entity>, 0>() |
211 |
✓✗ |
2 |
.def("signalRegistration", &PythonEntity::signalRegistration) |
212 |
✓✗ |
2 |
.def("signalDeregistration", &PythonEntity::signalDeregistration); |
213 |
|
|
|
214 |
|
2 |
python::exposeEntity<python::PythonSignalContainer, bp::bases<Entity>, 0>() |
215 |
|
|
.def("rmSignal", &python::PythonSignalContainer::rmSignal, |
216 |
✓✗✓✗
|
2 |
"Remove a signal", bp::arg("signal_name")); |
217 |
|
2 |
} |
218 |
|
|
|
219 |
|
2 |
void exposeCommand() { |
220 |
|
|
using dg::command::Command; |
221 |
|
2 |
bp::class_<Command, boost::noncopyable>("Command", bp::no_init) |
222 |
✓✗ |
4 |
.def("__call__", bp::raw_function(dg::python::entity::executeCmd, 1), |
223 |
✓✗ |
2 |
"execute the command") |
224 |
✓✗ |
2 |
.add_property("__doc__", &Command::getDocstring); |
225 |
|
2 |
} |
226 |
|
|
|
227 |
|
2 |
void exposeOldAPI() { |
228 |
✓✗ |
2 |
bp::def("plug", dynamicgraph::python::plug, |
229 |
|
|
"plug an output signal into an input signal", |
230 |
✓✗ |
4 |
(bp::arg("signalOut"), "signalIn")); |
231 |
|
2 |
bp::def("enableTrace", dynamicgraph::python::enableTrace, |
232 |
|
|
"Enable or disable tracing debug info in a file"); |
233 |
|
|
// Signals |
234 |
✓✗ |
2 |
bp::def("create_signal_wrapper", |
235 |
|
|
dynamicgraph::python::signalBase::createSignalWrapper, |
236 |
|
2 |
reference_existing_object(), "create a SignalWrapper C++ object"); |
237 |
|
|
// Entity |
238 |
|
2 |
bp::def("factory_get_entity_class_list", |
239 |
|
|
dynamicgraph::python::factory::getEntityClassList, |
240 |
|
|
"return the list of entity classes"); |
241 |
|
2 |
bp::def("writeGraph", dynamicgraph::python::pool::writeGraph, |
242 |
|
|
"Write the graph of entities in a filename."); |
243 |
|
2 |
bp::def("get_entity_list", dynamicgraph::python::pool::getEntityList, |
244 |
|
|
"return the list of instanciated entities"); |
245 |
|
2 |
bp::def("addLoggerFileOutputStream", |
246 |
|
|
dynamicgraph::python::debug::addLoggerFileOutputStream, |
247 |
|
|
"add a output file stream to the logger by filename"); |
248 |
|
2 |
bp::def("addLoggerCoutOutputStream", |
249 |
|
|
dynamicgraph::python::debug::addLoggerCoutOutputStream, |
250 |
|
|
"add std::cout as output stream to the logger"); |
251 |
|
2 |
bp::def("closeLoggerFileOutputStream", |
252 |
|
|
dynamicgraph::python::debug::closeLoggerFileOutputStream, |
253 |
|
|
"close all the loggers file output streams."); |
254 |
|
2 |
bp::def("real_time_logger_destroy", |
255 |
|
|
dynamicgraph::python::debug::realTimeLoggerDestroy, |
256 |
|
|
"Destroy the real time logger."); |
257 |
|
2 |
bp::def("real_time_logger_spin_once", |
258 |
|
|
dynamicgraph::python::debug::realTimeLoggerSpinOnce, |
259 |
|
|
"Destroy the real time logger."); |
260 |
|
2 |
bp::def("real_time_logger_instance", |
261 |
|
|
dynamicgraph::python::debug::realTimeLoggerInstance, |
262 |
|
|
"Starts the real time logger."); |
263 |
|
2 |
} |
264 |
|
|
|
265 |
|
2 |
void enableEigenPy() { |
266 |
|
2 |
eigenpy::enableEigenPy(); |
267 |
|
|
|
268 |
✓✗ |
2 |
if (!eigenpy::register_symbolic_link_to_registered_type<Eigen::Quaterniond>()) |
269 |
|
2 |
eigenpy::exposeQuaternion(); |
270 |
✓✗ |
2 |
if (!eigenpy::register_symbolic_link_to_registered_type<Eigen::AngleAxisd>()) |
271 |
|
2 |
eigenpy::exposeAngleAxis(); |
272 |
|
|
|
273 |
|
2 |
eigenpy::enableEigenPySpecific<Eigen::Matrix4d>(); |
274 |
|
2 |
} |
275 |
|
|
|
276 |
✓✗✓✗
|
8 |
BOOST_PYTHON_MODULE(wrap) { |
277 |
|
4 |
enableEigenPy(); |
278 |
|
|
|
279 |
|
4 |
exposeOldAPI(); |
280 |
|
|
|
281 |
|
4 |
dg::python::exposeSignals(); |
282 |
|
4 |
exposeEntityBase(); |
283 |
|
4 |
exposeCommand(); |
284 |
|
|
|
285 |
|
|
typedef dg::PoolStorage::Entities MapOfEntities; |
286 |
|
4 |
bp::class_<MapOfEntities>("MapOfEntities") |
287 |
✓✗ |
4 |
.def("__len__", &MapOfEntities::size) |
288 |
|
|
.def( |
289 |
|
|
"keys", |
290 |
|
|
+[](const MapOfEntities& m) -> bp::tuple { |
291 |
|
|
bp::list res; |
292 |
|
|
for (const auto& el : m) res.append(el.first); |
293 |
|
|
return bp::tuple(res); |
294 |
✓✗ |
4 |
}) |
295 |
|
|
.def( |
296 |
|
|
"values", |
297 |
|
|
+[](const MapOfEntities& m) -> bp::tuple { |
298 |
|
|
bp::list res; |
299 |
|
|
for (const auto& el : m) res.append(bp::ptr(el.second)); |
300 |
|
|
return bp::tuple(res); |
301 |
✓✗ |
4 |
}) |
302 |
|
|
.def("__getitem__", |
303 |
|
|
static_cast<dg::Entity*& (MapOfEntities::*)(const std::string& k)>( |
304 |
|
|
&MapOfEntities::at), |
305 |
✓✗ |
4 |
reference_existing_object()) |
306 |
|
|
.def( |
307 |
|
|
"__setitem__", +[](MapOfEntities& m, const std::string& n, |
308 |
✓✗ |
4 |
dg::Entity* e) { m.emplace(n, e); }) |
309 |
✓✗✓✗
|
8 |
.def("__iter__", bp::iterator<MapOfEntities>()) |
310 |
|
|
.def( |
311 |
|
|
"__contains__", |
312 |
|
|
+[](const MapOfEntities& m, const std::string& n) -> bool { |
313 |
|
|
return m.count(n); |
314 |
✓✗ |
4 |
}); |
315 |
|
4 |
bp::to_python_converter<MapOfEntities::value_type, |
316 |
|
|
MapOfEntitiesPairToPythonConverter>(); |
317 |
|
4 |
} |