dynamic-graph-py.cc 11 KB
Newer Older
Thomas Moulard's avatar
Thomas Moulard committed
1
// Copyright 2010, Florent Lamiraux, Thomas Moulard, LAAS-CNRS.
florent's avatar
florent committed
2
3

#include <iostream>
florent's avatar
florent committed
4
#include <sstream>
florent's avatar
florent committed
5

Joseph Mirabel's avatar
Joseph Mirabel committed
6
7
8
9
10
11
12
#include <boost/python.hpp>
#include <boost/python/suite/indexing/map_indexing_suite.hpp>

#include <eigenpy/eigenpy.hpp>
#include <Eigen/Geometry>
#include <eigenpy/geometry.hpp>

13
#include <dynamic-graph/debug.h>
14
#include <dynamic-graph/exception-factory.h>
florent's avatar
florent committed
15
#include <dynamic-graph/signal-base.h>
Joseph Mirabel's avatar
Joseph Mirabel committed
16
17
18
19
#include <dynamic-graph/signal.h>
#include <dynamic-graph/signal-time-dependent.h>
#include <dynamic-graph/entity.h>
#include <dynamic-graph/command.h>
Joseph Mirabel's avatar
Joseph Mirabel committed
20
#include <dynamic-graph/factory.h>
Joseph Mirabel's avatar
Joseph Mirabel committed
21
22
23
#include <dynamic-graph/pool.h>

#include <dynamic-graph/tracer.h>
florent's avatar
florent committed
24

25
26
#include "dynamic-graph/python/dynamic-graph-py.hh"
#include "dynamic-graph/python/signal-wrapper.hh"
Joseph Mirabel's avatar
Joseph Mirabel committed
27
#include "dynamic-graph/python/convert-dg-to-py.hh"
Joseph Mirabel's avatar
Joseph Mirabel committed
28
#include "dynamic-graph/python/module.hh"
29

30
namespace dynamicgraph {
Guilhem Saurel's avatar
format    
Guilhem Saurel committed
31
32
33
34
35
namespace python {

/**
   \brief plug a signal into another one.
*/
Guilhem Saurel's avatar
Format    
Guilhem Saurel committed
36
void plug(SignalBase<int>* signalOut, SignalBase<int>* signalIn) { signalIn->plug(signalOut); }
florent's avatar
florent committed
37

Guilhem Saurel's avatar
Format    
Guilhem Saurel committed
38
void enableTrace(bool enable, const char* filename) {
Joseph Mirabel's avatar
Joseph Mirabel committed
39
40
41
42
  if (enable)
    DebugTrace::openFile(filename);
  else
    DebugTrace::closeFile(filename);
Guilhem Saurel's avatar
format    
Guilhem Saurel committed
43
}
florent's avatar
florent committed
44

Joseph Mirabel's avatar
Joseph Mirabel committed
45
46
47
48
49
50
51
52
53
54
55
56
57
58
}  // namespace python
}  // namespace dynamicgraph

namespace bp = boost::python;
namespace dg = dynamicgraph;

typedef bp::return_value_policy<bp::manage_new_object> manage_new_object;
typedef bp::return_value_policy<bp::reference_existing_object> reference_existing_object;

typedef dg::PoolStorage::Entities MapOfEntities;

struct MapOfEntitiesPairToPythonConverter {
  static PyObject* convert(const MapOfEntities::value_type& pair) {
    return bp::incref(bp::make_tuple(pair.first, bp::ptr(pair.second)).ptr());
59
  }
Joseph Mirabel's avatar
Joseph Mirabel committed
60
61
};

Guilhem Saurel's avatar
Format    
Guilhem Saurel committed
62
MapOfEntities* getEntityMap() { return const_cast<MapOfEntities*>(&dg::PoolStorage::getInstance()->getEntityMap()); }
63

Guilhem Saurel's avatar
Format    
Guilhem Saurel committed
64
dg::SignalBase<int>* getSignal(dg::Entity& e, const std::string& name) { return &e.getSignal(name); }
65

