/* */

9 de febrero de 2008

Problema 7.2

.
Enunciado
Una empresa de auxilio a vehicular necesita un programa para administrar la asignación de móviles a las diferentes zonas en las que los socios transitan.

La operatoria es la siguiente: cuando un socio necesita auxilio llama a central, indica su número de socio, la zona (barrio numerado de 1 a 15) y la dirección exacta donde se encuentra detenido. Si existe algún movil disponible en esa zona entonces se le asigna inmediatamente. Si no existe ningún movil disponible en esa zona (porque todos están atendiendo otros casos) entonces la solicitud debe ir a una cola de espera hasta que se libere algún movil de los que operan en la zona en cuestión.

En una misma zona pueden operar varios móviles pero un mismo movil solo opera en una única zona.

Cuando un movil finaliza un auxilio llama a central para indicar que cerró el caso y (si corresponde) que le asignen otro servicio. En el llamado se indica la zona y el tipo de servicio que se realizó.

La información que se maneja es la siguiente

Llamado de un socio

  • Número de Socio – (8 dígitos)
  • Número de Zona – (1..15)
  • Dirección - (string[20])
Llamado de un movil (para cerrar un caso)
  • Número de Zona
  • Número de Movil
  • Tipo de Servicio Realizado (1 caracter)

La empresa cuenta con un máximo de 200 móviles y se sabe que en ningún caso un mismo movil puede atender más de 50 servicios durante un día.

Se dispone del siguiente archivo:

MOVILES.dat
  • nroMovil – (4 dígitos)
  • nroZona – (1..15)
  • cantSrvAcum – (word, “cantidad de servicios acumulados”)
  • apeYNom – (string[30], “apellido y nombre del conductor”)

Se Pide

1 - Emitir el listado que se describe a continuación. Debe salir ordenado por número de zona y luego por número de movil. El número de orden debe tomarse como un valor que comienza desde 1 y se incrementa por cada servicio a lo largo de todas las operaciones del día, sin discriminar por zona.



2 - Actualizar el campo cantSrvAcum del archivo MOVILES.dat según la cantidad de servicios realizados por cada movil durante el día.

Se cuenta con las siguientes funciones (no deben ser desarrolladas, simplemene están disponibles para ser utilizadas):

- getTime(): longint
Retorna la hora actual en formato numérico

- timeToString( h:longint ): string[5]
Dada una hora obtenida con getTime retorna un string con formato "hh:mm"


Análisis
El programa debe reflejar la operatoria de trabajo que describe el enunciado. El operador (usuario que utiliza el programa) espera recibir llamados, los llamados pueden provenir de los socios (que se quedaron con el auto en alguna zona) o de los conductores de los móviles para indicar que cerraron un caso. Así hasta finalizar el día de trabajo.

De lo anterior se desprende que el programa que tenemos que desarrollar debe ser un programa interactivo, que solicite al operador el tipo de llamado ("de socio" o "de conductor") y luego solicite la información adicional según el tipo de llamado que recibió.

Por otro lado, tenemos un archivo con todos los móviles disponibles que serán asignados a los diferentes casos (según la zona) a medida que se vayan registrando los pedidos de auxilio. Al iniciar el día todos estos móviles estarán disponibles.

Luego de este análisis podemos deducir que el problema presenta:
  • una cola de móviles disponibles (una cola por cada zona)
  • una cola de socios en espera (una cola por cada zona)
  • una lista de móviles que están en atendiendo un caso (y por lo tanto momentaneamente no están disponibles)
Hagamos un pequeño resumen de las situaciones que pueden ocurrir para analizar que debemos hacer en cada caso.

1 - Llama un socio que se quedó en una zona. Leemos los datos adicionales y creamos un registro para encolalo en la cola de espera de la zona en la que se encuentra el socio. Luego hacemos la asignación del movil (ver más abajo).

2 - Llama un conductor indicando que cerró un caso. Leemos los datos adicionales y cerramos el servicio. Esto implica buscar el movil en la lista de móviles que están brindando servicio, asignarle los datos que correspondan para cerrar el caso (por ejemplo tipo de servicio y hora de finalización) y volver a encolar al movil en la lista de móviles disponibles para luego realizar una nueva asignación (ver más abajo).

Cuando hablamos de "hacer la asignación" nos referimos a lo siguiente: verificar si hay elementos en la cola de socios en espera y verificar si hay elementos en la cola de móviles disponibles. Si las dos colas (de la zona en cuestión) tienen elementos entonces desencolamos un elemento en cada cola y establecemos la relación de servicio agregando (o actualizando) el nodo que correspondan a la lista de móviles prestando servicios.

