For a more up-to-date method of dealing with devices see:
For a more up-to-date method of dealing with devices see:
In YARP, a device driver is a class that implements one or more interfaces.
If you're interested in learning how to use device drivers, see Getting Started with YARP Devices, Device invocation examples, and yarpdev: the standard YARP device utility. If you're read all that before and are interested in learning how to create devices, then read on...
A device driver should derive from the abstract class yarp::dev::DeviceDriver. This interface contains methods common to all device drivers, such as open and close.
Additionally a device driver should implement a selection of other interfaces that capture what it shares with other families of devices. For example a camera might implement yarp:dev::IFrameGrabber (a "raw" interface to the raw data) and/or yarp::dev::IFrameGrabberImage (a higher-level source of images). For example, the yarp::dev::DragonflyDeviceDriver class implements a few of these interfaces. Interfaces are abstract classes, the idea is that from outside the user can access a device by using a pointer to the interface he/she needs, if that interface is supported by the device. This is quite similar to the way COM works (in a simplified way).
In practice to implement a new device you create a new object which derives from DeviceDriver and from the interface you want to support, and you implement them. The interfaces are documented in the code, using the doxygen standard. See Getting Started with YARP Devices for an example.
Some practical rules are also required to have cmake include your .cpp/.h files in the YARP_dev (device library) project.
Let's assume you want to create the Foo device. Start by creating a folder called:
YARP_dev/src/foo/
Files in foo will be organized in:
Each of these directories should be organized as:
CMake searches for YARP device drivers listed in:
YARP_dev/AvailableDevices.txt
Here you specify the directory that contains the device driver tree (in the above example we called it foo). For example we could add "foo" in as follows:
SET(AVAILABLE_DEVICES picolo dragonfly esdMotionControl foo)
This is the list of directories under YARP_dev that are searched for device drivers, when CMake runs. CMake does this by searching for a file called libraries.txt.
The file libraries.txt tells cmake which additional libraries or packages the device depends on. This files is platform specific and is located in /foo/$os$/. If this file is not found cmake simply warns the user and ignores the device.
The file libraries.txt has two options. If the device driver requires a library that is included in the YARP tree you write:
YARP_LOCAL;ntcan.lib
This tells cmake that the device driver requires the library "ntcan.lib" and that this file is available within the YARP tree (in ./dd_orig/). CMake will search ./dd_orig/lib for ntcan.lib. If ntcan.lib is not found an error will be shown, and the user will have the possibility to manually enter the correct location of the file. This .lib file has probably header files associated with it. We recommend you put them in ./dd_orig/include and include them directly in you code as something like:
Be careful to "hide" header files specific of each device driver (in this example foo_lib.h) so that symbols defined there are not visible from outside. In practice you do this by avoiding to include them in your header files.
If the device driver has external dependencies you write:
YARP_EXTERNAL;raw1394;dc1394_control
In this example we are telling CMake that the device driver foo depends on the packages raw1394 and dc1394_control. In this case cmake will not do any check, but will print a warning message saying that these modules are required. It is the user's responsibility (for now, and perhaps forever) to install these packages in the correct locations.
Everything going well, you will see on CMake a list of variables whose name follows the pattern ENABLE_device_name. These are the devices that are listed in AvailableDevices.txt. By default each flag is OFF meaning that the corresponding device will be ignored by CMake. You can turn ON the flags for the devices you want to compile, and type configure again. CMake will search for the file libraries.txt. If among the selected drivers there are some that require additional libraries there will be a list of variables whose name follows the pattern PATH-device_name. If these libraries are correctly located in the ./dd_orig/lib folder, all PATH-device_name entry should be correctly set. Although you can manually locate the libraries yourself, this probably means that there is an error in your YARP distribution or in the way the device is configured.
If all libraries are located, you can go on with CMake and generate your make/project files. If at least one device driver was selected and correctly found, YARP_dev should now compile.
It is useful to be able to create devices in a reconfigurable way, for example using the yarpdev application or the yarp::dev::PolyDriver class. This is done be adding a "factory" for your device driver to YARP_dev.
Take a look at this file:
src/libYARP_dev/src/PopulateDrivers.cpp.in
This is a template that CMake configures based on the devices the user selects, adding factories for them to a global list. Suppose your device is called "foo". Find the part of this file that contains a list something like this:
and add:
This will be replaced with:
by CMake if the user chooses to compile your device, otherwise it will be commented out. You can see the code CMake generates at:
src/libYARP_dev/src_generated/PopulateDrivers.cpp
This is a generated file, do not modify it! Make all your changes to PopulateDrivers.cpp.in.
Now find the part of PopulateDrivers.cpp.in that has lines like:
And add:
or whatever header file is needed for your device. The path to the header file should usually work just as yarp/Foo.h rather than anything more complicated.
Finally, now that we have our define, and have included the right header file, we can add the factory for your device. Go to the part of PopulateDrivers.cpp.in that has a list of lines like this:
Now add in an entry for your device:
Replace FooDeviceDriver with the correct name of your class (which must be derived from yarp::dev::DeviceDriver). There are also three the Replace the text "foo" with whatever you want humans to call your device when they create it – that's completely up to you. If there is a network wrapper device that can transport the data produced by your device, mention it as the second argument. That's the "" above. See the Server classes in Network Servers (Wrappers - pre NWC/NWS architecture) for existing network wrappers. A fairly generic wrapper is under development; in the meantime yarp::dev::ServerInertial is an example to start from if you find you have to write your own. Note that network wrappers are completely optional. They are not needed for use of a device via yarp::dev::PolyDriver.
The last text argument just repeats the class name for documentation purposes.
Now you should be able to compile your device the same way as any other YARP device.
Then run:
yarpdev --list
Your device should show up in the list. To learn more about using devices from code, see Getting Started with YARP Devices, and the examples in example/dev.
True, we should do more automation than we do now. For now, we're just happy to have a mechanism that works solidly on both UNIX and Windows.