Guilhem Saurel's avatar
Format    
Guilhem Saurel committed
66
67
68
69
class PythonEntity : public dg::Entity {
  DYNAMIC_GRAPH_ENTITY_DECL();

 public:
Joseph Mirabel's avatar
Joseph Mirabel committed
70
71
  using dg::Entity::Entity;

Guilhem Saurel's avatar
Format    
Guilhem Saurel committed
72
73
  void signalRegistration(dg::SignalBase<int>& signal) { dg::Entity::signalRegistration(signal); }
  void signalDeregistration(const std::string& name) { dg::Entity::signalDeregistration(name); }
Joseph Mirabel's avatar
Joseph Mirabel committed
74
75
76
77
};

DYNAMICGRAPH_FACTORY_ENTITY_PLUGIN(PythonEntity, "PythonEntity");

Guilhem Saurel's avatar
Format    
Guilhem Saurel committed
78
void exposeEntityBase() {
Joseph Mirabel's avatar
Joseph Mirabel committed
79
  using namespace dynamicgraph;
Guilhem Saurel's avatar
Format    
Guilhem Saurel committed
80
81
82
83
84
85
86
  bp::enum_<LoggerVerbosity>("LoggerVerbosity")
      .value("VERBOSITY_ALL", VERBOSITY_ALL)
      .value("VERBOSITY_INFO_WARNING_ERROR", VERBOSITY_INFO_WARNING_ERROR)
      .value("VERBOSITY_WARNING_ERROR", VERBOSITY_WARNING_ERROR)
      .value("VERBOSITY_ERROR", VERBOSITY_ERROR)
      .value("VERBOSITY_NONE", VERBOSITY_NONE)
      .export_values();
Joseph Mirabel's avatar
Joseph Mirabel committed
87
88

  bp::class_<Entity, boost::noncopyable>("Entity", bp::no_init)
Guilhem Saurel's avatar
Format    
Guilhem Saurel committed
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
      .add_property("name", bp::make_function(&Entity::getName, bp::return_value_policy<bp::copy_const_reference>()))
      .add_property("className",
                    bp::make_function(&Entity::getClassName, bp::return_value_policy<bp::copy_const_reference>()),
                    "the class name of the Entity")
      .add_property("__doc__", &Entity::getDocString)

      .def("setLoggerVerbosityLevel", &Entity::setLoggerVerbosityLevel)
      .def("getLoggerVerbosityLevel", &Entity::getLoggerVerbosityLevel)
      .add_property("loggerVerbosityLevel", &Entity::setLoggerVerbosityLevel, &Entity::getLoggerVerbosityLevel,
                    "the verbosity level of the entity")
      .def("setTimeSample", &Entity::setTimeSample)
      .def("getTimeSample", &Entity::getTimeSample)
      .add_property("timeSample", &Entity::getTimeSample, &Entity::setTimeSample,
                    "the time sample for printing debugging information")
      .def("setStreamPrintPeriod", &Entity::setStreamPrintPeriod)
      .def("getStreamPrintPeriod", &Entity::getStreamPrintPeriod)
      .add_property("streamPrintPeriod", &Entity::getStreamPrintPeriod, &Entity::setStreamPrintPeriod,
                    "set the period at which debugging information are printed")

      .def("__str__",
           +[](const Entity& e) -> std::string {
             std::ostringstream os;
             e.display(os);
             return os.str();
           })
      .def("signals",
           +[](const Entity& e) -> bp::list {
             bp::list ret;
             for (auto& el : e.getSignalMap()) ret.append(bp::ptr(el.second));
             return ret;
           },
           "Return the list of signals.")
      //.def("signal", +[](Entity& e, const std::string &name) { return &e.getSignal(name); },
      // reference_existing_object())
      .def("signal", &getSignal, reference_existing_object(), "get signal by name from an Entity", bp::arg("name"))
      .def("hasSignal", &Entity::hasSignal, "return True if the entity has a signal with the given name")

      .def("displaySignals",
           +[](const Entity& e) {
             Entity::SignalMap signals(e.getSignalMap());
             std::cout << "--- <" << e.getName();
             if (signals.empty())
               std::cout << "> has no signal\n";
             else
               std::cout << "> signal list:\n";
             for (const auto& el : signals) el.second->display(std::cout << "    |-- <") << '\n';
           },
           "Print the list of signals into standard output: temporary.")

      /*
      .def("__getattr__", +[](Entity& e, const std::string &name) -> SignalBase<int>* { return &e.getSignal(name); },
          reference_existing_object())
      def __getattr__(self, name):
          try:
              return self.signal(name)
          except Exception:
              try:
                  object.__getattr__(self, name)
              except AttributeError:
                  raise AttributeError("'%s' entity has no attribute %s\n" % (self.name, name) +
                                       '  entity attributes are usually either\n' + '    - commands,\n' +
                                       '    - signals or,\n' + '    - user defined attributes')
                                       */
      /*
      .def("__setattr__", +[](bp::object self, const std::string &name, bp::object value) {
            Entity& e = bp::extract<Entity&> (self);
            if (e.hasSignal(name))
              throw std::invalid_argument(name + " already designates a signal. "
                  "It is not advised to set a new attribute of the same name.");
            // TODO How do you do that ? I am sure it is possible.
            //object.__setattr__(self, name, value)
          })
          */

      /* TODO ?
      def boundNewCommand(self, cmdName):
          """
          At construction, all existing commands are bound directly in the class.
          This method enables to bound new commands dynamically. These new bounds
          are not made with the class, but directly with the object instance.
          """
      def boundAllNewCommands(self):
          """
          For all commands that are not attribute of the object instance nor of the
          class, a new attribute of the instance is created to bound the command.
          """
          */

      // For backward compat
      .add_static_property("entities", bp::make_function(&getEntityMap, reference_existing_object()));

  python::exposeEntity<PythonEntity, bp::bases<Entity>, 0>()
Joseph Mirabel's avatar
Joseph Mirabel committed
181
      .def("signalRegistration", &PythonEntity::signalRegistration)
Guilhem Saurel's avatar
Format    
Guilhem Saurel committed
182
      .def("signalDeregistration", &PythonEntity::signalDeregistration);
Joseph Mirabel's avatar
Joseph Mirabel committed
183

Guilhem Saurel's avatar
Format    
Guilhem Saurel committed
184
185
  python::exposeEntity<python::PythonSignalContainer, bp::bases<Entity>, 0>().def(
      "rmSignal", &python::PythonSignalContainer::rmSignal, "Remove a signal", bp::arg("signal_name"));
Joseph Mirabel's avatar
Joseph Mirabel committed
186
187
}

Guilhem Saurel's avatar
Format    
Guilhem Saurel committed
188
void exposeCommand() {
Joseph Mirabel's avatar
Joseph Mirabel committed
189
  using dg::command::Command;
Guilhem Saurel's avatar
Format    
Guilhem Saurel committed
190
191
192
  bp::class_<Command, boost::noncopyable>("Command", bp::no_init)
      .def("__call__", bp::raw_function(dg::python::entity::executeCmd, 1), "execute the command")
      .add_property("__doc__", &Command::getDocstring);
Joseph Mirabel's avatar
Joseph Mirabel committed
193
}
florent's avatar
florent committed
194

Guilhem Saurel's avatar
Format    
Guilhem Saurel committed
195
196
197
198
void exposeOldAPI() {
  bp::def("plug", dynamicgraph::python::plug, "plug an output signal into an input signal",
          (bp::arg("signalOut"), "signalIn"));
  bp::def("enableTrace", dynamicgraph::python::enableTrace, "Enable or disable tracing debug info in a file");
Joseph Mirabel's avatar
Joseph Mirabel committed
199
  // Signals
Guilhem Saurel's avatar
Format    
Guilhem Saurel committed
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
  bp::def("create_signal_wrapper", dynamicgraph::python::signalBase::createSignalWrapper, reference_existing_object(),
          "create a SignalWrapper C++ object");
  // Entity
  bp::def("factory_get_entity_class_list", dynamicgraph::python::factory::getEntityClassList,
          "return the list of entity classes");
  bp::def("writeGraph", dynamicgraph::python::pool::writeGraph, "Write the graph of entities in a filename.");
  bp::def("get_entity_list", dynamicgraph::python::pool::getEntityList, "return the list of instanciated entities");
  bp::def("addLoggerFileOutputStream", dynamicgraph::python::debug::addLoggerFileOutputStream,
          "add a output file stream to the logger by filename");
  bp::def("addLoggerCoutOutputStream", dynamicgraph::python::debug::addLoggerCoutOutputStream,
          "add std::cout as output stream to the logger");
  bp::def("closeLoggerFileOutputStream", dynamicgraph::python::debug::closeLoggerFileOutputStream,
          "close all the loggers file output streams.");
  bp::def("real_time_logger_destroy", dynamicgraph::python::debug::realTimeLoggerDestroy,
          "Destroy the real time logger.");
  bp::def("real_time_logger_spin_once", dynamicgraph::python::debug::realTimeLoggerSpinOnce,
          "Destroy the real time logger.");
  bp::def("real_time_logger_instance", dynamicgraph::python::debug::realTimeLoggerInstance,
          "Starts the real time logger.");
Joseph Mirabel's avatar
Joseph Mirabel committed
219
220
}

