Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • ostasse/dynamic-graph-python
  • gsaurel/dynamic-graph-python
  • stack-of-tasks/dynamic-graph-python
3 results
Show changes
Showing
with 408 additions and 844 deletions
// -*- mode: c++ -*-
// Copyright 2011, Florent Lamiraux, CNRS.
//
// This file is part of dynamic-graph-python.
// dynamic-graph is free software: you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public License
// as published by the Free Software Foundation, either version 3 of
// the License, or (at your option) any later version.
//
// dynamic-graph is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Lesser Public License for more details. You should have
// received a copy of the GNU Lesser General Public License along with
// dynamic-graph. If not, see <http://www.gnu.org/licenses/>.
#ifdef WIN32
#include <Windows.h>
#else
#include <dlfcn.h>
#endif
namespace dynamicgraph {
namespace python {
std::string libpython("@PYTHON_LIBRARY@");
} // namespace python
} // namespace dynamicgraph
// Copyright 2011, 2012, Florent Lamiraux, LAAS-CNRS.
//
// This file is part of dynamic-graph-python.
// dynamic-graph-python is free software: you can redistribute it
// and/or modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation, either
// version 3 of the License, or (at your option) any later version.
//
// dynamic-graph-python is distributed in the hope that it will be
// useful, but WITHOUT ANY WARRANTY; without even the implied warranty
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Lesser Public License for more details. You should
// have received a copy of the GNU Lesser General Public License along
// with dynamic-graph. If not, see <http://www.gnu.org/licenses/>.
#include <Python.h>
#include <dynamic-graph/pool.h>
#include <dynamic-graph/entity.h>
#include <vector>
#include "exception.hh"
namespace dynamicgraph {
namespace python {
extern PyObject* dgpyError;
namespace pool {
PyObject* writeGraph (PyObject* /*self*/, PyObject* args)
{
char* filename;
if (!PyArg_ParseTuple(args, "s", &filename))
return NULL;
try {
PoolStorage::getInstance()->writeGraph (filename);
} CATCH_ALL_EXCEPTIONS();
return Py_BuildValue ("");
}
/**
\brief Get list of entities
*/
PyObject* getEntityList(PyObject* /*self*/, PyObject* args)
{
if (!PyArg_ParseTuple(args, ""))
return NULL;
std::vector <std::string> entityNames;
try {
const PoolStorage::Entities & listOfEntities=
dynamicgraph::PoolStorage::getInstance()
->getEntityMap();
Py_ssize_t classNumber = listOfEntities.size();
// Build a tuple object
PyObject* classTuple = PyTuple_New(classNumber);
Py_ssize_t iEntity = 0;
for (PoolStorage::Entities::const_iterator entity_it =
listOfEntities.begin();
entity_it != listOfEntities.end();
++entity_it)
{
const std::string & aname = entity_it->second->getName();
PyObject* className =
Py_BuildValue("s", aname.c_str());
PyTuple_SetItem(classTuple, iEntity, className);
iEntity++;
}
return Py_BuildValue("O", classTuple);
} CATCH_ALL_EXCEPTIONS();
return NULL;
}
} // python
} // namespace pool
} // dynamicgraph
// Copyright 2010, Florent Lamiraux, Thomas Moulard, LAAS-CNRS.
//
// This file is part of dynamic-graph-python.
// dynamic-graph-python is free software: you can redistribute it
// and/or modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation, either
// version 3 of the License, or (at your option) any later version.
//
// dynamic-graph-python is distributed in the hope that it will be
// useful, but WITHOUT ANY WARRANTY; without even the implied warranty
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Lesser Public License for more details. You should
// have received a copy of the GNU Lesser General Public License along
// with dynamic-graph. If not, see <http://www.gnu.org/licenses/>.
#include <Python.h>
#include <iostream>
#include <sstream>
#include <dynamic-graph/signal-base.h>
#include <dynamic-graph/signal.h>
#include <dynamic-graph/signal-ptr.h>
#include <dynamic-graph/signal-caster.h>
#include <dynamic-graph/linear-algebra.h>
#include <dynamic-graph/pool.h>
#include <dynamic-graph/factory.h>
#include "convert-dg-to-py.hh"
#include "exception.hh"
#include "signal-wrapper.hh"
using dynamicgraph::SignalBase;
namespace dynamicgraph {
namespace python {
extern PyObject* dgpyError;
using namespace convert;
namespace signalBase {
static void destroy (void* self);
/**
\brief Create an instance of SignalBase
*/
PyObject* create(PyObject* /*self*/, PyObject* args)
{
char *name = NULL;
if (!PyArg_ParseTuple(args, "s", &name))
return NULL;
SignalBase<int>* obj = NULL;
obj = new SignalBase<int>(std::string(name));
// Return the pointer
return PyCObject_FromVoidPtr((void*)obj, destroy);
}
template <class T> SignalWrapper<T, int>* createSignalWrapperTpl (const char* name, PyObject* o, std::string& error)
{
typedef SignalWrapper<T, int> SignalWrapper_t;
if (!SignalWrapper_t::checkCallable (o, error)) {
return NULL;
}
SignalWrapper_t* obj = new SignalWrapper_t(name, o);
return obj;
}
PythonSignalContainer* getPythonSignalContainer ()
{
const std::string instanceName = "python_signals";
const std::string className = "PythonSignalContainer";
Entity* obj;
if( PoolStorage::getInstance()->existEntity(instanceName, obj))
{
if( obj->getClassName()!=className ) {
std::string msg ("Found an object named "
+ std::string(instanceName)
+ ",\n""but this object is of type "
+ std::string(obj->getClassName())
+ " and not "
+ std::string(className));
PyErr_SetString(dgpyError, msg.c_str());
return NULL;
}
} else {
try {
obj = FactoryStorage::getInstance()->newEntity
(std::string(className), std::string(instanceName));
} CATCH_ALL_EXCEPTIONS();
}
return dynamic_cast<PythonSignalContainer*>(obj);
}
#define SIGNAL_WRAPPER_TYPE(IF, Enum, Type) \
IF (command::Value::typeName(command::Value::Enum) \
.compare(type) == 0) { \
obj = createSignalWrapperTpl<Type> (name, object, error); \
}
/**
\brief Create an instance of SignalWrapper
*/
PyObject* createSignalWrapper(PyObject* /*self*/, PyObject* args)
{
PythonSignalContainer* psc = getPythonSignalContainer();
if (psc == NULL)
return NULL;
char *name = NULL;
char *type = NULL;
PyObject* object = NULL;
if (!PyArg_ParseTuple(args, "ssO", &name, &type, &object))
return NULL;
SignalBase<int>* obj = NULL;
std::string error;
SIGNAL_WRAPPER_TYPE( if, BOOL ,bool)
// SIGNAL_WRAPPER_TYPE(else if, UNSIGNED ,bool)
SIGNAL_WRAPPER_TYPE(else if, INT ,int )
SIGNAL_WRAPPER_TYPE(else if, FLOAT ,float )
SIGNAL_WRAPPER_TYPE(else if, DOUBLE ,double)
// SIGNAL_WRAPPER_TYPE(else if, STRING ,bool)
SIGNAL_WRAPPER_TYPE(else if, VECTOR ,Vector)
// SIGNAL_WRAPPER_TYPE(else if, MATRIX ,bool)
// SIGNAL_WRAPPER_TYPE(else if, MATRIX4D ,bool)
else {
error = "Type not understood";
}
if (obj == NULL) {
PyErr_SetString(dgpyError, error.c_str());
return NULL;
}
// Register signal into the python signal container
psc->signalRegistration(*obj);
// Return the pointer
return PyCObject_FromVoidPtr((void*)obj, destroy);
}
/**
\brief Destroy an instance of InvertedPendulum
*/
static void destroy (void* self)
{
SignalBase<int>* obj = (SignalBase<int>*)self;
delete obj;
}
PyObject* getTime(PyObject* /*self*/, PyObject* args)
{
void* pointer = NULL;
PyObject* object = NULL;
if (!PyArg_ParseTuple(args,"O", &object))
return NULL;
if (!PyCObject_Check(object))
return NULL;
pointer = PyCObject_AsVoidPtr(object);
SignalBase<int>* obj = (SignalBase<int>*)pointer;
int time = obj->getTime();
return Py_BuildValue("i", time);
}
PyObject* setTime(PyObject* /*self*/, PyObject* args)
{
void* pointer = NULL;
PyObject* object = NULL;
int time;
if (!PyArg_ParseTuple(args,"Oi", &object, &time))
return NULL;
if (!PyCObject_Check(object)) {
PyErr_SetString(dgpyError, "object should be a C object");
return NULL;
}
pointer = PyCObject_AsVoidPtr(object);
SignalBase<int>* obj = (SignalBase<int>*)pointer;
obj->setTime(time);
return Py_BuildValue("");
}
PyObject* display(PyObject* /*self*/, PyObject* args)
{
void* pointer = NULL;
PyObject* object = NULL;
if (!PyArg_ParseTuple(args,"O", &object))
return NULL;
if (!PyCObject_Check(object))
return NULL;
pointer = PyCObject_AsVoidPtr(object);
SignalBase<int>* obj = (SignalBase<int>*)pointer;
std::ostringstream oss;
try {
obj->display(oss);
} CATCH_ALL_EXCEPTIONS ();
return Py_BuildValue("s", oss.str().c_str());
}
PyObject* displayDependencies(PyObject* /*self*/, PyObject* args)
{
void* pointer = NULL;
PyObject* object = NULL;
int time;
if (!PyArg_ParseTuple(args,"OI", &object,&time))
return NULL;
if (!PyCObject_Check(object))
return NULL;
pointer = PyCObject_AsVoidPtr(object);
SignalBase<int>* obj = (SignalBase<int>*)pointer;
std::ostringstream oss;
try {
obj->displayDependencies(oss,time);
} CATCH_ALL_EXCEPTIONS ();
return Py_BuildValue("s", oss.str().c_str());
}
PyObject* getValue(PyObject* /*self*/, PyObject* args)
{
void* pointer = NULL;
PyObject* object = NULL;
if (!PyArg_ParseTuple(args,"O", &object))
return NULL;
if (!PyCObject_Check(object))
return NULL;
pointer = PyCObject_AsVoidPtr(object);
SignalBase<int>* signal = (SignalBase<int>*)pointer;
try {
{ // --- VECTOR SIGNALS -----------------
// Two cases: the signal embeds directly a vector, or embeds
// an object deriving from vector.In the first case,
// the signal is directly cast into sig<vector>.
// In the second case, the derived object can be access as a vector
// using the signal-ptr<vector> type.
Signal<dynamicgraph::Vector,int> * sigvec
= dynamic_cast< Signal<dynamicgraph::Vector,int>* >( signal );
if( NULL!= sigvec ) {
return vectorToPython( sigvec->accessCopy() );
}
// Extraction of object derinving from vector: plug signal into
// a vector signal and get the value from the signal-ptr instead
// of the original vector.
SignalPtr<dynamicgraph::Vector,int> sigptr(NULL,"vector-caster");
try {
sigptr.plug(signal);
return vectorToPython( sigptr.accessCopy() );
}
catch( dynamicgraph::ExceptionSignal& ex ) {
if( ex.getCode() !=
dynamicgraph::ExceptionSignal::PLUG_IMPOSSIBLE )
throw;
}
}
{ // --- MATRIX SIGNALS --------------------
// Two cases: the signal embeds directly a matrix, or embeds
// an object deriving from matrix.In the first case,
// the signal is directly cast into sig<matrix>.
// In the second case, the derived object can be access as a matrix
// using the signal-ptr<matrix> type.
Signal<dynamicgraph::Matrix,int> * sigmat
= dynamic_cast< Signal<dynamicgraph::Matrix,int>* >( signal );
if( NULL!= sigmat ) {
return matrixToPython( sigmat->accessCopy() );
}
SignalPtr<dynamicgraph::Matrix,int> sigptr(NULL,"matrix-caster");
try {
sigptr.plug(signal);
return matrixToPython( sigptr.accessCopy() );
}
catch( dynamicgraph::ExceptionSignal& ex ) {
if( ex.getCode() !=
dynamicgraph::ExceptionSignal::PLUG_IMPOSSIBLE )
throw;
}
}
{ // --- HOMOGENEOUS MATRIX SIGNALS --------------------
// Two cases: the signal embeds directly a matrix, or embeds
// an object deriving from matrix.In the first case,
// the signal is directly cast into sig<matrix>.
// In the second case, the derived object can be access as a matrix
// using the signal-ptr<matrix> type.
//TODO: See if matrix homogeneous can be properly put in linear-algebra.h
typedef Eigen::Transform<double,3, Eigen::Affine> MatrixHomogeneous;
Signal<MatrixHomogeneous,int> * sigmat
= dynamic_cast< Signal<MatrixHomogeneous,int>* >( signal );
if( NULL!= sigmat ) {
return matrixToPython( sigmat->accessCopy().matrix() );
}
SignalPtr<Eigen::Transform<double,3, Eigen::Affine>,int> sigptr(NULL,"matrix-caster");
try {
sigptr.plug(signal);
return matrixToPython( sigptr.accessCopy().matrix() );
}
catch( dynamicgraph::ExceptionSignal& ex ) {
if( ex.getCode() !=
dynamicgraph::ExceptionSignal::PLUG_IMPOSSIBLE )
throw;
}
}
Signal<double,int> * sigdouble
= dynamic_cast< Signal<double,int>* >( signal );
if( NULL!= sigdouble )
{
return Py_BuildValue("d", sigdouble->accessCopy() );
}
} CATCH_ALL_EXCEPTIONS ();
/* Non specific signal: use a generic way. */
std::ostringstream value;
try {
signal->get(value);
} CATCH_ALL_EXCEPTIONS ();
std::string valueString = value.str();
return Py_BuildValue("s", valueString.c_str());
}
PyObject* getName(PyObject* /*self*/, PyObject* args)
{
void* pointer = NULL;
PyObject* object = NULL;
if (!PyArg_ParseTuple(args,"O", &object))
return NULL;
if (!PyCObject_Check(object))
return NULL;
pointer = PyCObject_AsVoidPtr(object);
SignalBase<int>* signal = (SignalBase<int>*)pointer;
std::string name;
try {
name = signal->getName();
} CATCH_ALL_EXCEPTIONS ();
return Py_BuildValue("s", name.c_str());
}
PyObject* getClassName(PyObject* /*self*/, PyObject* args)
{
void* pointer = NULL;
PyObject* object = NULL;
if (!PyArg_ParseTuple(args,"O", &object))
return NULL;
if (!PyCObject_Check(object))
return NULL;
pointer = PyCObject_AsVoidPtr(object);
SignalBase<int>* signal = (SignalBase<int>*)pointer;
std::string name;
try {
signal->getClassName(name);
} CATCH_ALL_EXCEPTIONS ();
return Py_BuildValue("s", name.c_str());
}
PyObject* setValue(PyObject* /*self*/, PyObject* args)
{
void * pointer = NULL;
PyObject* object = NULL;
char* valueString = NULL;
if (!PyArg_ParseTuple(args,"Os", &object, &valueString))
return NULL;
if (!PyCObject_Check(object))
return NULL;
pointer = PyCObject_AsVoidPtr(object);
SignalBase<int>* signal = (SignalBase<int>*)pointer;
std::ostringstream os;
os << valueString;
std::istringstream value(os.str());
try {
signal->set(value);
} CATCH_ALL_EXCEPTIONS ();
return Py_BuildValue("");
}
PyObject* recompute(PyObject* /*self*/, PyObject* args)
{
void * pointer = NULL;
PyObject* object = NULL;
unsigned int time;
if (!PyArg_ParseTuple(args,"OI", &object, &time))
return NULL;
if (!PyCObject_Check(object))
return NULL;
pointer = PyCObject_AsVoidPtr(object);
SignalBase<int>* signal = (SignalBase<int>*)pointer;
try {
signal->recompute(time);
} CATCH_ALL_EXCEPTIONS ();
return Py_BuildValue("");
}
PyObject* unplug(PyObject* /*self*/, PyObject* args)
{
void * pointer = NULL;
PyObject* object = NULL;
if (!PyArg_ParseTuple(args,"O", &object))
return NULL;
if (!PyCObject_Check(object))
return NULL;
pointer = PyCObject_AsVoidPtr(object);
SignalBase<int>* signal = (SignalBase<int>*)pointer;
try {
signal->unplug();
} CATCH_ALL_EXCEPTIONS ();
return Py_BuildValue("");
}
PyObject* isPlugged (PyObject*, PyObject* args)
{
void * pointer = NULL;
PyObject* object = NULL;
if (!PyArg_ParseTuple(args,"O", &object))
return NULL;
if (!PyCObject_Check(object))
return NULL;
pointer = PyCObject_AsVoidPtr(object);
SignalBase<int>* signal = (SignalBase<int>*)pointer;
bool plugged = false;
try {
plugged = signal->isPlugged();
} CATCH_ALL_EXCEPTIONS ();
if (plugged) return PyBool_FromLong(1); else return PyBool_FromLong(0);
}
PyObject* getPlugged (PyObject*, PyObject* args)
{
void * pointer = NULL;
PyObject* object = NULL;
if (!PyArg_ParseTuple(args,"O", &object))
return NULL;
if (!PyCObject_Check(object))
return NULL;
pointer = PyCObject_AsVoidPtr(object);
SignalBase<int>* signal = (SignalBase<int>*)pointer;
SignalBase<int>* otherSignal = 0;
try {
bool plugged = signal->isPlugged ();
otherSignal = signal->getPluged();
if (!plugged || otherSignal == 0) {
std::string msg = std::string ("Signal ") + signal->getName()
+ std::string (" is not plugged.");
throw std::runtime_error (msg);
}
} CATCH_ALL_EXCEPTIONS ();
// Return the pointer to the signal without destructor since the signal
// is not owned by the calling object.
return PyCObject_FromVoidPtr((void*)otherSignal, NULL);
}
} // namespace signalBase
} // namespace python
} // namespace dynamicgraph
// Copyright 2010, Florent Lamiraux, Thomas Moulard, LAAS-CNRS.
//
// This file is part of dynamic-graph-python.
// dynamic-graph-python is free software: you can redistribute it
// and/or modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation, either
// version 3 of the License, or (at your option) any later version.
//
// dynamic-graph-python is distributed in the hope that it will be
// useful, but WITHOUT ANY WARRANTY; without even the implied warranty
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Lesser Public License for more details. You should
// have received a copy of the GNU Lesser General Public License along
// with dynamic-graph. If not, see <http://www.gnu.org/licenses/>.
#include <Python.h>
#include <iostream>
#include <sstream>
#include <dynamic-graph/signal-caster.h>
namespace dynamicgraph {
namespace python {
namespace signalCaster {
PyObject* getSignalTypeList(PyObject* /*self*/, PyObject* args)
{
if (!PyArg_ParseTuple(args, ""))
return NULL;
std::vector<std::string> typeList =
dynamicgraph::SignalCaster::getInstance()->listTypenames();
Py_ssize_t typeNumber = typeList.size();
// Build a tuple object
PyObject* typeTuple = PyTuple_New(typeNumber);
for (Py_ssize_t iType = 0; iType < typeNumber; ++iType)
{
PyObject* className =
Py_BuildValue("s", typeList[iType].c_str());
PyTuple_SetItem(typeTuple, iType, className);
}
return Py_BuildValue("O", typeTuple);
}
} //namespace signalCaster
} // namespace dynamicgraph
} // namespace python
// Copyright (c) 2018, Joseph Mirabel
// Authors: Joseph Mirabel (joseph.mirabel@laas.fr)
//
// This file is part of dynamic-graph-python.
// dynamic-graph-python is free software: you can redistribute it
// and/or modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation, either version
// 3 of the License, or (at your option) any later version.
//
// dynamic-graph-python is distributed in the hope that it will be
// useful, but WITHOUT ANY WARRANTY; without even the implied warranty
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Lesser Public License for more details. You should have
// received a copy of the GNU Lesser General Public License along with
// dynamic-graph-python. If not, see <http://www.gnu.org/licenses/>.
#include "signal-wrapper.hh"
#include <Python.h>
#include <dynamic-graph/factory.h>
#include <dynamic-graph/command-bind.h>
namespace dynamicgraph {
namespace python {
namespace signalWrapper {
void convert (PyObject* o, bool & v) { v = (o == Py_True); }
void convert (PyObject* o, int & v) { v = (int)PyInt_AS_LONG (o); }
void convert (PyObject* o, float & v) { v = (float)PyFloat_AS_DOUBLE (o); }
void convert (PyObject* o, double& v) { v = PyFloat_AS_DOUBLE (o); }
void convert (PyObject* o, Vector & v)
{
v.resize(PyTuple_Size(o));
for (int i = 0; i < v.size(); ++i)
convert(PyTuple_GetItem(o,i), v[i]);
}
}
PythonSignalContainer::PythonSignalContainer(const std::string& name)
: Entity (name)
{
std::string docstring;
docstring = " \n"
" Remove a signal\n"
" \n"
" Input:\n"
" - name of the signal\n"
" \n";
addCommand("rmSignal",
command::makeCommandVoid1(*this,&PythonSignalContainer::rmSignal,docstring));
}
void PythonSignalContainer::signalRegistration (const SignalArray<int>& signals)
{
Entity::signalRegistration (signals);
}
void PythonSignalContainer::rmSignal (const std::string& name)
{
Entity::signalDeregistration (name);
}
DYNAMICGRAPH_FACTORY_ENTITY_PLUGIN(PythonSignalContainer, "PythonSignalContainer");
template <class T, class Time>
bool SignalWrapper<T,Time>::checkCallable (PyObject* c, std::string& error)
{
if (PyCallable_Check(c) == 0) {
PyObject* str = PyObject_Str(c);
error = PyString_AsString(str);
error += " is not callable";
Py_DECREF(str);
return false;
}
return true;
}
template class SignalWrapper<bool , int>;
template class SignalWrapper<int , int>;
template class SignalWrapper<float , int>;
template class SignalWrapper<double, int>;
template class SignalWrapper<Vector, int>;
} // namespace dynamicgraph
} // namespace python
// Copyright (c) 2018, Joseph Mirabel
// Authors: Joseph Mirabel (joseph.mirabel@laas.fr)
//
// This file is part of dynamic-graph-python.
// dynamic-graph-python is free software: you can redistribute it
// and/or modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation, either version
// 3 of the License, or (at your option) any later version.
//
// dynamic-graph-python is distributed in the hope that it will be
// useful, but WITHOUT ANY WARRANTY; without even the implied warranty
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Lesser Public License for more details. You should have
// received a copy of the GNU Lesser General Public License along with
// dynamic-graph-python. If not, see <http://www.gnu.org/licenses/>.
#include <Python.h>
#include <dynamic-graph/linear-algebra.h>
#include <dynamic-graph/signal.h>
#include <dynamic-graph/entity.h>
namespace dynamicgraph {
namespace python {
namespace signalWrapper {
void convert (PyObject* o, int & v);
void convert (PyObject* o, bool & v);
void convert (PyObject* o, float & v);
void convert (PyObject* o, double& v);
// void convert (PyObject* o, std::string& v);
void convert (PyObject* o, Vector & v);
// void convert (PyObject* o, Eigen::MatrixXd& v);
// void convert (PyObject* o, Eigen::Matrix4d& v);
}
class PythonSignalContainer : public Entity
{
DYNAMIC_GRAPH_ENTITY_DECL();
public:
PythonSignalContainer (const std::string& name);
void signalRegistration (const SignalArray<int>& signals);
void rmSignal (const std::string& name);
};
template <class T, class Time>
class SignalWrapper : public Signal<T, Time>
{
public:
typedef Signal<T,Time> parent_t;
static bool checkCallable (PyObject* c, std::string& error);
SignalWrapper (std::string name, PyObject* _callable) :
parent_t (name)
, callable (_callable)
{
typedef boost::function2<T&,T&,Time> function_t;
Py_INCREF(callable);
function_t f = boost::bind (&SignalWrapper::call, this, _1, _2);
this->setFunction (f);
}
virtual ~SignalWrapper ()
{
Py_DECREF(callable);
};
private:
T& call (T& value, Time t)
{
PyGILState_STATE gstate;
gstate = PyGILState_Ensure();
if (PyGILState_GetThisThreadState() == NULL) {
dgDEBUG(10) << "python thread not initialized" << std::endl;
}
char format[] = "i";
PyObject* obj = PyObject_CallFunction(callable, format, t);
if (obj == NULL) {
dgERROR << "Could not call callable" << std::endl;
} else {
signalWrapper::convert (obj, value);
Py_DECREF(obj);
}
PyGILState_Release (gstate);
return value;
}
PyObject* callable;
};
} // namespace dynamicgraph
} // namespace python
# Copyright 2010-2020, Florent Lamiraux, Thomas Moulard, Olivier Stasse, Guilhem
# Saurel, JRL, CNRS/AIST, LAAS-CNRS
# Test the interpreter
add_unit_test(interpreter-test interpreter-test.cc)
target_link_libraries(interpreter-test PRIVATE ${PROJECT_NAME})
# Test runfile
add_unit_test(interpreter-test-runfile interpreter-test-runfile.cc)
target_link_libraries(interpreter-test-runfile PRIVATE ${PROJECT_NAME})
target_include_directories(interpreter-test-runfile
PRIVATE Boost::unit_test_framework)
target_compile_definitions(interpreter-test-runfile
PRIVATE PATH="${CMAKE_CURRENT_LIST_DIR}/")
# Test the module generation Create an entity
set(LIBRARY_NAME "custom_entity")
add_library(${LIBRARY_NAME} SHARED "${LIBRARY_NAME}.cpp")
if(SUFFIX_SO_VERSION)
set_target_properties(${LIBRARY_NAME} PROPERTIES SOVERSION ${PROJECT_VERSION})
endif(SUFFIX_SO_VERSION)
target_link_libraries(${LIBRARY_NAME} PRIVATE dynamic-graph::dynamic-graph)
# Create its bindings This mimics DYNAMIC_GRAPH_PYTHON_MODULE(${LIBRARY_NAME}
# ${LIBRARY_NAME} "${LIBRARY_NAME}-wrap")
configure_file(
${PROJECT_SOURCE_DIR}/cmake/dynamic_graph/submodule/__init__.py.cmake
${CMAKE_CURRENT_BINARY_DIR}/${LIBRARY_NAME}/__init__.py)
set(PYTHON_MODULE "${LIBRARY_NAME}-wrap")
set(DYNAMICGRAPH_MODULE_HEADER
"${CMAKE_SOURCE_DIR}/tests/custom_entity_module.h")
configure_file(${PROJECT_SOURCE_DIR}/cmake/dynamic_graph/python-module-py.cc.in
${CMAKE_CURRENT_BINARY_DIR}/python-module-py.cc @ONLY)
add_library(${PYTHON_MODULE} MODULE
${CMAKE_CURRENT_BINARY_DIR}/python-module-py.cc)
set_target_properties(${PYTHON_MODULE}
PROPERTIES PREFIX "" OUTPUT_NAME ${LIBRARY_NAME}/wrap)
if(UNIX AND NOT APPLE)
target_link_libraries(${PYTHON_MODULE} PRIVATE "-Wl,--no-as-needed")
endif(UNIX AND NOT APPLE)
target_link_libraries(${PYTHON_MODULE} PRIVATE ${LIBRARY_NAME} ${PROJECT_NAME})
# Test it
add_python_unit_test("test-custom-entity" "tests/test_custom_entity.py" src
tests)
# also test other bindings, using this custom entity
add_python_unit_test("test-bindings" "tests/test_bindings.py" src tests)
/* Copyright 2010-2019 LAAS, CNRS
* Thomas Moulard.
*
*/
#define ENABLE_RT_LOG
#include "custom_entity.h"
#include <dynamic-graph/command-bind.h>
#include <dynamic-graph/entity.h>
#include <dynamic-graph/exception-factory.h>
#include <dynamic-graph/factory.h>
#include <dynamic-graph/pool.h>
#include <dynamic-graph/real-time-logger.h>
#include <dynamic-graph/signal-ptr.h>
#include <dynamic-graph/signal-time-dependent.h>
#include <boost/bind.hpp>
namespace dynamicgraph {
CustomEntity::CustomEntity(const std::string n)
: Entity(n),
m_sigdSIN(NULL, "CustomEntity(" + name + ")::input(double)::in_double"),
m_sigdTimeDepSOUT(boost::bind(&CustomEntity::update, this, _1, _2),
m_sigdSIN,
"CustomEntity(" + name + ")::input(double)::out_double")
{
addSignal();
using namespace dynamicgraph::command;
this->addCommand("act",
makeCommandVoid0(*this, &CustomEntity::act,
docCommandVoid0("act on input signal")));
}
void CustomEntity::addSignal() {
signalRegistration(m_sigdSIN << m_sigdTimeDepSOUT);
}
void CustomEntity::rmValidSignal() {
signalDeregistration("in_double");
signalDeregistration("out_double");
}
double &CustomEntity::update(double &res, const int &inTime) {
const double &aDouble = m_sigdSIN(inTime);
res = aDouble;
logger().stream(MSG_TYPE_ERROR) << "start update " << res << '\n';
DYNAMIC_GRAPH_ENTITY_DEBUG(*this)
<< "This is a message of level MSG_TYPE_DEBUG\n";
DYNAMIC_GRAPH_ENTITY_INFO(*this)
<< "This is a message of level MSG_TYPE_INFO\n";
DYNAMIC_GRAPH_ENTITY_WARNING(*this)
<< "This is a message of level MSG_TYPE_WARNING\n";
DYNAMIC_GRAPH_ENTITY_ERROR(*this)
<< "This is a message of level MSG_TYPE_ERROR\n";
DYNAMIC_GRAPH_ENTITY_DEBUG_STREAM(*this)
<< "This is a message of level MSG_TYPE_DEBUG_STREAM\n";
DYNAMIC_GRAPH_ENTITY_INFO_STREAM(*this)
<< "This is a message of level MSG_TYPE_INFO_STREAM\n";
DYNAMIC_GRAPH_ENTITY_WARNING_STREAM(*this)
<< "This is a message of level MSG_TYPE_WARNING_STREAM\n";
DYNAMIC_GRAPH_ENTITY_ERROR_STREAM(*this)
<< "This is a message of level MSG_TYPE_ERROR_STREAM\n";
logger().stream(MSG_TYPE_ERROR) << "end update\n";
return res;
}
void CustomEntity::act() { m_sigdSIN.accessCopy(); }
DYNAMICGRAPH_FACTORY_ENTITY_PLUGIN(CustomEntity, "CustomEntity");
} // namespace dynamicgraph
/* Copyright 2020 LAAS, CNRS
* Joseph Mirabel
*
*/
#define ENABLE_RT_LOG
#include <dynamic-graph/entity.h>
#include <dynamic-graph/signal-ptr.h>
#include <dynamic-graph/signal-time-dependent.h>
#include <sstream>
namespace dynamicgraph {
class CustomEntity : public Entity {
public:
dynamicgraph::SignalPtr<double, int> m_sigdSIN;
dynamicgraph::SignalTimeDependent<double, int> m_sigdTimeDepSOUT;
DYNAMIC_GRAPH_ENTITY_DECL();
CustomEntity(const std::string n);
void addSignal();
void rmValidSignal();
double &update(double &res, const int &inTime);
void act();
};
} // namespace dynamicgraph
#include "custom_entity.h"
typedef boost::mpl::vector<dynamicgraph::CustomEntity> entities_t;
...@@ -2,10 +2,10 @@ ...@@ -2,10 +2,10 @@
* Copyright * Copyright
*/ */
#include "dynamic-graph-python-test.hh"
#include <iostream> #include <iostream>
#include "dynamic-graph-python-test.hh"
GraphTest::GraphTest() GraphTest::GraphTest() {
{
std::cout << "Constructor of unitTesting object of class Graph." << std::endl; std::cout << "Constructor of unitTesting object of class Graph." << std::endl;
} }
/*
* Copyright
*/
#include "dynamic-graph-python-test.hh"
#include <iostream>
GraphTest::GraphTest() {
std::cout << "Constructor of unitTesting object of class Graph." << std::endl;
}
...@@ -5,19 +5,19 @@ ...@@ -5,19 +5,19 @@
#ifndef DYNAMIC_GRAPH_PYTHON_HH #ifndef DYNAMIC_GRAPH_PYTHON_HH
#define DYNAMIC_GRAPH_PYTHON_HH #define DYNAMIC_GRAPH_PYTHON_HH
#include "dynamic-graph-python/dynamic-graph-python.hh" #include "dynamic-graph-python/dynamic-graph-python.hh"
/** /**
\brief UnitTesting class of class Graph \brief UnitTesting class of class Graph
*/ */
class GraphTest class GraphTest {
{ public:
public:
/** /**
\brief Constructor \brief Constructor
*/ */
GraphTest(); GraphTest();
private:
private:
dynamic::Graph graph_; dynamic::Graph graph_;
}; };
......
// The purpose of this unit test is to check the interpreter::runPythonFile method // The purpose of this unit test is to check the interpreter::runPythonFile
// method
#include <cstring> #include <cstring>
#include <iostream> #include <iostream>
#include "dynamic-graph/python/interpreter.hh" #include "dynamic-graph/python/interpreter.hh"
bool testFile(const std::string & filename, bool testFile(const std::string& filename, const std::string& expectedOutput,
const std::string & expectedOutput, int numTest) {
int numTest)
{
std::string err = ""; std::string err = "";
dynamicgraph::python::Interpreter interp; dynamicgraph::python::Interpreter interp;
for (int i=0; i<numTest; ++i) for (int i = 0; i < numTest; ++i) {
{
interp.runPythonFile(filename, err); interp.runPythonFile(filename, err);
if (err != expectedOutput) if (err != expectedOutput) {
{ std::cerr << "At iteration " << i
std::cerr << "At iteration " << i << ", the output was not the one expected:" << std::endl; << ", the output was not the one expected:" << std::endl;
std::cerr << " expected: " << expectedOutput << std::endl; std::cerr << " expected: " << expectedOutput << std::endl;
std::cerr << " err: " << err << std::endl; std::cerr << " err: " << err << std::endl;
return false; return false;
} }
} }
return true; return true;
} }
bool testInterpreterDestructor(const std::string & filename, bool testInterpreterDestructor(const std::string& filename,
const std::string & expectedOutput) const std::string& expectedOutput) {
{
std::string err = ""; std::string err = "";
{ {
dynamicgraph::python::Interpreter interp; dynamicgraph::python::Interpreter interp;
...@@ -35,44 +32,57 @@ bool testInterpreterDestructor(const std::string & filename, ...@@ -35,44 +32,57 @@ bool testInterpreterDestructor(const std::string & filename,
{ {
dynamicgraph::python::Interpreter interp; dynamicgraph::python::Interpreter interp;
interp.runPythonFile(filename, err); interp.runPythonFile(filename, err);
if (err != expectedOutput) if (err != expectedOutput) {
{
std::cerr << "The output was not the one expected:" << std::endl; std::cerr << "The output was not the one expected:" << std::endl;
std::cerr << " expected: " << expectedOutput << std::endl; std::cerr << " expected: " << expectedOutput << std::endl;
std::cerr << " err: " << err << std::endl; std::cerr << " err: " << err << std::endl;
return false; return false;
} }
} }
return true; return true;
} }
int main(int argc, char ** argv) int main(int argc, char** argv) {
{
// execute numerous time the same file. // execute numerous time the same file.
// While running 1025, we can notice a change in the error. // While running 1025, we can notice a change in the error.
// unfortunately, it can not be shown using a redirection of the streams // unfortunately, it can not be shown using a redirection of the streams
int numTest = 1025; int numTest = 1025;
if (argc > 1) if (argc > 1) numTest = atoi(argv[1]);
numTest = atoi(argv[1]);
bool res = true; bool res = true;
// This test succeeds only because it is launched before "test_python-ok.py" // This test succeeds only because it is launched before "test_python-ok.py"
// because re as been imported in a previous test and it is not // because re as been imported in a previous test and it is not
// safe to delete imported module... // safe to delete imported module...
res = testFile("test_python-name_error.py", res = testFile(PATH "test_python-name_error.py",
std::string("<type 'exceptions.NameError'>: name 're' is not defined:")+ std::string(
" File \"test_python-name_error.py\", line 6, in <module>\n" + "Traceback (most recent call last):\n"
" pathList = re.split(':', pkgConfigPath)\n", numTest) && res; " File \"" PATH
"test_python-name_error.py\", line 7, in <module>\n"
" pathList = re.split(\":\", pkgConfigPath) # noqa\n"
"NameError: name 're' is not defined\n"),
numTest) &&
res;
res = testFile("test_python-ok.py", "", numTest) && res; res = testFile(PATH "test_python-ok.py", "", numTest) && res;
res = testFile("unexistant_file.py", res = testFile(PATH "unexistant_file.py",
"unexistant_file.py cannot be open", PATH "unexistant_file.py cannot be open", numTest) &&
numTest) && res; res;
res = testFile("test_python-syntax_error.py", res = testFile(PATH "test_python-syntax_error.py",
std::string("<type 'exceptions.SyntaxError'>: ('invalid syntax', ")+ std::string(" File \"" PATH
"('test_python-syntax_error.py', 1, 11, "+ "test_python-syntax_error.py\", line 2\n"
"'hello world\\n'))", numTest) && res; " hello world\n"
res = testInterpreterDestructor ("test_python-restart_interpreter.py", #if PY_MINOR_VERSION >= 10
"") && res; " ^^^^^\n"
return (res?0:1); #elif PY_MINOR_VERSION >= 8
" ^\n"
#else
" ^\n"
#endif
"SyntaxError: invalid syntax\n"),
numTest) &&
res;
res = testInterpreterDestructor(PATH "test_python-restart_interpreter.py",
"") &&
res;
return (res ? 0 : 1);
} }
// The purpose of this unit test is to evaluate the memory consumption // The purpose of this unit test is to evaluate the memory consumption
// when call the interpreter. // when call the interpreter.
#include "dynamic-graph/python/interpreter.hh" #include "dynamic-graph/python/interpreter.hh"
int main(int argc, char ** argv) int main(int argc, char** argv) {
{
int numTest = 1; int numTest = 1;
if (argc > 1) if (argc > 1) numTest = atoi(argv[1]);
numTest = atoi(argv[1]);
dynamicgraph::python::Interpreter interp; dynamicgraph::python::Interpreter interp;
std::string command; std::string command;
...@@ -14,12 +12,16 @@ int main(int argc, char ** argv) ...@@ -14,12 +12,16 @@ int main(int argc, char ** argv)
std::string out; std::string out;
std::string err; std::string err;
for (int i=0; i<numTest; ++i) for (int i = 0; i < numTest; ++i) {
{ // correct input
//correct input interp.python("print('I am the interpreter')", result, out, err);
interp.python("print \"I am the interpreter\"", result, out, err); assert(out.compare("I am the interpreter"));
//incorrect input assert(err.length() == 0);
// incorrect input
interp.python("print I am the interpreter", result, out, err); interp.python("print I am the interpreter", result, out, err);
assert(result.length() == 0);
assert(out.length() == 0);
assert(err.length() > 50);
} }
return 0; return 0;
} }
/*
* Copyright
*/
#include "dynamic-graph-python-test.hh"
int main(int argc, char** argv) { GraphTest testGraph(); }
import unittest
import dynamic_graph as dg
from custom_entity import CustomEntity
ERR = (
"""Python argument types in
dynamic_graph.wrap.plug(%s, %s)
did not match C++ signature:
plug("""
"dynamicgraph::SignalBase<int>* signalOut, "
"dynamicgraph::SignalBase<int>* signalIn)"
)
class BindingsTests(unittest.TestCase):
def test_type_check(self):
"""
test the type checking in signal plugs
"""
first = CustomEntity("first_entity")
second = CustomEntity("second_entity")
# Check that we can connect first.out to second.in
dg.plug(first.signal("out_double"), second.signal("in_double"))
# Check that we can't connect first.out to second
with self.assertRaises(TypeError) as cm_in:
dg.plug(first.signal("out_double"), second)
self.assertEqual(
str(cm_in.exception), ERR % ("SignalTimeDependentDouble", "CustomEntity")
)
# Check that we can't connect first to second.in
with self.assertRaises(TypeError) as cm_out:
dg.plug(first, second.signal("in_double"))
self.assertEqual(
str(cm_out.exception), ERR % ("CustomEntity", "SignalPtrDouble")
)
def test_dg_exc(self):
"""
test that exceptions from dynamic graph are correctly raised
"""
ent = CustomEntity("test_dg_exc")
# check that accessing a non initialized signal raises
with self.assertRaises(RuntimeError) as cm:
ent.act()
self.assertEqual(
str(cm.exception),
"In SignalPtr: SIN ptr not set. "
"(in signal <CustomEntity(test_dg_exc)::input(double)::in_double>)",
)
# check that accessing an initialized signal doesn't raise
ent_2 = CustomEntity("another_entity")
dg.plug(ent_2.signal("out_double"), ent.signal("in_double"))
ent.act()
if __name__ == "__main__":
unittest.main()
# Olivier Stasse # Olivier Stasse
# 2019 CNRS # 2019 CNRS
# #
import sys, os import os
import time import time
# Put local python module at first priority from custom_entity import CustomEntity
sys.path.insert(0,os.getcwd()+'/../src') from dynamic_graph import (
sys.path.insert(0,os.getcwd()) addLoggerCoutOutputStream,
addLoggerFileOutputStream,
closeLoggerFileOutputStream,
real_time_logger_destroy,
real_time_logger_spin_once,
)
from dynamic_graph.entity import VerbosityLevel
print(os.getcwd()) print(os.getcwd())
from dynamic_graph_tests.custom_entity import *
from dynamic_graph.entity import VerbosityLevel
from dynamic_graph import addLoggerFileOutputStream, addLoggerCoutOutputStream, closeLoggerFileOutputStream
from dynamic_graph import real_time_logger_instance, real_time_logger_spin_once, real_time_logger_destroy
# Starts the real time logger instance # Starts the real time logger instance
real_time_logger_instance()
aCustomEntity = CustomEntity("a_custom_entity") aCustomEntity = CustomEntity("a_custom_entity")
...@@ -29,43 +30,45 @@ print(aCustomEntity.getStreamPrintPeriod()) ...@@ -29,43 +30,45 @@ print(aCustomEntity.getStreamPrintPeriod())
aCustomEntity.setLoggerVerbosityLevel(VerbosityLevel.VERBOSITY_INFO_WARNING_ERROR) aCustomEntity.setLoggerVerbosityLevel(VerbosityLevel.VERBOSITY_INFO_WARNING_ERROR)
print(aCustomEntity.getLoggerVerbosityLevel()) print(aCustomEntity.getLoggerVerbosityLevel())
for i in range(0,5): for i in range(0, 5):
aCustomEntity.in_double.value=i aCustomEntity.in_double.value = i
aCustomEntity.out_double.recompute(i) aCustomEntity.out_double.recompute(i)
real_time_logger_spin_once() real_time_logger_spin_once()
print(i) print(i)
time.sleep(1)
aCustomEntity.setLoggerVerbosityLevel(VerbosityLevel.VERBOSITY_WARNING_ERROR) aCustomEntity.setLoggerVerbosityLevel(VerbosityLevel.VERBOSITY_WARNING_ERROR)
print(aCustomEntity.getLoggerVerbosityLevel()) print(aCustomEntity.getLoggerVerbosityLevel())
for i in range(5,10): for i in range(5, 10):
aCustomEntity.in_double.value=i aCustomEntity.in_double.value = i
aCustomEntity.out_double.recompute(i) aCustomEntity.out_double.recompute(i)
real_time_logger_spin_once() real_time_logger_spin_once()
time.sleep(1)
aCustomEntity.setLoggerVerbosityLevel(VerbosityLevel.VERBOSITY_ERROR) aCustomEntity.setLoggerVerbosityLevel(VerbosityLevel.VERBOSITY_ERROR)
print(aCustomEntity.getLoggerVerbosityLevel()) print(aCustomEntity.getLoggerVerbosityLevel())
for i in range(10,15): for i in range(10, 15):
aCustomEntity.in_double.value=i aCustomEntity.in_double.value = i
aCustomEntity.out_double.recompute(i) aCustomEntity.out_double.recompute(i)
real_time_logger_spin_once() real_time_logger_spin_once()
time.sleep(1)
addLoggerCoutOutputStream() addLoggerCoutOutputStream()
time.sleep(1)
aCustomEntity.setLoggerVerbosityLevel(VerbosityLevel.VERBOSITY_NONE) aCustomEntity.setLoggerVerbosityLevel(VerbosityLevel.VERBOSITY_NONE)
print(aCustomEntity.getLoggerVerbosityLevel()) print(aCustomEntity.getLoggerVerbosityLevel())
for i in range(15,20): for i in range(15, 20):
aCustomEntity.in_double.value=i aCustomEntity.in_double.value = i
aCustomEntity.out_double.recompute(i) aCustomEntity.out_double.recompute(i)
real_time_logger_spin_once() real_time_logger_spin_once()
time.sleep(1)
aCustomEntity.setLoggerVerbosityLevel(VerbosityLevel.VERBOSITY_ALL) aCustomEntity.setLoggerVerbosityLevel(VerbosityLevel.VERBOSITY_ALL)
print(aCustomEntity.getLoggerVerbosityLevel()) print(aCustomEntity.getLoggerVerbosityLevel())
for i in range(20,25): for i in range(20, 25):
aCustomEntity.in_double.value=i aCustomEntity.in_double.value = i
aCustomEntity.out_double.recompute(i) aCustomEntity.out_double.recompute(i)
real_time_logger_spin_once() real_time_logger_spin_once()
# End the real time logger # End the real time logger
real_time_logger_destroy() real_time_logger_destroy()
......
# flake8: noqa
import os
pkgConfigPath = os.environ.get("PKG_CONFIG_PATH")
if pkgConfigPath is None:
pkgConfigPath = ""
pathList = re.split(":", pkgConfigPath) # noqa
print(pathList)
import sys, os import os
import re import re
pkgConfigPath = os.environ.get("PKG_CONFIG_PATH") pkgConfigPath = os.environ.get("PKG_CONFIG_PATH")
if pkgConfigPath == None: if pkgConfigPath is None:
pkgConfigPath = '' pkgConfigPath = ""
pathList = re.split(':', pkgConfigPath) pathList = re.split(":", pkgConfigPath)
print pathList
print(pathList)