1 Objetivos

2 Algunas aclaraciones

  • En esta práctica trabajaremos con programación dirigida por eventos mediante el proyecto boost. Necesitamos tener instalada la biblioteca boost::signals2.

  • Esta biblioteca se encuentra ya instalada en el SO de la EPS. Si usas un SO diferente pero basado en Ubuntu prueba lo que se indica en este enlace (sección Install Howto) para instalarla. No obstante la recomendacion desde la asignatura es que uses la versión del SO de la EPS para evitar problemas con la instalacion de esta biblioteca.

  • En el código de partida entregado junto a este enunciado se incluye un archivo mainp3.cc en el que verás una serie de funciones definidas y un ejemplo de uso del simulador del robot. En la función main se conectan dichas funciones con las señales que se tendrán que implementar en el archivo p3.cc.

  • Ten en cuenta que las funciones conectadas a las señales pueden ser diferentes de las que se utilizarán para evaluar la práctica, pero el prototipo de la función será el mismo (tipo de dato que devuelve y parámetros de entrada).

  • En esta práctica no debes mostrar ningún mensaje por pantalla en tu implementación. Cualquier mensaje que se muestre en la consola será responsabilidad de la función main o cualquiera de las funciones conectadas a las señales.

  • Recuerda que seguimos empleando C++ como un mejor C, es decir, podremos emplear:

  • Recomendación: Puedes comprobar fácilmente el contenido del mapa en cualquier momento utilizando la función displayMap que tienes implementada en p3.h.

3 Enunciado

  • En esta tercera práctica vamos a implementar un simulador de un robot que se encuentra en un mapa con algunos obstáculos. Se dispondrá de un mapa rectangular limitado en tamaño en el que se podrán añadir obstáculos.

  • Como punto de partida tienes los siguientes ficheros:

    • Función principal: mainp3.cc: contiene un ejemplo de uso. Puedes modificarlo para diseñar tus propias pruebas.
    • Cabecera: p3.h: contiene los tipos de datos básicos necesarios para la práctica así como el prototipo de las funciones.
  • Deberás crear el archivo p3.cc e implementar las funciones y el uso de señales.

3.0.1 mainp3.cc

  1. El fichero mainp3.cc contiene varias funciones que se ejecutarán cuando se activen determinadas señales. Con la función main proporcionada, estas funciones estarán conectadas a las señales de forma que:
    void show_out_map_attempt([[maybe_unused]] int32_t row, int32_t col) :
    Se ejecutará cuando se active la señal que devuelve la función get_out_map_signal() del fichero p3.h. Los parámetros de entrada representan la coordenada a la que se ha intentado acceder pero que se encuentra fuera del mapa.
    void show_obstacle_sensor_attempt([[maybe_unused]] uint32_t row, uint32_t col) :
    Se ejecutará cuando se active la señal que devuelve la función get_obstacle_signal() del fichero p3.h. Los parámetros de entrada representan la coordenada en la que se ha detectado el obstáculo.
    void show_low_battery_info([[maybe_unused]] uint16_t battery_level) :
    Se ejecutará cuando se active la señal que devuelve la función get_battery_signal() del fichero p3.h. El parámetro de entrada representa el nivel de batería del robot. Ten en cuenta que si se activa esta señal, el robot no habrá sido capaz de realizar el movimiento.
  2. Fíjate en la anotación [[maybe_unused]]. Comprueba lo que ocurre al compilar si quitas esa anotación y comentas el contenido de la función.