Guilhem Saurel's avatar
Format    
Guilhem Saurel committed
221
void enableEigenPy() {
Joseph Mirabel's avatar
Joseph Mirabel committed
222
  eigenpy::enableEigenPy();
Guilhem Saurel's avatar
Format    
Guilhem Saurel committed
223
224
225

  if (!eigenpy::register_symbolic_link_to_registered_type<Eigen::Quaterniond>()) eigenpy::exposeQuaternion();
  if (!eigenpy::register_symbolic_link_to_registered_type<Eigen::AngleAxisd>()) eigenpy::exposeAngleAxis();
226

Joseph Mirabel's avatar
Joseph Mirabel committed
227
  eigenpy::enableEigenPySpecific<Eigen::Matrix4d>();
florent's avatar
florent committed
228
}
229

Guilhem Saurel's avatar
Format    
Guilhem Saurel committed
230
BOOST_PYTHON_MODULE(wrap) {
Joseph Mirabel's avatar
Joseph Mirabel committed
231
232
233
234
235
236
237
238
239
240
  enableEigenPy();

  exposeOldAPI();

  dg::python::exposeSignals();
  exposeEntityBase();
  exposeCommand();

  typedef dg::PoolStorage::Entities MapOfEntities;
  bp::class_<MapOfEntities>("MapOfEntities")
Guilhem Saurel's avatar
Format    
Guilhem Saurel committed
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
      .def("__len__", &MapOfEntities::size)
      .def("keys",
           +[](const MapOfEntities& m) -> bp::tuple {
             bp::list res;
             for (const auto& el : m) res.append(el.first);
             return bp::tuple(res);
           })
      .def("values",
           +[](const MapOfEntities& m) -> bp::tuple {
             bp::list res;
             for (const auto& el : m) res.append(bp::ptr(el.second));
             return bp::tuple(res);
           })
      .def("__getitem__", static_cast<dg::Entity*& (MapOfEntities::*)(const std::string& k)>(&MapOfEntities::at),
           reference_existing_object())
      .def("__setitem__", +[](MapOfEntities& m, const std::string& n, dg::Entity* e) { m.emplace(n, e); })
      .def("__iter__", bp::iterator<MapOfEntities>())
      .def("__contains__", +[](const MapOfEntities& m, const std::string& n) -> bool { return m.count(n); });
  bp::to_python_converter<MapOfEntities::value_type, MapOfEntitiesPairToPythonConverter>();
Joseph Mirabel's avatar
Joseph Mirabel committed
260
}