Concurrent Elevator (1)


package Elevator is

   -- the range of the floors in the building
   NumberFloors : constant Integer := 5;
   type FloorNumber is new Integer range 1..NumberFloors;
   type FloorList is array (FloorNumber) of Boolean;

   -- the direction of moving
   type DirectionType is (Up, Down);

   function GetActionId return Integer;

   task Controller is
      entry Approaching (F : in FloorNumber);
      entry StoppedAt (F : in FloorNumber);
      entry CarReady (F : in FloorNumber);
      entry CarReadyEmpty (F : in FloorNumber);
      entry NewDestination (F : in FloorNumber);
   end Controller;

   task Car is
      entry Stop;
      entry Continue;
      entry DoorsOpen;
      entry DoorsClose;
      entry MoveUp;
      entry MoveDown;
   end Car;

end Elevator;



package body Elevator is

task body Controller is

   CallsUp, CallsDown, Stops : FloorList;

   PeoplePresent : Boolean := False;
   DirectionUP : boolean;
   Random : Integer;
   Floor : FloorNumber;
   Dummy : Boolean := False;

   function GetCallFloor return FloorNumber;
   function IsDestination (Floor : in FloorNumber) return Boolean;
   function isInCallsUp (Floor : in FloorNumber) return Boolean;
   function isInCallsDown (Floor : in FloorNumber) return Boolean;
   function ThereAreRequestsAbove (Floor : in FloorNumber) return Boolean;

begin
   DirectionUP := true; --event[{directionup=t}]
   loop
      -- obtain a random number that is supposed to model obtaining the
      -- code for a particular real-time event
      Random := GetActionId;
      case Random is
	 when 1 => 
	    -- someone pushed the call Up button on one of the floors
	    Floor := GetCallFloor;

	    -- enter the floor in the list of calls Up
	    CallsUp(Floor) := True;
	 when 2 => 
	    -- someone pushed the call Up button on one of the floors
	    Floor := GetCallFloor;

	    -- enter the floor in the list of calls Up
	    CallsDown(Floor) := True;
	 when 3 => 
	    -- the car is approaching a certain floor
	    accept Approaching(F : in FloorNumber) do
	       Floor := F; --event[{reporting_approach}]
	       null;
	    end Approaching;

	    -- stop there only if this floor was requested as a destination
	    -- by one of the people in the car or has a person waiting who
	    -- is going in the direction the car is currently going
	    if IsDestination(Floor) or 
	       ((DirectionUp) and then IsInCallsUp(Floor)) or
	       ((not DirectionUp) and then IsInCallsDown(Floor)) then
	       Car.Stop; --event[{ordered_to_stop}]
	       null;
	    -- also stop if the floor is the top or first floor
	    elsif ((DirectionUp) and then (Floor = FloorNumber'last)) or
		  ((not DirectionUp) and then (Floor = FloorNumber'first)) then
	       Car.Stop; --event[{ordered_to_stop}]
	       null;
	    else
	       Car.Continue;
	    end if;
	 when 4 => 
	    -- the car has stopped at a certain floor
	    accept StoppedAt(F : in FloorNumber) do
	       Floor := F; --event[{not_moving}]
	       null;
	    end StoppedAt;

	    -- open the doors
	    Car.DoorsOpen; -- event[{open_doors}]

	    -- no more people should be waiting for this car on this floor
	    if DirectionUp then
	       CallsUp(Floor) := False;
	    else
	       CallsDown(Floor) := False;
	    end if;

	    -- set the direction to Up if the car is on the first floor and
	    -- to Down if it is on the top floor
	    if Floor = FloorNumber'First then
	       DirectionUp := true; --event[{directionup=t}]
	       null;
	    end if;

	    if Floor = FloorNumber'Last then
	       DirectionUp := false; --event[{directionup=f}]
	       null;
	    end if;
	 when 5 =>
	    -- the car is ready to move (people are not standing in the
	    -- doors)
	    accept CarReady(F : in FloorNumber) do
	       Floor := F;
	    end CarReady;

	    -- all people who wanted should have exited by now
	    Stops(Floor) := False;
	    -- close the doors
	    Car.DoorsClose; -- event[{close_doors}]

	    -- move the car in the same direction unless there are no 
	    -- desired destinations in that direction
	    if (DirectionUp) then
	       null; --event[{directionupis=t}]
	       if ThereAreRequestsAbove(Floor) then
		  Car.MoveUp; --event[{ordered_to_move}]
		  null; --event[{ordered_to_move_up}]
		  null;
	       else
		  DirectionUp := false; --event[{directionup=f}]
		  Car.MoveDown; --event[{ordered_to_move}]
		  null; --event[{ordered_to_move_down}]
		  null;
	       end if;
	    else
	       null; --event[{directionupis=f}]
	       Car.MoveDown; --event[{ordered_to_move}]
	       null; --event[{ordered_to_move_down}]
	       null;
	    end if;
	 when 6 => 
	    -- someone pushed a button while in the car
	    accept NewDestination(F : in FloorNumber) do
	       Floor := F;
	    end NewDestination;

	    Stops(Floor) := True;
	 when 7 => 
	    -- the car is open and empty
	    accept CarReadyEmpty(F : in FloorNumber) do
	       Floor := F;
	    end CarReadyEmpty;

	    -- clear all the stops now because there's nobody in the car
	    for I in FloorNumber loop
	       Stops(I) := False;
	    end loop;

	    -- this line is added only for the benefit of FLAVERS, in
	    -- general the controller does not have to be terminating
	    -- the exit from the loop may only occur here, when there are
	    -- no people in the car and the doors are open
	    exit when Dummy;

	    -- close the doors
	    Car.DoorsClose; --event[{close_doors}]

	    -- of course it's unfair, but start moving to the first floor
	    Car.MoveDown;  --event[{ordered_to_move}]
	    null; --event[{ordered_to_move_down}]
	    null;
	 when others =>
	    null;
      end case;
      null;
   end loop;
end Controller;

task body Car is

   CurrentFloor : FloorNumber := FloorNumber'First;
   Random : Integer;
   Dummy : Boolean := False;
   WaitForResponse : Duration := 10.0;
   WaitWithDoorsOpen : Duration := 100.0;

begin
   loop
      Random := GetActionId;
      case Random is
	 when 1 => 
	    -- the car is approaching the CurrentFloor
	    Controller.Approaching(CurrentFloor);

	    -- wait for the response from the controller, if none then stop
	    -- to be on the safe side
	    select
	       accept Stop;
	       Controller.StoppedAt(CurrentFloor);
	    or
	       accept Continue;
	    or
	       delay WaitForResponse;
	       Controller.StoppedAt(CurrentFloor);
	    end select;
	 when 2 => 
	    -- receive the order to open the doors
	    accept DoorsOpen;

	    -- wait for a predefined amount of time and then either report
	    -- that the car is ready or that it is empty and ready
	    -- (make a non-deterministic choice)
	    delay WaitWithDoorsOpen;
	    if Dummy then
	       Controller.CarReady(CurrentFloor);
	    else
	       Controller.CarReadyEmpty(CurrentFloor);
	       exit when Dummy;
	    end if;
	 when 3 => 
	    accept DoorsClose;
	 when 4 => 
	    accept MoveUp;
	 when 5 => 
	    accept MoveDown;
	 when others => 
	    -- report to the controller that a new destination button was
	    -- pushed from inside the car
	    Controller.NewDestination(CurrentFloor);
      end case;
      null;
   end loop;
end Car;

end Elevator;