Con la siguiente estructura podemos soportar las colas de móviles disponibles por zona. Un array de 15 posiciones donde cada elemento es un puntero a un nodo con información de un movil.



El nodo debe tener el número de movil y la posición en el archivo del registro que lo describe ya que más adelante tendremos que acceder directamente para recuperar el nombre del conductor y actualizar el campo de servicios acumulados.

Recordemos que la estructura representa "un array de colas". Como estudiamos en el capítulo de estructuras dinámicas, una cola se implementa sobre una lista circular, de forma tal que el último nodo de la lista apunta al primero.

Veamos la parte de la sección type en donde definimos estos tipos de datos.
   1:
2:const
3: // constante que defime el maximo de zonas
4: // existentes (segun el enunciado)
5: CANT_ZONAS = 15;
6:
7:type
8: // registro para el archivo MOVILES.dat
9: RMoviles = record
10: nroMovil: integer;
11: nroZona: byte;
12: cantSrvAcum: word;
13: apeYNom: string[30];
14: end;
15:
16: // tipo para el archivo de moviles
17: FMoviles = file of RMoviles;
18:
19:
20: // nodo y puntero a nodo para las colas de
21: // moviles disponibles
22:
23: PDisponible = ^RDisponible;
24:
25: RDisponible = record
26: nroMovil: integer;
27: pos: byte;
28: sig: PDisponible;
29: end;
30:
31: // array para contener las colas de moviles
32: // disponibles por zona
33: ADisponible = array[1..CANT_ZONAS] of PDisponible;
34:

El programa debe recorrer el archivo y crear las 15 colas (una por zona) encolando los móviles indicados en los registros del archivo.

Cuando llama un socio tomamos un movil de la cola y "lo asignamos". Esto implica que el movil dejará de estar disponible momentaneamente para atender el problema del socio.

Lo reflejaremos con la siguiente estructura.



Esta estructura representa un array de 15 posiciones (una por zona) donde cada elemento contiene el puntero a una lista de móviles. Cada nodo de esta lista tiene un puntero a una sublista de servicios atendidos por ese movil.

Por lo tanto cuando el programa asigne un movil a un caso de auxilio lo que tendremos que hacer es acceder por nroZona a este array (aServ) buscar o insertar un nodo en la lista y agregar un nodo en la sublista.

La lista principal representa a los móviles que brindaron (hasta el momento) servicio en la zona y la sublista representa el detalle de los servicios prestados por cada movil.

Obviamente, al momento de asignar un movil solo podremos guardar la hora de inicio del servicio. Los otros datos (hora de fin y tipo de servicio) los tendremos que asignar cuando se reciba el llamado del conductor para cerrar el caso.

Veamos la parte de la sección type que define esta estructura.
   1:
2:
3:const
4: // :
5: // continuacion...
6: // :
7: SOLICITA_SRV = 1;
8: FINALIZA_SRV = 2;
9: FINALIZA_PRG = 2;
10:
11:type
12: // :
13: // continuacion...
14: // :
15:
16: // nodo que describe cada servicio prestado
17: // por un movil
18: PSrv = ^RSrv;
19: RSrv = record
20: horaInicio: longint;
21: horaFin: longint;
22: tipoSrv: byte;
23: sig: PSrv;
24: end;
25:
26: // nodo que describe cada movil que presto
27: // servicio
28: PSrvHeader = ^RSrvHeader;
29: RSrvHeader = record
30: nroMovil: integer;
31: pos: byte;
32: cont: byte;
33: lstSrv: PSrv;
34: sig: PSrvHeader;
35: end;
36:
37: // array para contener las listas de moviles
38: // y (por cada movil) los servicios que presto
39: AServicio = array[1..CANT_ZONAS] of PSrvHeader;
40:

El último punto que debemos analizar es el caso en el que llame un socio pidiendo auxilio y en ese momento no exista disponibilidad de móviles en la zona porque todos están atendiendo otros servicios. En este caso el debemos asignar el pedido del socio en una cola de espera de forma tal que cuando se libere un movil en esa zona lo pueda atender.

La estructura que utilizaremos es similar a la de móviles disponibles. Una cola por zona donde cada nodo tiene la información del socio.



Veamos la parte de la sección type que describe esta estructura.
   1:
2:type
3: // :
4: // continuacion...
5: // :
6:
7: // nodo que representa un socio a la espera
8: // de un movil.
9: PEspera = ^REspera;
10: REspera = record
11: codSocio: longint;
12: dir: string[20];
13: horaLlamada: longint;
14: sig: PEspera;
15: end;
16:
17: // array de colas de espera de socios
18: AEspera = array[1..CANT_ZONAS] of REspera;
19:

Con la estructura de datos resuelta podemos comenzar a atender la operatoria diaria. Esto implica leer una opción para determinar un tipo de llamada y luego, según corresponda leer los datos relacionados con el tipo de llamada.





El diagrama del programa principal comienza invocando al procedimiento inicializarArrays pasándole como parámetros los tres arrays (el de móviles disponibles: aDisp, el de socios en espera: aEsp y el de servicios: aServ). Todos son arrays de punteros por lo tanto este procedimiento debe asignar NIL a todos sus elementos.

Siguiendo con el programa principal, luego de abrir el archivo se invoca al procedimiento cargarMovilesDisponibles cuyo objetivo es recorrer el archivo y encolar cada movil en la cola de la zona que corresponda, según el campo nroZona del registro leido.



Luego se ingresa una opción por teclado. Las opciones posibles fueron definidas como constantes (ver código Pascal) y son:
  • SOLICITA_SRV - Llama un socio pidiendo auxilio
  • FINALIZA_SRV - Llama un conductor para cerrar un caso
  • FINALIZA_PRG - El operador decide finalizar el programa
Si la opción ingresada fue SOLICITA_SRV leemos los datos adicionales y luego encolamos un registro en la cola de socios en espera. Luego invocamos al procedimiento asignarMovil que analizaremos más abajo.

Si la opción ingresada fue FINALIZA_SRV invocamos al procedimiento cerrarServicio que retorna en la variable rDisp los datos del movil que acaba de liberarse. Encolamos este registro en la cola de móviles disponibles e invocamos al procedimiento asignarMovil.

Veamos el diagrama del procedimiento cerrarServicio.



En este procedimiento accedemos a la lista de servicios aServ[nroZona] para buscar (y si no encontramos para agregar) el nodo que representa al movil nroMovil. La función buscarEInsertar retorna un puntero a ese nodo. Incrementamos el contador (pHSrv^.cont) de servicios realizados por el movil y luego buscamos el último nodo de su sublista (pHSrv^.lstSrv) para cerrar el servicio, lo que implica asignar la hora de finalización y el tipo de servicio realizado.

Antes de finalizar el procedimiento debemos asignar valores a la variable rDisp (que se recibe por referencia).

Veamos ahora el procedimiento asignarMovil.



Lo primero que hacemos es verificar si alguna de las dos colas está vacia. En este caso finalizamos la llamada al procedimiento con la sentencia exit ya que no tenemos nada para hacer. Es decir: si alguna de las dos colas está vacia entonces o no hay socios en espera o no hay móviles disponibles para asignar (o las dos cosas).

Si pasamos de los dos if es porque existe al menos un socio en espera y disponemos de un movil para asignar.

Desencolamos al socio (rEsp), desencolamos al movil (rDisp) y creamos un registro de tipo RSrvHeader (rHSrv) para buscarEInsertar en la lista se móviles en servicio. También creamos un registro de tipo RSrv (rSrv) para agregar a la sublista del movil al que le asignamos el caso (pHSrv^.lstSrv). Notemos que a este registro solo podemos asignarle la hora de inicio (rSrv.horaInicio) ya que los otros campos (horaFin y tipoSrv) recién los tendremos cuando llame el conductor del movil para cerrar el caso.

Por último mostramos un mensaje al operador para que le indique (por teléfono) al conductor del movil asignado el número de socio y la dirección exacta en donde lo tiene que ir a auxiliar.

Si la opción ingresada fue FINALIZA_PRG entonces debemos emitir el listado y actualizar el archivo. Para esto invocamos al procedimiento listarYActualizar que debe recorrer el array aServ y por cada posición (zona) debe recorrer la lista de móviles que trabajaron. Por cada movil debe recorrer la sublista de servicios prestados.

Con esto resuelvemos el listado del punto 1. Debemos tener en cuenta también que por cada movil debemos mostrar el nombre del conductor por lo tanto tendremos que hacer un acceso directo al archivo y recuperar este dato. Ese será un buen momento también para actualizar el campo cantSrvAcum con el valor del contador cont del nodo.

El desarrollo de este procedimiento queda a cargo del lector.





Algoritmos y Estructuras de Datos UTN - UBA

No hay comentarios: