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
#include <boost/python.hpp>
Guilhem Saurel's avatar
Guilhem Saurel committed
7
#include <boost/python/raw_function.hpp>
Joseph Mirabel's avatar
Joseph Mirabel committed
8
9
10
11
12
13
#include <boost/python/suite/indexing/map_indexing_suite.hpp>

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

14
#include <dynamic-graph/debug.h>
15
#include <dynamic-graph/exception-factory.h>
florent's avatar
florent committed
16
#include <dynamic-graph/signal-base.h>
Joseph Mirabel's avatar
Joseph Mirabel committed
17
18
19
20
#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
21
#include <dynamic-graph/factory.h>
Joseph Mirabel's avatar
Joseph Mirabel committed
22
23
24
#include <dynamic-graph/pool.h>

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

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

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

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

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

Joseph Mirabel's avatar
Joseph Mirabel committed
46
47
48
49
50
51
52
53
54
55
56
57
58
59
}  // 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());
60
  }
Joseph Mirabel's avatar
Joseph Mirabel committed
61
62
};

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

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

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

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

Guilhem Saurel's avatar
Format    
Guilhem Saurel committed
73
74
  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
75
76
77
78
};

DYNAMICGRAPH_FACTORY_ENTITY_PLUGIN(PythonEntity, "PythonEntity");

Guilhem Saurel's avatar
Format    
Guilhem Saurel committed
79
void exposeEntityBase() {
Joseph Mirabel's avatar
Joseph Mirabel committed
80
  using namespace dynamicgraph;
Guilhem Saurel's avatar
Format    
Guilhem Saurel committed
81
82
83
84
85
86
87
  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
88
89

  bp::class_<Entity, boost::noncopyable>("Entity", bp::no_init)
Guilhem Saurel's avatar
Format    
Guilhem Saurel committed
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
      .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
182
      .def("signalRegistration", &PythonEntity::signalRegistration)
Guilhem Saurel's avatar
Format    
Guilhem Saurel committed
183
      .def("signalDeregistration", &PythonEntity::signalDeregistration);
Joseph Mirabel's avatar
Joseph Mirabel committed
184

Guilhem Saurel's avatar
Format    
Guilhem Saurel committed
185
186
  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
187
188
}

Guilhem Saurel's avatar
Format    
Guilhem Saurel committed
189
void exposeCommand() {
Joseph Mirabel's avatar
Joseph Mirabel committed
190
  using dg::command::Command;
Guilhem Saurel's avatar
Format    
Guilhem Saurel committed
191
192
193
  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
194
}
florent's avatar
florent committed
195

Guilhem Saurel's avatar
Format    
Guilhem Saurel committed
196
197
198
199
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
200
  // Signals
Guilhem Saurel's avatar
Format    
Guilhem Saurel committed
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
  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
220
221
}

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

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

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

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

  exposeOldAPI();

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

  typedef dg::PoolStorage::Entities MapOfEntities;
  bp::class_<MapOfEntities>("MapOfEntities")
Guilhem Saurel's avatar
Format    
Guilhem Saurel committed
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
      .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
261
}