Simulación de Ascensores

El siguiente enunciado propone la creación de un programa en ada que gestione un sistema de control de ascensores. Dado que ada es un lenguaje de programación bastante didáctico a la hora de estudiar la concurrencia, este ejercicio resulta muy interesante para practicar este tema.

En un edificio de oficinas se tienen dos ascensores con memoria (las llamadas pueden acumularse). En cada planta del edificio (enumeradas de 0 a N) hay un pulsador común para llamar a los ascensores (llamada externa). En el interior de cada ascensor hay un panel con pulsadores para cada planta (llamada interna).

Programar en Ada las tareas correspondientes a los ascensores (y todas aquellas tareas adicionales que se consideren necesarias), de forma que se verifiquen las siguientes propiedades:

  • Un ascensor ocioso (no tiene llamadas pendientes) permanece en la planta en la que atendió la última llamada, y con las puertas cerradas, hasta que reciba alguna llamada.
  • Un ascensor solo se detiene en una planta si hay llamadas pendientes correspondientes a dicha planta. En este caso abrirá sus puertas durante T segundos y las volverá a cerrar.
  • Toda llamada (interna/externa) será atendida en tiempo finito.
  • Simetría: ningún ascensor es preferible a otro.
  • Concurrencia: ambos ascensores son independientes en sus movimientos, pudiendo subir/bajar simultáneamente.
Una propuesta de solución (junto con unas pruebas de funcionamiento al final) sería el siguiente:

with Ada.Text_Io; use Ada.Text_Io;
with Ada.Integer_Text_Io; use Ada.Integer_Text_Io;