3.0.2 p3.h

  1. Tipos de datos definidos en p3.h.
    obstacle_sig_t :
    Tipo de dato que representa una señal que se activa al detectar un obstáculo en la posición destino
    out_map_sig_t :
    Tipo de dato que representa una señal que se activa cuando el robot intenta salirse del mapa.
    battery_sig_t :
    Tipo de dato que representa una señal que se activa cuando el robot no tiene suficiente batería para realizar el movimiento solicitado.
    enum Direction { UP, DOWN, LEFT, RIGHT } :
    Tipo enumerado que representa los posibles movimientos del robot.
  2. Registros:
    Position :
    Representa unas coordenadas en el mapa.
    Robot :
    Representa la información del robot. Incluye una posición y un nivel de batería.
    Obstacle :
    Representa un obstáculo en el mapa.
    Map :
    Representa un mapa en el que se sitúa un robot y un vector de obstáculos.
  3. Explicación de funciones:
    obstacle_sig_t& get_obstacle_signal() :
    Devuelve la señal de tipo obstacle_sig_t. Deberás decidir cómo implementarla.
    out_map_sig_t& get_out_map_signal() :
    Devuelve la señal de tipo out_map_sig_t. Deberás decidir cómo implementarla.
    battery_sig_t& get_battery_signal() :
    Devuelve la señal de tipo battery_sig_t. Deberás decidir cómo implementarla.
    Map createMap(uint32_t rows, uint32_t cols, const Position &pos_robot, uint16_t battery_level=100) :
    Crea un mapa nuevo. Debe inicializar sus datos utilizando los parámetros de la función. El nivel de batería, por defecto, tendrá el valor 100. Asumimos en todo momento que la esquina superior izquierda del mapa es la posición (0,0). Si las coordenadas iniciales del robot no se encuentran dentro del mapa, el robot se situará en la coordenada (0,0).
    bool placeObstacle(Map& map, const Position& position) :
    Añade un nuevo obstáculo al mapa solo si la posición se encuentra dentro del mapa. Devuelve cierto en el caso de que pueda añadirlo y falso en el caso contrario. Si ya había un obstáculo en esa posición, no se podrá añadir el nuevo obstáculo y deberá devolver falso. Consideramos que tanto las filas como las columnas comienzan en la posición 0.
    bool isObstacle(const Map& map, const Position& position) :
    Comprueba si en el mapa hay un obstáculo en la posición recibida por parámetro. Devuelve cierto en el caso de que sea un obstáculo y falso en el caso contrario.
    void moveRobot(Map& map, Direction direction) :
    Función encargada de mover el robot a una posición según la dirección proporcionada. Cada movimiento conlleva un consumo de batería, reduciendo su batería hasta 0, en cuyo caso, el robot no podrá moverse. Si se ha podido realizar el movimiento (no hay obstáculos, la posición se encuentra dentro del mapa y hay suficiente batería para realizar el movimiento), entonces se calculará el nuevo nivel de batería teniendo en cuenta el consumo de batería indicado por la constante kBATTERY_COST_MOVEMENT. Además, esta función es la encargada de activar las señales correspondientes según el caso que se encuentre. Estas señales están incluídas en p3.cc:
    • static obstacle_sig_t obst_sig :
      Se activa al intentar mover el robot a una posición donde hay un obstáculo que lo impide. En este caso, el robot no debe moverse de su sitio y el consumo en este caso está reflejado por la constante kBATTERY_COST_OBSTACLE.
    • static out_map_sig_t out_map_sig :
      Se activa al intentar mover el robot a una posición que se encuentra fuera del mapa. En este caso, el robot no debe moverse de su sitio y el consumo en este caso está reflejado por la constante kBATTERY_COST_OUT_MAP.
    • static battery_sig_t battery_sig :
      Se activa cuando se intenta realizar un movimiento que no se puede hacer debido a que no hay suficiente batería. Si se da este caso, el robot no debe moverse de su sitio. En este caso, no hay consumo de batería.
    • Importante: Antes de realizar el movimiento, se debe comprobar si el robot tiene suficiente batería para llevarlo a cabo. En el caso de que no haya suficiente batería, no se realizará el movimiento y no se activarán las señales obst_sig y out_map_sig, pero sí la de battery_sig.
    void moveRobotListMovements(Map &map, const vector<Direction>& list_directions) :
    Esta función realizará secuencialmente una lista de movimientos (con las mismas reglas descritas para la función moveRobot). Los movimientos que debe hacer se encuentran en el parámetro de tipo vector<Direction>.
    void displayMap(const Map& map) :
    Función que muestra el mapa en la consola. Representa al robot con la letra R y a los obstáculos con la letra X. Esta función ya la tienes implementada y no tienes que programarla.
  • A modo de recordatorio: el tipo std::vector<XXX> de C++ es similar a un array de C en el que todos los elementos de dicho vector son de tipo XXX. En el siguiente ejemplo, se puede ver un vector de elementos de tipo int. Inicialmente, una variable de tipo std::vector<XXX> está vacío. Para añadir elementos a ese vector, se utiliza la función push_back, como se puede ver en el ejemplo.
    1
    2
    3
    4
    
    std::vector<int> ve;
    ve.push_back(3);
    ve.push_back(7);
    // ve == [3,7]
    
  • Con la función size se puede conocer la cantidad de elementos que contiene el vector:
    1
    
    assert(ve.size() == 2);
    

