GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: src/dynamic_graph/dynamic-graph-py.cc Lines: 91 130 70.0 %
Date: 2022-09-30 08:22:45 Branches: 55 162 34.0 %

Line Branch Exec Source
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
}