Programación 2 Curso 2014/2015 Práctica 2 Halcón Milenario (parte 2) Esta práctica consiste en completar la práctica anterior, almacenando los datos de los viajes y permitiendo guardar y recuperar los datos de la nave desde ficheros binarios. Entrega 1. El último dı́a para entregar esta parte de la práctica es el viernes 24 de abril, hasta las 23:59. No se admitirán entregas fuera de plazo. 2. La práctica debe tener un único fichero fuente llamado “millenniumFalcon.cc”. Detección de plagios/copias En el Grado en Ingenierı́a Informática, la Programación es una materia fundamental, que se aprende programando y haciendo las prácticas de las diferentes asignaturas (y otros programas, por supuesto). Si alguien pretende aprobar las asignaturas de programación sin programar (copiando), obviamente tendrá serios problemas en otras asignaturas o cuando intente trabajar. Concretamente, en Programación 2 es muy difı́cil que alguien que no ha hecho las prácticas supere el examen de teorı́a, por lo que copiar una práctica es una de las peores decisiones que se puede tomar. La 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 suspenderá la asignatura a todos los alumnos implicados (tanto al que copia como al que se deja copiar), y se enviará un informe a la dirección de la EPS, para que se tomen las medidas disciplinarias oportunas. Normas generales comunes a todas las prácticas 1. La práctica se debe entregar exclusivamente a través del servidor de prácticas del departamento de Lenguajes y Sistemas Informáticos, al que se puede acceder desde la página principal del departamento ( www.dlsi.ua.es, “Entrega de prácticas”) o directamente en la url http://pracdlsi.dlsi.ua.es. No se admitirán entregas por otros medios (correo electrónico, Campus Virtual, etc.). El usuario y contraseña para entregar prácticas es el mismo que se utiliza en el Campus Virtual. La práctica se puede entregar varias veces, pero sólo se corregirá la última entrega. 2. El programa debe poder ser compilado sin errores con el compilador de C++ existente en la distribución de Linux de los laboratorios de prácticas; si la práctica no se puede compilar su calificación será 0. Se recomienda compilar y probar la práctica con el autocorrector inmediatamente antes de entregarla. Programación 2, 2014/2015 2 3. Los ficheros fuente deben estar adecuadamente documentados, con comentarios donde se considere necesario. Además, no se pueden utilizar variables globales de ninguna clase, los únicos sı́mbolos globales permitidos son las funciones, los tipos y las constantes. 4. La corrección de la práctica se hará de forma automática, por lo que es imprescindible respetar estrictamente los formatos de salida que se indican en este enunciado. Además, al principio de todos los ficheros fuente entregados se debe incluir un comentario con el DNI de la persona que entrega la práctica, con el siguiente formato: // DNI tuDNI Nombre donde tuDNI es el DNI del alumno correspondiente (sin letras), tal y como aparece en el Campus Virtual, y Nombre es el nombre completo; por ejemplo, el comentario podrı́a ser: // DNI 12345678 GARCIA GARCIA, JUAN MANUEL 5. El cálculo de la nota de la práctica y su influencia en la nota final de la asignatura se detallan en las transparencias de la presentación de la asignatura. Introducción En esta práctica se implementarán las opciones del menú que no se implementaron en la práctica anterior, y además, se deben implementar dos ampliaciones sobre la práctica anterior: 1. Añadir dos bodegas de carga más, de manera que la nave en total dispondrá de 3 bodegas. Este cambio afecta principalmente a la opción Load container, pero también a otras opciones. Cuando se introducen correctamente los datos de un contenedor, se almacenará provisionalmente en la bodega con menor número de contenedores (en caso de empate en la que tenga un número menor), para luego invocar al algoritmo para balancear la carga. Inicialmente, los contenedores se irán colocando en la primera bodega vacı́a. A partir del momento en que todas las bodegas tengan al menos un contenedor, el algoritmo tomará el peso total de cada bodega, y hará un intercambio de contenedores entre las bodegas con mayor y menor peso; en caso de igualdad de peso, se tomará la que tenga un número menor. En el caso de que las tres bodegas tengan el mismo peso (se puede detectar este caso porque la bodega con mayor peso es la misma que la bodega con menor peso) no se hará ningún intercambio, como tampoco se hará intercambio cuando alguna bodega esté vacı́a. El intercambio consistirá en extraer el último contenedor metido en la bodega con mayor peso, extraer el último de la de menor peso, y meter cada uno en la otra bodega, actualizando los pesos (y precios) de cada bodega.1 Este intercambio solamente se hará una vez, en caso contrario podrı́a producirse un bucle infinito. Por ejemplo, después de introducir el contenedor 8 en la bodega 2 (la que menos contenedores tiene junto con la 3) tenemos la siguiente situación de las bodegas: 1 Este algoritmo de balanceado de carga no es óptimo, pero es el mejor que se le ocurrió a Chewie. Programación 2, 2014/2015 Bodega 1 2 3 Contenedores (id,peso) (1, 100 Kg) (5, 15 Kg) (6, 22 Kg) (4, 67 Kg) (7, 55 Kg) (8, 33 Kg) (2, 120 Kg) (3, 27 Kg) 3 Peso 137 Kg 155 Kg 147 Kg El programa intercambiarı́a los contenedores 8 (de 33 Kg) y 6 (de 22 Kg), quedando las bodegas de esta forma: Bodega 1 2 3 Contenedores (id,peso) (1, 100 Kg) (5, 15 Kg) (8, 33 Kg) (4, 67 Kg) (7, 55 Kg) (6, 22 Kg) (2, 120 Kg) (3, 27 Kg) Peso 148 Kg 144 Kg 147 Kg Para evitar que la nave se desequilibre, cuando se elimine un contenedor (en la opción Unload container o en Remove passenger si se baja un pasajero que llevaba un contenedor de equipaje) se volverá a invocar al algoritmo de balanceado de carga para recolocar de forma adecuada los contenedores, siempre que no haya bodegas vacı́as. Un ejemplo de la salida de la opción Show data con 3 bodegas serı́a: Hold 1 (400 Kg, 402 credits) (3,300,301) (1,100,101) Hold 2 (200 Kg, 201 credits) (2,200,201) Hold 3 (400 Kg, 401 credits) (4,400,401) Seat 1: 123456,Luke Skywalker,1000,0 Seat 2: 987654,Obi wan Kenobi,1500,3 2. Almacenar los datos de los viajes en un registro de viajes: una vez se hayan calculado y mostrado los datos del viaje (en la opción de Travel), se guardarán en el registro de viajes de la nave, almacenando los datos primero en un struct como el siguiente: typedef struct { int km; int time; int consumption; int kminitial,kmhv,kmfinal; int tinitial,thv,tfinal; } Trip; Los datos de los viajes se almacenarán en una lista simplemente enlazada en la que siempre se insertará el nuevo viaje al principio de la lista. Cada nodo de la lista contendrá un registro Trip y un puntero al siguiente nodo. Al finalizar el programa la lista debe borrarse, liberando adecuadamente la memoria dinámica reservada. Programación 2, 2014/2015 4 Descripción de las opciones restantes Lo que debe hacer el programa en cada una de las opciones que quedaban por implementar es: Trip log : cuando se elija esta opción, el programa permitirá consultar los datos de los viajes realizados, para lo que mostrará el siguiente menú: 1- Show all 2- Filter km 3- Filter time 4- Filter consumption 5- Save log 6- Load log q- Quit Option: Si se elige la opción 1, se mostrarán todos los viajes tal y como están almacenados en la lista. Si se elige la opción 2, 3 o 4, se pedirán dos números enteros, el lı́mite inferior y el lı́mite superior (con los mensajes “Lower limit: ” y “Upper limit: ”), y a continuación se mostrarán todos los viajes cuyo kilometraje, tiempo o consumo se encuentre entre dichos lı́mites (ambos valores incluı́dos, y si ningún viaje encaja con esos lı́mites no saldrá nada). Si el usuario introduce un lı́mite superior menor que el lı́mite inferior, se debe emitir el siguiente mensaje de error: Error, wrong limits En las opciones 1 a 4 los viajes se mostrarán con un formato más compacto, con todos los datos del viaje en una sola lı́nea. Por ejemplo, para el viaje del ejemplo de la práctica anterior se mostrarı́a: 1357 km, 797 s, 75555 litres, (79 1200 78) [395 12 390] Si se elige la opción 5, el programa debe pedir un nombre de fichero (con el mensaje “Filename: ”), y almacenar en ese fichero de texto los datos de todos los viajes en el orden en el que aparecen en la lista, escribiendo en cada lı́nea todos los datos un viaje, en el mismo formato que en las opciones 1 a 4. Si el fichero no se puede abrir, se debe emitir el siguiente mensaje de error: Error, can’t open file Si al elegir alguna de las opciones 1, 2, 3, 4 o 5 todavı́a no hay viajes se debe emitir el siguiente error (sin llegar a pedir ningún dato adicional como los lı́mites o el nombre del fichero): Error, no trips in the log yet! Programación 2, 2014/2015 5 Si se elige la opción 6, el programa debe pedir un nombre de fichero y, si el fichero se puede abrir, borrar la lista y leer los datos de los viajes del fichero para meterlos en la lista insertando por la cabeza, lo cual provocará que la nueva lista esté invertida con respecto a la que se usó para generar el fichero, pero es absolutamente normal y no se debe hacer nada al respecto. Como en la opción anterior, si el fichero no se puede abrir se debe emitir el error de apertura del fichero. La lista se debe borrar solamente después de haber abierto el fichero con éxito. Finalmente, si se introduce una opción incorrecta en este menú se mostrará el mismo mensaje de error que en el menú principal. Save data : si se elige esta opción, el programa debe almacenar en el fichero binario “mfdata.dat” todos los datos de las bodegas y de los pasajeros (pero no el registro de viajes). Para ello deben emplearse los siguientes registros: typedef struct { double weight; int price; int ncontainers; } HoldBinary; const int MAXGIN=12; const int MAXNAME=20; typedef struct { char GIN[MAXGIN]; char name[MAXNAME]; int price; int idLuggage; int seat; } PassengerBinary; En el fichero binario “mfdata.dat” se almacenará la siguiente información: todas las bodegas, con sus datos. Además, después del registro de cada bodega (HoldBinary) se almacenarán todos los registros de sus contenedores (antes de los datos de la siguiente bodega) el identificador del siguiente contenedor, idNextContainer (int) los datos de cada pasajero, en el mismo orden que tienen en el vector de pasajeros. Si el GIN o el nombre de alguno de los pasajeros no cabe en el vector del registro PassengerBinary, se debe recortar para que quepa (con el “\0”).2 Si el fichero no puede abrirse se debe emitir el error de apertura del fichero. Load data : si el usuario elige esta opción, se debe pedir confirmación con el siguiente mensaje: Load data (current data will be deleted)? (Y/N) 2 En las transparencias del tema de ficheros se explica cómo hacerlo correctamente. Programación 2, 2014/2015 6 Si la respuesta del usuario es una “Y” entonces se intentará abrir el fichero “mfdata.dat”, y si se puede abrir el fichero se borrarán todos los datos de las bodegas y de los asientos de la nave (pero no de los viajes), y se leerán los datos del fichero binario. Como en la opción anterior, si el fichero no puede abrirse se debe emitir el error de apertura del fichero, y no debe borrarse nada si no se ha podido abrir el fichero, es decir, solamente se debe borrar los datos después de haber abierto el fichero con éxito. En el registro Spaceship, el identificador del siguiente contenedor será el que se lea desde el fichero, no debe dejarse el valor que tuviera (que podrı́a ser mayor o menor, en cuyo caso podrı́an generarse contenedores con el mismo identificador). Los contenedores se supone que fueron almacenados de forma balanceada en las bodegas, por lo que no es necesario invocar al algoritmo de balanceado de carga al leer los contenedores, simplemente se leen y almacenan en la bodega que les corresponda. Por otro lado, al leer los datos de los pasajeros es necesario ir actualizando el vector de asientos para evitar que los nuevos pasajeros que suban a la nave ocupen el mismo asiento que un pasajero leı́do del fichero. Uso del programa con argumentos El usuario puede invocar el programa desde la lı́nea de comandos con los siguientes argumentos (opcionales), o bien sin argumentos: Opción -bf : cuando se invoca el programa con esta opción, lo primero que debe hacer el programa es recuperar los datos del fichero binario “mfdata.dat”, pero sin pedir confirmación para borrar los datos actuales, puesto que al iniciar el programa no hay datos. Si el fichero puede abrirse, se leerán los datos (utilizando la función implementada en la opción correspondiente del menú), y a continuación se iniciará el programa como si no hubiera habido argumentos, mostrando el menú.3 Si el fichero no se puede abrir, después de emitir el mensaje correspondiente el programa terminará (sin hacer nada más ni emitir ningún mensaje más). Opción -lt <fichero> : al poner esta opción se asumirá que el argumento siguiente es un nombre de fichero, y el programa intentará abrir ese fichero, cargar los datos de los viajes en la lista (como en la opción Load log del menú de Trip log), e iniciar el programa como si no hubiera habido argumentos, mostrando el menú. Como en la opción anterior, si el fichero no puede abrirse se emitirá el correspondiente mensaje de error y se terminará el programa. Ambas opciones son compatibles, es decir, el usuario puede invocar el programa con una de las opciones, con las dos, o con ninguna. Si se produce algún error en los argumentos del programa, se debe emitir el siguiente mensaje de error (y salir del programa): Error in arguments, syntax: millenniumFalcon [-lt <filename> | -bf] 3 La implementación es muy sencilla: en la función main se debe comprobar y gestionar los argumentos justo antes del bucle do-while que implementa el menú. Programación 2, 2014/2015 7 Si el usuario introduce ambas opciones en la lı́nea de comandos, primero se debe leer el fichero binario y a continuación el fichero de los viajes. Implementación Para implementar la práctica debes tener en cuenta: 1. El menú de la opción de Trip log, como el menú principal, debe implementarse con una instrucción do-while con un switch en su interior. 2. Para almacenar los datos de los viajes se utilizará una lista simplemente enlazada en la que los datos del nuevo viaje se insertarán por la cabeza de la lista, tal y como se ha explicado en las clases de teorı́a 3. En el registro de la nave (Spaceship) debes sustituir la declaración de la bodega por un vector de 3 posiciones (declarando el “3” como constante): typedef struct { ... Hold holds[NUMHOLDS]; ... } Spaceship; 4. En general, se asumirá que no hay errores de lectura o de escritura, es decir, si se consigue abrir el fichero (para lectura o para escritura), se podrá leer/escribir sin problemas. 5. El resto de decisiones en la implementación de la práctica quedan a tu criterio, pero ten en cuenta que el código fuente de la práctica será revisado por tu profesor de prácticas.