Estructura del driver
Declaración de la clase RomaaDriver
1 class RomaaDriver : public Driver 2 { 3 public: 4 RomaaDriver( ConfigFile* cf, int section ); 5 ~RomaaDriver(); 6 7 // Must implement the following methods 8 virtual int Setup(); 9 virtual int Shutdown(); 10 11 // This method will be invoked on each incoming message 12 virtual int ProcessMessage( QueuePointer& resp_queue, 13 player_msghdr* hdr, 14 void* data ); 15 16 protected: 17 private: 18 19 // Main function for device thread 20 virtual void Main(); 21 };
Métodos virtuales
Setup: Realiza funciones de inicialización especificas del dispositivo por ej. abrir y configurar un puerto de comunicación serie; también ejecuta el thread del driver. El método Setup() es llamado cada vez que un cliente se subscribe al driver.
Shutdown: Es el opuesto al método Setup(). Shutdown() asegura la terminación de dispositivos como por ej. cerrar un puerto de comunicación serie; también detiene el thread del driver.
ProcessMessage: Este método se invoca con cada mensaje entrante al servidor y ofrece la posibilidad de enviar una respuesta publicando un mensaje utilizando la función miembro pública Publish().
Main: Es la función principal del thread del dispositivo, donde se incluyen todas las interacciones con el dispositivo.
Registro del driver en el servidor e inicialización
El server player inicia el plug-in driver llamando a player_driver_init (función en C), esta función llama a RomaaDriver_Register.
extern "C" { int player_driver_init( DriverTable* table ) { RomaaDriver_Register( table ); return(0); } }
RomaaDriver_Register agrega el driver a una tabla de drivers del player server llamando a RomaaDriver_Init.
void RomaaDriver_Register( DriverTable* table ) { table->AddDriver( "romaadriver", RomaaDriver_Init ); }
RomaaDriver_Init crea un nuevo objeto RomaaDriver y devuelve un puntero al driver.
Driver* RomaaDriver_Init( ConfigFile* cf, int section ) { // Create and return a new instance of this driver return ( (Driver*)(new RomaaDriver( cf, section )) ); }
Una vez creado el objeto RomaaDriver se ejecuta el constructor, este carga datos del archivo de configuración (*.cfg) del player server. En el siguiente código utiliza un inicializador de constructor para inicializar los miembros datos con los parámetros pasados. Este constructor se utiliza para una única interfaz, en este caso position2d (ver listado de interfaces definidas en Player).
RomaaDriver::RomaaDriver(ConfigFile* cf, int section) : Driver(cf, section, false, PLAYER_MSGQUEUE_DEFAULT_MAXLEN, PLAYER_POSITION2D_CODE) { // Read an option from the configuration file return; }
El prototipo de la clase Driver es
Driver (ConfigFile *cf, int section, bool overwrite_cmds, size_t queue_maxlen, int interf)
Las funciones RomaaDriver_Init y RomaaDriver_Register son funciones declaradas fuera de la clase así pueden ser invocada sin ningún contexto de objeto.
Secuencia del server
Al ejecutar el servidor player mediante =player romaa.cfg=, se imprime lo siguiente en pantalla, donde en color negro se muestran los mensaje del server player, en %RED%rojo%ENDCOLOR% ejecución de métodos y %BLUE%azul%ENDCOLOR% mensajes del driver.
- trying to load /home/gfpp/programas/romaa_player_driver/./libromaadriver...
- success
- invoking player_driver_init()...
%RED%- RomaaDriver _Register%ENDCOLOR%
- success
%RED%- RomaaDriver _Init%ENDCOLOR%
%RED%- RomaaDriver::RomaaDriver%ENDCOLOR%
- listening on 6665
- Added file watch 4
- Added file watch 5
- Listening on ports: 6665
- Added file watch 6
- accepted TCP client 0 on port 6665, fd 6
%RED%- RomaaDriver::Setup%ENDCOLOR%
- romaadriver initialising...
- %BLUE%Connecting at ...%ENDCOLOR%
- %BLUE%serial dev: /dev/ttyS1%ENDCOLOR%
- %BLUE%baudrate: 38400%ENDCOLOR%
- %BLUE%romaadriver ready%ENDCOLOR%
%RED%- RomaaDriver::Main%ENDCOLOR%
%RED%- RomaaDriver::ProcessMessage%ENDCOLOR%
- closing TCP connection to client 0 on port 6665
%RED%- RomaaDriver::Shutdown%ENDCOLOR%
- Shuting romaadriver down
- %BLUE%romaadriver has been shutdown%ENDCOLOR%
- Quitting.
%RED%- RomaaDriver::~RomaaDriver%ENDCOLOR%
Se destacan algunos puntos importantes
1 se carga el driver libromaadriver
12 se ejecuta un programa cliente ( =playerv=)
22 se desconecta el cliente
26 se termina la ejecución del servidor
Constructor
En el contructor se lee el archivo de configuración (*.cfg) y se definen las interfaces utilizadas. (ver Configuration file class).
En el caso de una única interfaz position2d para el control de motores y odometria seria
RomaaDriver::RomaaDriver(ConfigFile* cf, int section) : Driver(cf, section, false, PLAYER_MSGQUEUE_DEFAULT_MAXLEN, PLAYER_POSITION2D_CODE) // Read an option from the configuration file this->tty_dev = cf->ReadString(section, "port", "/dev/ttyS0"); this->baudrate = cf->ReadInt(section, "baudrate", 38400); return; }
Si el driver provee de múltiples interfaces se utiliza el siguiente constructor
RomaaDriver::RomaaDriver(ConfigFile* cf, int section) : Driver(cf, section)
Y se deben agregar las interfaces explicitamente
player_devaddr_t position_addr; if ( cf->ReadDeviceAddr( &position_addr, section, "provides", PLAYER_POSITION2D_CODE, -1, NULL) == 0 ) { if ( this->AddInterface( position_addr ) != 0 ) { this->SetError(-1); return; } }
Main
La función Main en el núcleo del plugin driver que se ejecuta en un thread, esto significa que este corre en paralelo con otros drivers. La mayor parte de la función Main esta contenida en un loop while(true) o for(;;). La función Main tiene que llamar unas pocas funciones específicas:
pthread_testcancel: Verifica si el thread fue terminado (killed). La función saldrá del loop y ejecutara la el método Shutdown.
ProcessMessage: Es una llamada a Player que llamará la función ProcessMessage() del plugin con cada mensaje esperando en la cola de mensajes.
usleep: Es un comando de sleep para liberar los recursos utilizados por el driver.
La función Main es donde se debería leer información de los dispositivos, formatear los datos especificos para un interfaz y publicarlos a Player.