procedure Ascensor is
   type Id_Ascen is range 1..2;
   type Pisos is mod 10;
   P1 : Pisos := 0; --Piso en el que se encuentra el ascensor uno.
   P2 : Pisos := 0; --Piso en el que se encuentra el ascensor dos.
   Ab1 : Boolean := False; --Testigo para saber si están abiertas las puertas del ascensor 1.
   Ab2 : Boolean := False; --Testigo para saber si están abiertas las puertas del ascensor 2.
   L1 : Boolean := True; --Testigo que muestra si está libre el ascensor 1.
   L2 : Boolean := True; --Testigo que muestra si está libre el ascensor 2.
   Dif1, Dif2 : Integer := 0;
   Aux1 : Id_Ascen;
   --//////////////////
   auxpis : Pisos;
   ext : Boolean;
   --//////////////////
   
   -- Tarea que organiza las llamadas
   task Tarea_Call is
      entry L_Externa(Num : Pisos; Id : out Id_Ascen); --LLamada externa desde uno de los pisos del edificio.
      entry L_Interna(Num : Pisos; Id : Id_Ascen); --LLamada interna en el ascensor con determinada id.
   end Tarea_Call;
   
   -- Tarea que controla los movimientos de los ascensores
   task Tarea_Asc1 is
      entry A1(Num : Pisos; externa : boolean); --Entrada para el movimiento del ascensor 1.
   end Tarea_Asc1;
   task Tarea_Asc2 is
      entry A2(Num : Pisos; externa : boolean); --Entrada para el movimiento del ascensor 2.
   end Tarea_Asc2;
   
   task body Tarea_Call is
   begin
      loop
         accept L_Externa(Num : Pisos; Id : out Id_Ascen) do
            loop
               if L1 = True then
                  Dif1 := Integer(P1) - Integer(Num);
                  Dif1 := Abs(Dif1);
               end if;
               if L2 = True then
                  Dif2 := Integer(P2) - Integer(Num);
                  Dif2 := Abs(Dif2);
               end if;
               --Condiciones en las que un ascensor está libre y el otro no
               if L1 = True and L2 = false then
                  Tarea_Asc1.A1(Num, True);
                  Id := 1;
               end if;
               if L1 = False and L2 = True then
                  Tarea_Asc2.A2(Num, True);
                  Id := 2;
               end if;
               --********************************
               if Dif1 > Dif2 then
                  Tarea_Asc2.A2(Num, True);
                  Id := 2;
                  exit;
               end if;
               if Dif2 > Dif1 then
                  Tarea_Asc1.A1(Num, True);
                  Id := 1;
                  exit;
               end if;
               if Dif1 = Dif2 then
                  Dif1 := 0;
                  Dif2 := 0;
                  Tarea_Asc1.A1(Num, True);
                  Id := 1;
                  exit;
               end if;
            end loop;
         end L_Externa;
         accept L_Interna(Num : Pisos; Id : Id_Ascen) do
            if Integer(Id) = 1 then
               Tarea_Asc1.A1(Num, false);
            end if;
            if Integer(Id) = 2 then
               Tarea_Asc2.A2(Num, false);
            end if;
         end L_Interna;
      end loop;
   end Tarea_Call;
   
   task body Tarea_Asc1 is --Instrucciosnes para el ascensor 1.
   begin
      loop
         accept A1(Num : Pisos; Externa : Boolean) do
         auxpis := Num;
         Ext := Externa;
         end A1; --Aquí termina el accept para que el procesador se libere y pueda asignarse a otra tarea.
         L1 := False; --Pone a falso la señal de servicio
         if ext = true then
            Put_Line(" Recibida llamada externa al ascensor 1.");
         else
            Put_Line(" Recibida llamada interna al ascensor 1.");
         end if;
         Ab1 := False; --Cierra puertas
         Put_Line("Puertas cerradas en A1.");
         Put_Line(" En movimiento ... ");
         if auxpis > P1 then --Sentencia que analiza la diferencia de pisos para saber si subir o bajar.
            loop
               exit when P1 = auxpis;
               Put_Line("   Planta " & Pisos'image(P1) & ".");
               P1 := P1 + 1;
            end loop; --Bucle para subir.
            Ab1 := True;
            Put_Line("Puertas abiertas en A1.");
            delay 5.0; --Cinco segundos de puertas abiertas.
            Put_Line("Puertas cerradas en A1.");
            Ab1 := False;
         else
            loop
               exit when P1 = auxpis;
               Put_Line("   Planta " & Pisos'image(P1) & ".");
               P1 := P1 - 1;
            end loop; --Bucle para bajar.
            Ab1 := True;
            Put_Line("Puertas abiertas en A1.");
            delay 5.0; --Cinco segundos de puertas abiertas.
            Put_Line("Puertas cerradas en A1.");
            Ab1 := False;
         end if;
         --end A1;
      end loop;
   end Tarea_Asc1; --Fin de instrucciones para el ascensor 1.
   
   task body Tarea_Asc2 is --Instrucciones para el ascensor 2.
   begin
      loop
         accept A2(Num : Pisos; Externa : Boolean) do
         auxpis := Num;
         Ext := Externa;
         end A2; --Aquí termina el accept para que el procesador se libere y pueda asignarse a otra tarea.
         L2 := False; --Pone a falso la señal de servicio
         if ext = true then
            Put_Line(" Recibida llamada externa al ascensor 2.");
         else
            Put_Line(" Recibida llamada interna al ascensor 2.");
         end if;
         Ab2 := False; --Cierra puertas
         Put_Line("Puertas cerradas en A2.");
         Put_Line(" En movimiento ... ");
         if auxpis > P2 then --Sentencia que analiza la diferencia de pisos para saber si subir o bajar.
            loop
               exit when P2 = auxpis;
               Put_Line("   Planta " & Pisos'image(P2) & ".");
               P2 := P2 + 1;
            end loop; --Bucle para subir.
            Ab2 := True;
            Put_Line("Puertas abiertas en A2.");
            delay 5.0; --Cinco segundos de puertas abiertas.
            Put_Line("Puertas cerradas en A2.");
            Ab2 := False;
         else
            loop
               exit when P2 = auxpis;
               Put_Line("   Planta " & Pisos'image(P2) & ".");
               P2 := P2 - 1;
            end loop; --Bucle para bajar.
            Ab2 := True;
            Put_Line("Puertas abiertas en A2.");
            delay 5.0; --Cinco segundos de puertas abiertas.
            Put_Line("Puertas cerradas en A2.");
            Ab2 := False;
         end if;
         --end A2;
      end loop;
   end Tarea_Asc2; --Fin de instrucciones para el ascensor 2.

--Tarea que controla los usuarios.
--Lo hemos hecho para que cada uno llame desde un piso distinto y vaya a un piso distinto.
task type Tarea_Usu;
task body Tarea_Usu is
   --De esta forma manejamos 10 usuarios, modificando el bucle podremos poner más o menos.
   --Nota: nuestra simulación se basa en que siempre que hacemos una llamada externa en la 
   --simulación se producirá en la siguiente línea una llamada interna en el ascensor que acudió a la externa.
   begin
   for i in 0..10 loop
      Tarea_Call.L_Externa(Pisos(i)+2, Aux1);
      Tarea_Call.L_Interna(Pisos(i)+5, Aux1);
   end loop;
end Tarea_Usu;

func : Tarea_Usu;

begin
Put_Line("------------");
Put_Line(" ASCENSORES ");
Put_Line("------------");   
--Tarea_Call.L_Externa(5, Aux1);
--Tarea_Call.L_Interna(9, Aux1);
--Tarea_Call.L_Externa(2, Aux2);
--Tarea_Call.L_Interna(8, Aux2);
--Tarea_Call.L_Externa(2, Aux);
--Tarea_Call.L_Interna(0, Aux);
end Ascensor;
	

Para obtener un fichero con el código y poder interpretarlo mejor haga clic aquí.

El enunciados (sin la solución) en formato pdf está aquí.

vidalmb_admin – Lun, 03/04/2006 – 19:37