4 División del código fuente en archivos

  • Tendremos un único archivo de cabecera (p3.h) con los prototipos de las funciones además de los #include necesarios para que no haya errores de compilación. Este archivo hará uso de la guarda para protegerlo de su inclusión más de una vez, como se vió en el tema-1.

  • Tendremos un único archivo de implementación (p3.cc) con la implementación de todas las funciones además de los #include necesarios. Este archivo no debe contener ningún programa principal (main), pero sí podrá contener las funciones auxiliares que necesites.

  • Para poder probar tus funciones dispones de un programa principal de prueba en el fichero mainp3.cc. No es necesario entregarlo, pero si se hace no pasa nada. Es importante que intentes diseñar todas las pruebas que se te ocurran para asegurarte de que tu implementación funciona correctamente.

  • Puedes emplear el archivo Makefile proporcionado para compilar y enlazar cada uno de estos programas principales.

  • A la hora de entregar la solución de la práctica, todos los archivos `.h' y `.cc' se deben encontrar dentro de una carpeta llamada irp2-p3.

5 Entrega. Requisitos técnicos

  • Requisitos que tiene que cumplir este trabajo práctico para considerarse válido y ser evaluado (si no se cumple alguno de los requisitos la calificación será cero):

  • El archivo entregado se llama irp2-p3.tgz (todo en minúsculas), no es necesario entregar ningún programa principal, sólo es necesario tu archivo `p3.cc' en una carpeta llamada irp2-p3. A su vez, esta carpeta estará comprimida en el fichero llamado irp2-p3.tgz. Este archivo lo puedes crear así en el terminal:

    tar cfz irp2-p3.tgz irp2-p3

  • También puedes emplear el archivo Makefile que tienes en la carpeta de la práctica para generar el fichero de la entrega automáticamente: make tgz.

  • A modo de aclaración, al descomprimir el archivo irp2-p3.tgz se crea un directorio de nombre irp2-p3 (todo en minúsculas) y dentro de esa carpeta es donde estarán los ficheros `.cc' y `.h'.

  • Dentro del directorio irp2-p3 estará al menos el archivo p3.cc (todo en minúsculas).

    Recuerda que el fichero ‘p3.hno se debe modificar, de lo contrario tus ficheros ‘.cc’ no compilarán con el corrector. No es necesario entregar el archivo de cabecera, si se hace no pasa nada.

  • Las clases, los métodos y las funciones que implementes deben llamarse como se indica en el enunciado (respetando en todo caso el uso de mayúsculas y minúsculas). Ten en cuenta que el corrector podrá conectar funciones diferentes de las que tienes en mainp3.cc a las señales, por lo que los mensajes de salida podrían cambiar.

  • Al principio de todos los ficheros fuente (`.h’ y `.cc’) entregados y escritos por ti se debe incluir un comentario con el nombre y el NIF (o equivalente) de la persona que entrega la práctica, como en el siguiente ejemplo:

    // NIF: 12345678-Z
    // NOMBRE: GARCIA PEREZ, LAURA
    
  • Un error de compilación/enlace implicará un cero en esa parte de la práctica.

  • Lugar y fecha de entrega : Recuerda que las entregas se realizan siempre en (y sólo en) https://pracdlsi.dlsi.ua.es en las fechas y condiciones allí publicadas. Puedes entregar la práctica tantas veces como quieras, sólo se corregirá la última entrega (las anteriores no se borran). El usuario y contraseña para entregar prácticas es el mismo que se utiliza en UACloud.

6 Detección de plagios/copias

  • En el Grado en Ingeniería Robótica, la Programación es una materia muy importante. La manera más efectiva de aprender a programar es programando y, por tanto, haciendo las prácticas correspondientes además de otros programas (más sencillos o más complicados, eso da igual).

  • Tratar de aprobar las asignaturas de programación sin programar (copiando) es complicado y obviamente, te planteará problemas para seguir otras asignaturas así como en tu futura vida laboral.

  • En una asignatura como Programación-II es difícil que alguien que no ha hecho las prácticas supere los exámenes de teoría o el de recuperación de prácticas.

  • IMPORTANTE:

    • Cada práctica debe ser un trabajo original de la persona que la entrega.

    • En caso de detectarse indicios de copia de una o más prácticas se tomarán las medidas disciplinarias correspondientes, informando a las direcciones de Departamento y de la EPS por si hubiera lugar a otras medidas disciplinarias adicionales.