Tutorial yarp devices

From Asociación de Robótica UC3M
Jump to navigation Jump to search

¿Qué es un YARP device?

Es, en esencia, una clase C++ que se compila en una librería, y se puede cargar dinámicamente a través de una cadena de texto que contiene su nombre. El hecho de cargar una clase cuya implementación interna sólo depende de una cadena de texto que contiene su nombre es extremadamente útil (además de difícil de implementar de cero).

¿Para qué se utilizan los YARP device?

Imagínate que dentro de tu código llamas a funciones de lectura de un encoder. Lo habitual es incorporar una capa de abstracción para tener una clase base para cualquier tipo de encoder. El polimorfismo característico de C++ te permite cambiar de:

EncoderTypeA enc;
enc.getEncoders(&encoderValues);

a:

EncoderTypeB enc;
enc.getEncoders(&encoderValues);

para cambiar de implementación. Esto es, de buenas a primeras, debemos cambiar nuestro código y recompilar si queremos pasar de utilizar la implementación de la clase EncoderTypeA a utilizar la de la clase EncoderTypeB. En esencia, YARP devices nos permite un código mucho más parecido a;

std::cout << "¿Qué tipo de encoders utilizas (EncoderTypeA o EncoderTypeB)?";
std::cin >> encoderType;
...
options.put("device",encoderType);
...
enc->getEncoders(&encoderValues);

Esto es, la selección de implementación de encoder se realiza en tiempo de ejecución, y no es necesario modificar el código ni recompilar para conmutar entre una implementación y otra.

¿Qué más nos permiten los YARP device?

Recordamos que YARP es, de base, una librería para comunicaciones a través de puertos. En el ejemplo del encoder, se ha cargado una librería con una implementación, no se han abierto puertos. Siguiendo con el ejemplo del encoder, YARP device permite alternar de forma sencilla (sin tener que recompilar) entre dos formas de trabajar:

  1. Librería local. Esto es el ejemplo que hemos visto.
  2. Remotamente. Del lado cliente, se carga un YARP device que NO carga el YARP device del dispositivo real, sino que únicamente mapea <las llamadas a las funciones que se le hacen> a <comandos que se envían a un servidor>. La idea es que la sintaxis no cambia con respecto a como si lo estuviésemos utilizando como librería local. Del lado del PC que actúa de servidor, se carga el YARP device del dispositivo real, y simultáneamente se abren unos puertos. Se mapean <los comandos que entran por los puertos del servidor (los que emitió el cliente)> a <llamadas a las funciones del dispositivo real>.

¿Cómo se implementa un YARP device?

En cuanto a implementación, necesita un poco de magia CMake, pero en esencia un YARP device es una clase C++ que hereda de yarp::dev::DeviceDriver y de otras clases. Si sólo heredásemos de yarp::dev::DeviceDriver, sólo tendríamos las funciones open() y close(). Heredamos de otras clases, como por ejemplo yarp::dev::IEncoders (e implementamos sus funciones correspondientes) para agregar funcionalidades propias del dispositivo.

Las versiones actuales de YARP compilan esta clase en una librería dinámica.

¿Cómo se lanza/invoca un YARP device desde consola?

Para ver los YARP devices disponibles, tecleamos:

yarpdev --list

Para lanzar un YARP device (esto es la forma habitual de ejecutar las partes "servidor"), tecleamos:

yarpdev --device nombre_dispositivo

¿Cómo se lanza/invoca un YARP device desde programa?

Desde programa se suelen cargar los YARP device a modo local, o utilizar las partes "cliente". Aquí un ejemplo C++, pero incluso se pueden invocar YARP devices C++ desde Python, Java, etc.

int main(int argc, char *argv[]) {

   yarp::os::Network yarp;
   if ( ! yarp::os::Network::checkNetwork() )
   {
       printf("Please start a yarp name server first\n");
       return(1);
   }
   yarp::os::Property options;
   options.put("device","nombre_dispositivo");
   //-- Se pueden pasar más parámetros al YARP device en el formato:
   //options.put("mas_parametros","valor_de_ese_parametro");
   //options.put("mas_parametros","valor_de_ese_parametro");
   //-- ...
   yarp::dev::PolyDriver dd(options);
   if( ! dd.isValid() )
   {
       printf("Device not available.\n");
       dd.close();
       yarp::os::Network::fini();
       return 1;
   }

   //-- yarp::dev::PolyDriver sólo tiene open() y close(). Necesitamos crear punteros a los demás interfaces...
   yarp::dev::IEncoders *enc;  //-- IEncoders = interfaz de encoders
   if( ! dd.view( encs ) )  //-- ..y "conectarlos" a nuestro dispositivo.
   {
       printf("Could not view encoder interface of device.\n");
       dd.close();
       yarp::os::Network::fini();
       return 1;
   }
   double posEnc;
   if (enc->getEncoder(0, &posEnc)){
       printf("Valor del encoder: %e\n", posEnc);
   } else printf("Mal!\n");

   while(1);  //-- Esta no es la mejor práctica, la idea es no cerrar el programa!

   return 0;
}