Difference between revisions of "Tutorial yarp devices"

From Asociación de Robótica UC3M
Jump to navigation Jump to search
 
(9 intermediate revisions by 2 users not shown)
Line 1: Line 1:
 
== ¿Qué es un YARP device? ==
 
== ¿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).
+
Es un YARP device es una clase C++ que hereda de yarp::dev::DeviceDriver y se compila en una librería. Una de sus principales ventajas es que se puede cargar en tiempo de ejecución 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). Nota: Hablamos de devices, pero más genéricamente este tutorial se refiere a cualquier YARP plugin, que incluye devices (orientado a dispositivos) y carriers (orientado a protocolos de comunicación).
  
 
== ¿Para qué se utilizan los YARP device? ==
 
== ¿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:
 
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);
+
<syntaxhighlight lang="c++">
 +
EncoderTypeA enc;
 +
enc.getEncoders(&encoderValues);
 +
</syntaxhighlight>
 +
 
 
a:
 
a:
EncoderTypeB enc;
+
 
enc.getEncoders(&encoderValues);
+
<syntaxhighlight lang="c++">
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;
+
EncoderTypeB enc;
std::cout << "¿Qué tipo de encoders utilizas (EncoderTypeA o EncoderTypeB)?";
+
enc.getEncoders(&encoderValues);
std::cin >> encoderType;
+
</syntaxhighlight>
...
+
 
options.put("device",encoderType);
+
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:
...
+
 
enc->getEncoders(&encoderValues);
+
<syntaxhighlight lang="c++">
 +
std::cout << "¿Qué tipo de encoders utilizas (EncoderTypeA o EncoderTypeB)?";
 +
std::cin >> encoderType;
 +
...
 +
options.put("device",encoderType);
 +
...
 +
enc->getEncoders(&encoderValues);
 +
</syntaxhighlight>
 +
 
 
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.
 
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.
  
Line 31: Line 43:
 
== ¿Cómo se lanza/invoca un YARP device desde consola? ==
 
== ¿Cómo se lanza/invoca un YARP device desde consola? ==
 
Para ver los YARP devices disponibles, tecleamos:
 
Para ver los YARP devices disponibles, tecleamos:
yarpdev --list
+
 
 +
<syntaxhighlight lang="bash">
 +
yarpdev --list
 +
</syntaxhighlight>
 +
 
 
Para lanzar un YARP device (esto es la forma habitual de ejecutar las partes "servidor"), tecleamos:
 
Para lanzar un YARP device (esto es la forma habitual de ejecutar las partes "servidor"), tecleamos:
yarpdev --device nombre_dispositivo
+
 
 +
<syntaxhighlight lang="bash">
 +
yarpdev --device nombre_dispositivo
 +
</syntaxhighlight>
  
 
== ¿Cómo se lanza/invoca un YARP device desde programa? ==
 
== ¿Cómo se lanza/invoca un YARP device desde programa? ==
Line 39: Line 58:
 
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.
 
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[]) {
+
<syntaxhighlight lang="c++">
+
int main(int argc, char *argv[])
    yarp::os::Network yarp;
+
{
    if ( ! yarp::os::Network::checkNetwork() )
+
  yarp::os::Network yarp;
    {
+
  if ( ! yarp::os::Network::checkNetwork() )
        printf("Please start a yarp name server first\n");
+
  {
        return(1);
+
      printf("Please start a yarp name server first\n");
    }
+
      return 1;
    yarp::os::Property options;
+
  }
    options.put("device","nombre_dispositivo");
+
  yarp::os::Property options;
    //-- Se pueden pasar más parámetros al YARP device en el formato:
+
  options.put("device","nombre_dispositivo");
    //options.put("mas_parametros","valor_de_ese_parametro");
+
  //-- 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");
    //-- ...
+
  //options.put("mas_parametros","valor_de_ese_parametro");
    yarp::dev::PolyDriver dd(options);
+
  //-- ...
    if( ! dd.isValid() )
+
  yarp::dev::PolyDriver dd(options);
    {
+
  if( ! dd.isValid() )
        printf("Device not available.\n");
+
  {
        dd.close();
+
      printf("Device not available.\n");
        yarp::os::Network::fini();
+
      dd.close();
        return 1;
+
      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
+
  //-- yarp::dev::PolyDriver sólo tiene open() y close(). Necesitamos crear punteros a los demás interfaces...
    if( ! dd.view( encs ) )  //-- ..y "conectarlos" a nuestro dispositivo.
+
  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();
+
      printf("Could not view encoder interface of device.\n");
        yarp::os::Network::fini();
+
      dd.close();
        return 1;
+
      yarp::os::Network::fini();
    }
+
      return 1;
    double posEnc;
+
  }
    if (enc->getEncoder(0, &posEnc)){
+
  double posEnc;
        printf("Valor del encoder: %e\n", posEnc);
+
  if ( ! enc->getEncoder(0, &posEnc) )
    } else printf("Mal!\n");
+
  {
+
      printf("No se pudo obtener el valor del encoder 0!\n");
    while(1);  //-- Esta no es la mejor práctica, la idea es no cerrar el programa!
+
      return 1;
+
  }  
    return 0;
+
  printf("Valor del encoder 0: %d\n", posEnc);
}
+
 
 +
  //-- Hacer cosas...
 +
 
 +
  //-- Cerrar device antes de acabar programa.
 +
  dd.close();
 +
 
 +
  return 0;
 +
}
 +
</syntaxhighlight>

Latest revision as of 13:10, 23 January 2017

¿Qué es un YARP device?

Es un YARP device es una clase C++ que hereda de yarp::dev::DeviceDriver y se compila en una librería. Una de sus principales ventajas es que se puede cargar en tiempo de ejecución 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). Nota: Hablamos de devices, pero más genéricamente este tutorial se refiere a cualquier YARP plugin, que incluye devices (orientado a dispositivos) y carriers (orientado a protocolos de comunicación).

¿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("No se pudo obtener el valor del encoder 0!\n");
       return 1;
   } 
   printf("Valor del encoder 0: %d\n", posEnc);

   //-- Hacer cosas...

   //-- Cerrar device antes de acabar programa.
   dd.close();

   return 0;
}