dynamic-graph-py.cc 9.93 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
20
21
22
#include <dynamic-graph/signal.h>
#include <dynamic-graph/signal-time-dependent.h>
#include <dynamic-graph/entity.h>
#include <dynamic-graph/command.h>
#include <dynamic-graph/pool.h>

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

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

28
namespace dynamicgraph {
Guilhem Saurel's avatar
format    
Guilhem Saurel committed
29
30
31
32
33
namespace python {

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

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

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

MapOfEntities* getEntityMap() {
  return const_cast<MapOfEntities*>(&dg::PoolStorage::getInstance()->getEntityMap());
florent's avatar
florent committed
66
}
67

Joseph Mirabel's avatar
Joseph Mirabel committed
68
69
dg::SignalBase<int>* getSignal(dg::Entity& e, const std::string& name) {
  return &e.getSignal(name);
Guilhem Saurel's avatar
Guilhem Saurel committed
70
}
71

Joseph Mirabel's avatar
Joseph Mirabel committed
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
void exposeEntityBase()
{
  using namespace dynamicgraph;
  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();


  bp::class_<Entity, boost::noncopyable>("Entity", bp::no_init)
    .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.")
florent's avatar
florent committed
135

Joseph Mirabel's avatar
Joseph Mirabel committed
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
    /*
    .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)
        })
        */
160

Joseph Mirabel's avatar
Joseph Mirabel committed
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
    /* 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()))
    ;
}

void exposeCommand()
181
{
Joseph Mirabel's avatar
Joseph Mirabel committed
182
183
184
185
186
187
  using dg::command::Command;
  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)
    ;
}
florent's avatar
florent committed
188

Joseph Mirabel's avatar
Joseph Mirabel committed
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
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");
  // Signals
  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.");
}

void enableEigenPy()
{
  eigenpy::enableEigenPy();
  
  if(!eigenpy::register_symbolic_link_to_registered_type<Eigen::Quaterniond>())
    eigenpy::exposeQuaternion();
  if(!eigenpy::register_symbolic_link_to_registered_type<Eigen::AngleAxisd>())
    eigenpy::exposeAngleAxis();
224

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

Joseph Mirabel's avatar
Joseph Mirabel committed
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
BOOST_PYTHON_MODULE(wrap)
{
  enableEigenPy();

  exposeOldAPI();

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

  typedef dg::PoolStorage::Entities MapOfEntities;
  bp::class_<MapOfEntities>("MapOfEntities")
    .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 >();
}