RobWork is an extensible framework that allows addition of functionality through the use of plugins. Plugins can be dynamically loaded at runtime, and makes it possible to reduce the coupling and complexity. Furthermore, the framework provides the concept of lazily loaded plugins. Functionality in a lazy-loaded plugin will not be loaded before it is needed. To make the framework extensible, RobWork provides a bunch of ExtensionPoints . Each extension point has a unique identifier, and by referring to the identifier, it is possible for Extension(s) to extend the extension point. A Plugin can provide one or more extensions, and these do not need to be extensions for the same extension point. To keep track of all plugins and extensions, the ExtensionRegistry is used. On this page, some of the existing extension points and extensions will be discussed to give a better understanding of how they can be used to produce better and more modular code. After that, there is more in-depth examples of how a user can write a plugin that provides one or more extensions, and how this plugin is actually loaded and used in a program.
We will here try to give an overview of extension points currently available in the RobWork framework. Notice that the Extension Points group should always contain a up-to-date comprehensive list of all the extension points.
RobWork has various loaders, for XML files, images, geometry data and workcells. These are defined as extension points, such that the framework can be easily extended to support new formats and loaders.
|Extension Point||Description||Known Extensions|
|rw::common::DOMParser::Factory||Different parsers understanding the Document Object Model.|
|rw::loaders::ImageLoader::Factory||Loaders that are able to load an rw::sensor::Image|
|rw::loaders::Model3DLoader::Factory||Loaders that will load a rw::graphics::Model3D|
|rw::loaders::WorkCellLoader::Factory||Loaders that will load a rw::models::WorkCell|
It is possible to use different strategies for detecting collisions and calculating distance between geometries. Typically the Proximity Query Package (PQP) is used. RobWork allows other algorithms to be supported by the framework, as there are extension points defined for each of the different proximity strategy types. Notice that the ProximityStrategy is a superclass of CollisionStrategy, CollisionToleranceStrategy, DistanceStrategy and DistanceMultiStrategy. An extension to any of the four subtypes, should therefore also be available in the ProximityStrategy extension point.
|Extension Point||Description||Known Extensions|
|rw::proximity::CollisionStrategy::Factory||Proximity strategies that supports the rw::proximity::CollisionStrategy interface.|
|rw::proximity::CollisionToleranceStrategy::Factory||Proximity strategies that supports the rw::proximity::CollisionToleranceStrategy interface.|
|rw::proximity::DistanceMultiStrategy::Factory||Proximity strategies that supports the rw::proximity::DistanceMultiStrategy interface.|
|rw::proximity::DistanceStrategy::Factory||Proximity strategies that supports the rw::proximity::DistanceStrategy interface.|
RobWork has a framework for defining assembly operations and the result of performing assembly operations. It is the intention that a library of different assembly strategies will be built with time. The framework can easily be extended with new user-specified control strategies using this extension point.
|rwlibs::assembly::AssemblyRegistry||Strategies for assembly rwlibs::assembly::AssemblyControlStrategy|
The script interface can be extended with custom LUA commands, using the extension point for LUA.
|Extension Point||Known Extensions|
|rwlibs::swig::LuaState::Factory||RobWork, RobWorkStudio and RobWorkSim all extends this extension point with their own LUA interfaces.|
RobWorkSim defines an extension point for physics engines. This way it is possible to add new engines for dynamic simulation in a modular way. Furthermore, a hierarchic logging structure is provided for debugging simulation engines. As different engines can have different data that is relevant to log, the logging structure is extensible. On top of the logging structure, there is also a graphical layer, that can be extended with specific Qt widgets for graphical visualisation of the log. The engine test framework also allows extension with new custom tests.
|Extension Point||Description||Known Extensions|
|rwsim::simulator::PhysicsEngine::Factory||Physics engines rwsim::simulator::PhysicsEngine.|
|rwsim::log::SimulatorLogEntry::Factory||Logging types for simulators rwsim::log::SimulatorLogEntry, making it possible for specific physics engines to add more specific log types.|
|rwsimlibs::gui::SimulatorLogEntryWidget::Factory||rwsimlibs::gui::SimulatorLogEntryWidget::Dispatcher for a Qt Widget that is able to show a rwsim::log::SimulatorLogEntry graphically. Physics engines can make engine specific log types and graphical representation.|
|rwsimlibs::test::EngineTest::Factory||Tests of physics engines rwsimlibs::test::EngineTest. Makes it possible to extend the test suite with new tests.|
Most plugins will have a very similar structure. In this example a plugin named MyPlugin is created, by implementing the rw::common::Plugin interface. The MyPlugin.hpp file will look as follows:
By default, RobWork has the ability to load images in PGM, PPM and RGB formats. If, for instance, a user needs support for JPG or JPEG formats, it is possible to implement the rw::loaders::ImageLoader interface with a specific loader for these formats. Assume that such an implementation, MyImageLoader, is already developed. Now, the user could just use this loader directly in its program. Instead, the loader should be provided in a plugin, as it will then be possible to use the loader in all programs based on the RobWork framework, such as RobWorkStudio. The implementation of the Plugin interface would then look similar to the following example:
The RW_ADD_PLUGIN() macro will define a standard entry point in the compiled binary, and is required to make it possible to load the plugin again after creation. The constructor must call the parent Plugin constructor, with a unique id, a human-friendly name, and a version string. The Plugin will be responsible for providing a list of extension descriptors and a method for actually creating a specific extension from an extension id. The extension descriptor provides meta-information about the extensions in the plugin. The importance of the distinction between Extensions and ExtensionDescriptors, will become apparent for lazy-loading of plugins, where the meta-information can also be stored in a xml file. Each extension and extension descriptor has a unique id, and refers to the id of the extension point that it fits into. In this case the rw::loaders::ImageLoader::Factory extension point has the id rw.loaders.ImageLoader. The id should be found in the documentation for the relevant extension point or on the Extension Points list. A concrete Extension takes two additional arguments compared to the extension descriptor, namely a pointer to the plugin itself, and a pointer to the object provided by the extension. In this case, the object provided by the extension is an instance of MyImageLoader. It is important to notice that a plugin will never be able to provide more than one instance per extension. Sometimes objects can contain state or information that makes it unreasonable to only have one instance of an object. In such cases, the extension point will typically expect the object to be a so-called "dispatcher" that is then used to construct multiple objects. Finally, notice that each extension has an attached PropertyMap with a list of properties. It is up to the individual extension point to define the properties necessary for the extension point. For a loader, this will typically be the names of the file extensions supported. For a proximity strategy, it will be the type of strategy, such as PQP, FCL or Bullet.
Compilation of the plugin will typically just require a couple of lines to be added to the CMakeLists.txt file:
Notice that the MyPlugin.cpp should never be included in other targets than the MyPlugin.rwplugin target. If a target has source files containing more than one RW_ADD_PLUGIN definition, compilation will fail.
On Linux the plugin will have the name libMyPlugin.rwplugin.so. When this plugin is loaded dynamically, rw::loaders::ImageLoader::Factory will be able to provide a loader for the JPG and JPEG image formats. In fact, RobWorkStudio provides the rws::RWSImageLoaderPlugin that does exactly this for a wide range of image formats. The RWSImageLoaderPlugin basically uses Qt to load the images, and support the formats that Qt supports. This is an example of how the functionality of RobWork can be extended, without RobWork having to know about anything called Qt. Hence, dependencies are reduced, and code should be easier to maintain and modularize.
There are different ways to actually load the plugins after creation. RobWork will look in various default directories where it will try to load all libraries with the correct rwplugin extension. If plugins lie in other directories, the user must point RobWork to these plugins. Currently, RobWork will automatically do the loading. In the future this behaviour is likely to change, so if program is written that depends on plugins being loaded, an explicit statement should be used to initialize the RobWork framework.
Every program that needs to use plugins, should initialize RobWork in the main function using rw::RobWork::init() or rw::RobWork::init(int argc, const char *const * argv) :
RobWork will automatically search for plugins using the first of the following rules that succeeds:
Additionally, a plugin can be loaded directly in code with:
When RobWork encounters finds files with the ending .rwplugin.so or .rwplugin.dll, it will try to load these plugins immediately. There can, potentially, be many plugins to load, which can take up unnecessary resources as they might not be needed by a specific application. For this reason, there is also the concept of lazy-loaded plugins. When RobWork encounters a file with the extension .rwplugin.xml, it will recognize this as a specification of a plugin that should only be loaded at the time where it is needed. To make a plugin lazy-loaded, create a file with a filename ending in .rwplugin.xml and give it a similar content as the following:
In this example, it is clear to see that the same meta-information is provided as in the extension descriptor discussed in Writing a Plugin - ImageLoader Example .