openrave.org

 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
Writing Plugins and Interfaces

Contents

Every plugin needs to export several functions as defined in Plugin Export Functions to notify OpenRAVE what interfaces it has. When a plugin is first loaded, it is validated by the environment and its OpenRAVEGetPluginAttributes function will be called in order so the OpenRAVE core can register the names of its provided interfaces. Plugins themselves can query functionality offer by other plugins through the environment's interface querying functions.

Making a Simple Interface

Example plugincpp.cpp creates a OpenRAVE::ModuleBase interface named MyModule and have it offer two commands: numbodies and load.

The first #include the compiler sees has to be openrave/openrave.h. Then for the main C++ file, we include openrave/plugin.h for several helper functions.

#include <boost/bind.hpp>
using namespace std;
using namespace OpenRAVE;
namespace cppexamples {
class MyModule : public ModuleBase
{

Now register the two commands of the module. boost::bind is necessary for specifying member functions as callbacks.

MyModule(EnvironmentBasePtr penv) : ModuleBase(penv)
{
__description = "A very simple plugin.";
RegisterCommand("numbodies",boost::bind(&MyModule::NumBodies,this,_1,_2),"returns bodies");
RegisterCommand("load",boost::bind(&MyModule::Load, this,_1,_2),"loads a given file");
}

Provide the implementations for the member functions:

bool NumBodies(ostream& sout, istream& sinput)
{
vector<KinBodyPtr> vbodies;
GetEnv()->GetBodies(vbodies);
sout << vbodies.size(); // publish the results
return true;
}
bool Load(ostream& sout, istream& sinput)
{
string filename;
sinput >> filename;
bool bSuccess = GetEnv()->Load(filename.c_str()); // load the file
return bSuccess;
}
};

It is recommend to plugin authors to include openrave/plugin.h in their main C++ file, this will provide implementations for the export functions and ask the user to provide a new set of functions CreateInterfaceValidated and GetPluginAttributesValidated.

Providing MyModule would look like:

InterfaceBasePtr CreateInterfaceValidated(InterfaceType type, const std::string& interfacename, std::istream& sinput, EnvironmentBasePtr penv)
{
if( type == PT_Module && interfacename == "mymodule" ) {
}

In order to tell OpenRAVE what is provided, have to define:

void GetPluginAttributesValidated(PLUGININFO& info)
{
info.interfacenames[PT_Module].push_back("MyModule");
}

Building the Plugin

Using CMake (Linux and Windows)

The main build system of OpenRAVE is cmake, and FindOpenRAVE.cmake can be used to find the OpenRAVE installation. An example of the CMakeLists.txt file for compiling a plugin using FindOpenRAVE.cmake is:

cmake_minimum_required (VERSION 2.6)
project (plugincpp)
find_package(OpenRAVE REQUIRED)
include_directories(${OpenRAVE_INCLUDE_DIRS})
link_directories(${OpenRAVE_LIBRARY_DIRS})
add_library(plugincpp SHARED plugincpp.cpp)
set_target_properties(plugincpp PROPERTIES COMPILE_FLAGS "${OpenRAVE_CXX_FLAGS}" LINK_FLAGS "${OpenRAVE_LINK_FLAGS}")
target_link_libraries(plugincpp ${OpenRAVE_LIBRARIES})

Other Build Systems

If not using CMake, then here's how the development files are organized:

Linux Users

Depending on where openrave was installed, a openrave-config should have been created in the $OPENRAVE_INSTALL/bin directory. It is possible to call openrave-config –cflags to get the correct paths and flags to include in gcc to link with libopenrave.so.

Using the Plugin

There are several ways to load the generated plugin.

Once the plugin is loaded, we can create the interface and call its commands to load an environment and return the number of bodies:

C++

ModuleBasePtr prob = RaveCreateModule(env,"MyModule");
env->AddModule(prob,"");
stringstream sinput, sout;
// input the load command
sinput << "load data/lab1.env.xml";
if( !prob->SendCommand(sout,sinput) ) {
RAVELOG_WARN("command failed!\n");
}
else {
sinput.str(""); // have to reset the stream from the previous command
sinput << "numbodies"; // input the numbodies command
prob->SendCommand(sout,sinput);
int numbodies;
sout >> numbodies;
RAVELOG_INFO("number of bodies are: %d\n",numbodies);
}

Python

prob = RaveCreateModule(env,'MyModule')
env.AddModule(prob,args='')
cmdout = prob.SendCommand('load data/lab1.env.xml')
if cmdout is None:
    raveLogWarn('command failed!')
else:
    cmdout = prob.SendCommand('numbodies')
    print 'number of bodies are: ',cmdout

Octave (only simple commands possible)

prob = orEnvCreateProblem('MyModule');
orProblemSendCommand('load data/lab1.env.xml',prob);
numbodies = orProblemSendCommand('numbodies',prob);
disp(['number of bodies are: ' num2str(numbodies)])

Documenting Interfaces

The format of all interface documentation is the widely adopted standard reStructuredText. The description of an interface and all information about its usage should be provided by two places:

These descriptions are automatically parsed using Sphinx and put on the web.

The reason why doxygen and other commenting tools are not adopted for plugin documentation is because the Base Interface Classes are the only binding between plugins. Even if the header file or provided functions of a particular plugin were provided, other plugins would not be able to use them if not offered through the OpenRAVE's channels.

Loading Plugins

Many mechanisms have been put in place to prevent mismatching/old plugins to be loaded by the core. Using interfaces from stale plugins can lead to unexpected crashes that are very difficult to debug. It is possible to automatically come up with a unique hash of the interface functions and members by running each interface through a C++ lexer and then creating a 128bit unique md5 hash. In order to protect plugins compiled with a different version, OpenRAVE creates a md5 hash from each interface class definition using cpp-gen-md5 and stores them in openrave/interfacehashes.h.

The interface hash can be retrieved using OpenRAVE::RaveGetInterfaceHash. For an interface to be loaded successfully, the plugin has to check that the hash the core is using matches the hash compiled with the plugin. These types of checks ensure that stale plugins will never be loaded; helper functions are offered in openrave/plugin.h, which all plugin authors should use.