Developing a plug-in driver for Player

From Robots in everyday human environments

Jump to: navigation, search

Writing drivers for Player can essentially be done in two ways. One option is to write a driver statically linked into the Player server, and thus included when compiling the Player distribution. The second option, is to write a so-called plug-in driver, which is loaded at runtime. Where the first option is generally used by leading developers, the second option is an obvious choice when developing new, experimental or third party drivers. This is due to the plug-in drivers being easier to compile since they do not require the whole distribution to be recompiled, and thus allow for faster development. No matter the choice of driver type, a configuration file is needed to inform Player on which interfaces the given driver supports. A short introductory example of such a configuration file is shown below.

1  driver
2  (
3  name "player_driver"
4  provides ["<host>:<robot>:<interface>:<index>"]
5  )

It is seen that a new driver section is introduced by the first instruction driver(), and subsequently a name of the driver provided. Line 4 contains the instructions on which interface the driver provides and at which address they are available. The host and robot fields indicate where the device can be located, while the interface field obviously indicates which interface the driver provides. Lastly, the index field, allows for supporting multiple instances of the same interface e.g. two laser range finders on the same robot.


Besides from the obligatory name option, the following options, not included in the example, are also available for use in the driver configuration file:

  • plugin A string option used when a driver has been developed as a plug-in to Player. Use of the plugin option is done by providing the name of the shared library containing the developed driver.
  • requires A tuple of strings option, enabling the developer to inform Player that the given driver requires certain device addresses to be available. Often used when writing so-called “abstract” drivers, that often use several devices to e.g. apply advanced control algorithms to a robot.
  • alwayson Integer option, that when set to 1 forces Player to set up the given driver when the Player server starts. Normally the Player server waits for any client connection. The alwayson option is e.g. advantageous when handling a driver which has a long start-up time to avoid time-outs.


The following presents the structure of a C++ plug-in driver and a description of the various parts it contains. The structure described in the text is taken from a basic example driver bundled with Player.



Contents




Class definition

Starting from the class definition seen below, lines 1-16 contains obligatory declarations needed for Player. These will be described in the following, where the implementation of the functions is treated.


1 #include <unistd.h>
2 #include <string.h>
3 #include <libplayercore/playercore.h>
4
5 class PlayerDriver : public Driver
6 {
7 public:
8 PlayerDriver(ConfigFile* cf, int section);
9
10 virtual int Setup();
11 virtual int Shutdown();
12
13 virtual int ProcessMessage(MessageQueue* resp_queue,player_msghdr *hdr,void * data);
14
15 private:
16 virtual void Main()
17 int intTest;
18 }

The remaining part of the class definition, is available for the user to declare variables or functions. In this case the integer int_Test is declared. In the following the various parts constituting the Player driver is treated:

PlayerDriver_Init()

A factory creation function where a pointer to the new instance of the driver is created and returned.

1 Driver* PlayerDriver_Init(ConfigFile* cf, int section)
2 {
3 return((Driver*)(new PlayerDriver(cf, section)));
4 }

PlayerDriver_Register()

Registers the driver in the given driver table, indicating the device name as well as instructions on how to create a new instance of the driver i.e. the function RobotinoDriver_Init().

1 void PlayerDriver_Register(DriverTable* table)
2 {
3 table->AddDriver("player_driver", PlayerDriver_Init);
4 }

PlayerDriver()

The class constructor invoked at any new instance of the given class. The arguments ConfigFile* cf and int section, passed by the Player server, are references to the name and section of the associated configuration file such that options can be read from here. The instruction between the : and { is a “constructor initializer”, which is used for initializing given members with the values in parenthesis. The amount of arguments initialized with the public member function Driver(),determines whether the constructor is for single- or multiple-interface drivers. The parameters for the function are:


  • cf - Current configuration file
  • section - Current section in configuration file
  • overwrite_cmds - Do new commands overwrite old ones?
  • queue_maxlen - How long can the incoming queue grow?
  • interf - Player interface code. The parameter that, if included, makes the constructor for single-interface drivers otherwise for multiple-interface drivers.


In the example below, the constructor is thus targeted at a single-interface driver providing the interface position2d. Thus, in the example below, a constructor for a single-device driver is used and afterwards the value example_interface, e.g. representing a serial port, is read from the configuration file into the driver.

1 PlayerDriver::PlayerDriver(ConfigFile* cf, int section) : Driver(cf,section, false, 
2 PLAYER_MSGQUEUE_DEFAULT_MAXLEN, PLAYER_POSITION2D_CODE)
3 {
4 this->in_section = cf->ReadInt(section, "player_interface", 0);
5 return;
6 }

Setup()

A function performing device-specific initialization e.g. opening and configuring a serial port for communication as well as starting the driver thread. The Setup() function is called every time a client subscribes to a driver. Setup() returns 0 for success and −1 when setup has failed causing Player to terminate.

1 int PlayerDriver::Setup()
2 {
3 StartThread();
4 return(0);
5 }

Shutdown()

Opposite the Setup() function, Shutdown() ensures device-specific finalization as e.g. shutting down the communication port and stoppage of the driver thread. Shutdown() returns 0 for success, and −1 values when failed.

1 int PlayerDriver::Shutdown()
2 {
3 StopThread();
4 return(0);
5 }

ProcessMessage()

This function is invoked on each incoming message to the server (see next item Main()). Furthermore, Player offers the opportunity to send a response i.e. publish a message using the Player public member function Publish(). If a message is handled successfully, 0 is returned, whilst −1 is returned when message handling has failed.

1 int PlayerDriver::ProcessMessage(MessageQueue* resp_queue, player_msghdr* hdr, void * data)
2 {
3 return(0);
4 }

Main()

The main function of the device thread, where all interaction with the device is to be included. Line 5 checks whether execution should be cancelled, and line 6 presents the call that ensures that ProcessMessage() is invoked at a certain iterval. Between the instruction at line 6 and 7, device-specific code is inserted to e.g. control motors or read IR-sensors assuming these kind of interfaces are specified. Resulting data is replied back to the associated interfaces using Publish().

1 void PlayerDriver::Main()
2 {
3 for(;;)
4 {
5 pthread_testcancel();
6 ProcessMessages();
7 usleep(100000);
8 }
9 }

extern “C”()

This instruction is used to inform the compiler that one or several functions have been compiled in C. The instruction is a so-called “linkage specification” and ensures that the function name will not be included in the standard name encoding performed by the C++ compiler for type-safe linkage. Omitting this instruction will result in an unrecognized function.

1 extern "C" {
2 int player_driver_init(DriverTable* table)
3 {
4 PlayerDriver_Register(table);
5 return(0);
6 }
7 }

Inserting code into the relevant sections of the above described framework for developing Player drivers, is basically what is needed in order to develop Player compatible drivers. This has been done in order to develop drivers for all available hardware on the Robotino® including the PBS-03JN range finder. However, the driver for the range finder has been developed individually, with a possible Player contribution in mind. Thus, the developed Robotino® driver will be compatible with all Robotino® robots, and the PBS-03JN driver possible to use for any robot equipped with the sensor. The developed drivers can be downloaded from the Downloads section.

Personal tools