OBJECT room IS

 ACTIVE

spec

-- Dining Philosophers - Ada 95 edition
-- 
-- Room.OBCS is responsible for assigning seats at the table, 
-- "left" and "right" chopsticks, and for reporting interesting 
-- events to the outside world.
-- 
-- Michael B. Feldman, The George Washington University, July 1995.
-- HOOD version by Pierre Dissaux, TNI, June 1998.


--  required interface : 
--   Required OPERATION :
--     OPERATION : open of object : windows
--     OPERATION : title of object : windows
--     OPERATION : borders of object : windows
--     OPERATION : put#1 of object : windows
--     OPERATION : new_line of object : windows
--     OPERATION : start_eating of object : phil
--     OPERATION : get_name of object : society
--     OPERATION : Clock of object : calendar
--   Required EXCEPTION : NONE
--   Required TYPE :
--     TYPE : Window of object : windows
--     TYPE : Philosopher of object : phil
--     TYPE : Philosopher_Ptr of object : phil
--     TYPE : States of object : phil
--     TYPE : Unique_DNA_Codes of object : society
--     TYPE : Stick of object : chop
--     TYPE : Stick_Ptr of object : chop
--     TYPE : Boolean of object : standard
--     TYPE : Integer of object : standard
--     TYPE : Natural of object : standard
--     TYPE : Positive of object : standard
--     TYPE : Time of object : calendar
--   Required CONSTANT : NONE
--   Required DATA : NONE

--  visibility on required modules : 
with phil;
use type phil.Philosopher;
use type phil.Philosopher_Ptr;
use type phil.States;
with society;
use type society.Unique_DNA_Codes;
with chop;
use type chop.Stick;
use type chop.Stick_Ptr;

package room is

  -- Room.Get_Stick is an access function to internal Sticks variable.
  -- It requires a chopstick ID (Which_Stick) to return a pointer to relevant
  --  protected object.
  -- (cf.FR2/Provide_chopsticks:)
  function get_stick (
    which_Stick : IN Positive)
     return Chop.Stick_Ptr;

  -- A dedicated state variable manages current state of dining room.
  -- This variable "Started" has a default value of "FALSE" and become "TRUE"
  --  after Start_Serving has been executed.
  -- Start_Serving and Report_State have both STATE and protocole constraints.
  task OBCS is
    entry start_serving;
    entry report_state (
      Which_Phil : IN Society.Unique_DNA_Codes; 
      Which_State : IN Phil.States; 
      How_Long : IN Natural := 0; 
      Which_Meal : IN Natural := 0);
  end OBCS;
  -- Room.Start_Serving is called by main procedure and renames OBCS.Start_Serving
  --  task entry.
  -- This procedure has no parameter. 
  -- (cf.FR1/Prepare&begin_diner:)
  procedure start_serving
     renames OBCS.start_serving;

  -- Room.Report_State is called by Phil.Start_Eating and renames OBCS.Report_State
  --  task entry.
  -- This procedure has four parameters:
  -- - Which_Phil: identifies actual Philosopher sending the message.
  -- - State: current state of sender.
  -- - How_Long: length of current state (or identifier of used chopstick).
  -- - Which_Meal: current meal.
  -- (cf.FR3/Report_events:)
  procedure report_state (
    Which_Phil : IN Society.Unique_DNA_Codes; 
    Which_State : IN Phil.States; 
    How_Long : IN Natural := 0; 
    Which_Meal : IN Natural := 0)
     renames OBCS.report_state;

end room;

body

-- Dining Philosophers - Ada 95 edition
-- 
-- Room.OBCS is responsible for assigning seats at the table, 
-- "left" and "right" chopsticks, and for reporting interesting 
-- events to the outside world.
-- 
-- Michael B. Feldman, The George Washington University, July 1995.
-- HOOD version by Pierre Dissaux, TNI, June 1998.

with Phil;
pragma Elaborate(Phil);

--  visibility on required modules : 
with calendar;
use type calendar.Time;
with windows;
use type windows.Window;

--  visibility on objects required by nested operation bodies : 


package body room is

  -- Specifies the total number of seats around the table. It is limited to
  --  five in this example.
  Table_Size : constant := 5;

  -- Identifies the possible locations around the table.
  subtype Table_Type is Positive range 1..Table_Size;

  -- First chopstick shared between seats 5 and 1.
  S1 : aliased Chop.Stick;

  -- second chopstick shared between seats 1 and 2.
  S2 : aliased Chop.Stick;

  -- Third chopstick shared between seats 2 and 3.
  S3 : aliased Chop.Stick;

  -- Fourth chopstick shared between seats 3 and 4.
  S4 : aliased Chop.Stick;

  -- Fifth chopstick shared between seats 4 and 5.
  S5 : aliased Chop.Stick;

  -- An array of pointers to the chopsticks.
  Sticks : array (Table_Type) of Chop.Stick_Ptr;

  -- First Philosopher.
  P1 : aliased Phil.Philosopher(My_ID => 1);

  -- Second Philosopher.
  P2 : aliased Phil.Philosopher(My_ID => 2);

  -- Third Philosopher.
  P3 : aliased Phil.Philosopher(My_ID => 3);

  -- Fourth Philosopher.
  P4 : aliased Phil.Philosopher(My_ID => 4);

  -- Fifth Philosopher.
  P5 : aliased Phil.Philosopher(My_ID => 5);

  -- An array of pointers to the Philosophers.
  Phils : array (Table_Type) of Phil.Philosopher_Ptr;

  -- An array of windows. One window for each seat.
  Phil_Windows : array (Table_Type) of Windows.Window;

  -- An array to indicate which seat each Philosopher occupies:
  -- Philosopher 1 occupies seat 1;
  -- Philosopher 2 occupies seat 3;
  -- Philosopher 3 occupies seat 5;
  -- Philosopher 4 occupies seat 4;
  -- Philosopher 5 occupies seat 2;
  Phil_Seats : array (Society.Unique_DNA_Codes) of Table_Type;

  -- Current time obtained by Calendar.Clock.
  T : Natural;

  -- Time when application is launched.
  Start_Time : Calendar.Time;

  -- State variable to switch between "waiting" and "dining" states.
  -- Initial state is "Waiting".
  Started : boolean := false;

  -- Performs following actions:
  -- - Calculates Start_Time;
  -- - Puts chopsticks on the table;
  -- - Assigns Philosophers to seats at the table;
  -- - Opens and draw a window to observe each seat;
  -- - Assigns right and left chopsticks to each Philosopher;
  procedure OPCS_start_serving is
  begin
  
    -- starting date is stored:
    Start_Time := Calendar.Clock;
  
    -- chopsticks are put on the table:
    Sticks :=
      (S1'Access,
       S2'Access,
       S3'Access,
       S4'Access,
       S5'Access);
  
    -- philosophers are assigned to seats at the table
    Phils :=
      (P1'Access,
       P3'Access,
       P5'Access,
       P4'Access,
       P2'Access);
  
    -- which seat each phil occupies:
    Phil_Seats := (1, 3, 5, 4, 2); 
  
    -- a window is open for each seat:
    Phil_Windows :=
      (Windows.Open(( 1, 24), 7, 30),
       Windows.Open(( 9,  2), 7, 30),
       Windows.Open(( 9, 46), 7, 30),
       Windows.Open((17,  7), 7, 30),
       Windows.Open((17, 41), 7, 30));
  
    -- windows borders are drawn:
    for Which_Win in Phil_Windows'range loop
      Windows.Borders(Phil_Windows(Which_Win),'+','|','-');
    end loop;
  
    -- philosophers are assigned their chopsticks:
    Phils (1).Start_Eating(1, 1, 2);
    Phils (3).Start_Eating(3, 3, 4);
    Phils (2).Start_Eating(2, 2, 3);
    Phils (5).Start_Eating(5, 1, 5);
    Phils (4).Start_Eating(4, 4, 5);
  
    -- dining room state changes:
    Started := true;
  end OPCS_start_serving;

  -- Performs following actions:
  -- - Calculates current time;
  -- - Displays a message on relevant window.
  procedure OPCS_report_state (
    Which_Phil : IN Society.Unique_DNA_Codes; 
    Which_State : IN Phil.States; 
    How_Long : IN Natural := 0; 
    Which_Meal : IN Natural := 0) is
  begin
  
    T := Natural (Calendar.Clock - Start_Time);
  
    case Which_State is
  
      when Phil.Breathing =>
        Windows.Title(
            Phil_Windows(Phil_Seats(Which_Phil)),
            Society.Get_Name(Which_Phil), '-');
        Windows.Put(
            Phil_Windows(Phil_Seats(Which_Phil)),
            "T =" & Integer'Image (T) & " " & "Breathing...");
        Windows.New_Line(Phil_Windows(Phil_Seats(Which_Phil)));
  
      when Phil.Thinking =>
        Windows.Put(
            Phil_Windows(Phil_Seats(Which_Phil)),
            "T =" & Integer'Image (T) & " " & "Thinking"
            & Integer'Image (How_Long) & " seconds.");
        Windows.New_Line(Phil_Windows(Phil_Seats(Which_Phil)));
  
      when Phil.Eating =>
        Windows.Put(
            Phil_Windows(Phil_Seats(Which_Phil)),
            "T =" & Integer'Image (T) & " " & "Meal"
            & Integer'Image (Which_Meal) & ","
            & Integer'Image (How_Long) & " seconds.");
        Windows.New_Line(Phil_Windows(Phil_Seats(Which_Phil)));
  
      when Phil.Done_Eating =>
        Windows.Put(
            Phil_Windows(Phil_Seats(Which_Phil)),
            "T =" & Integer'Image (T) & " " & "Yum-yum (burp)");
        Windows.New_Line(Phil_Windows(Phil_Seats(Which_Phil)));
  
      when Phil.Got_One_Stick =>
        Windows.Put(
            Phil_Windows(Phil_Seats(Which_Phil)),
            "T =" & Integer'Image (T) & " " & "First chopstick"
            & Integer'Image (How_Long));
        Windows.New_Line(Phil_Windows(Phil_Seats(Which_Phil)));
  
      when Phil.Got_Other_Stick =>
        Windows.Put(
            Phil_Windows(Phil_Seats(Which_Phil)),
            "T =" & Integer'Image (T) & " " & "Second chopstick"
            & Integer'Image (How_Long));
        Windows.New_Line(Phil_Windows(Phil_Seats(Which_Phil)));
  
      when Phil.Dying =>
        Windows.Put(
            Phil_Windows(Phil_Seats(Which_Phil)),
            "T =" & Integer'Image (T) & " " & "Croak");
        Windows.New_Line(Phil_Windows(Phil_Seats(Which_Phil)));
  
    end case; -- Which_State
  end OPCS_report_state;

  -- Just returns a pointer to specified chopstick.
  function get_stick (
    which_Stick : IN Positive)
     return Chop.Stick_Ptr is
  begin
    return Sticks(Which_Stick);
  end get_stick;

  -- A dedicated state variable manages current state of dining room.
  -- This variable "Started" has a default value of "FALSE" and become "TRUE"
  --  after Start_Serving has been executed.
  -- Start_Serving and Report_State have both STATE and protocole constraints.
  task body OBCS is

    start_serving_accepted : boolean := false;
    report_state_accepted : boolean := false;
  begin
    loop
      start_serving_accepted := not Started;
      report_state_accepted := Started;
      select
        -- ASER
        when start_serving_accepted =>
        accept start_serving;
        begin
          OPCS_start_serving;
          -- Initial state. Started is set to FALSE.
          -- This transition is triggered by Start_Serving execution request.
          -- No additional condition, neither exception code is required.
          Started := true;
        exception
          when others =>
            raise;
        end;
      or
        when not start_serving_accepted =>
        accept start_serving;
        null;
      or
        -- HSER
        when report_state_accepted =>
        accept report_state (
          Which_Phil : IN Society.Unique_DNA_Codes; 
          Which_State : IN Phil.States; 
          How_Long : IN Natural := 0; 
          Which_Meal : IN Natural := 0) do
          begin
            OPCS_report_state (Which_Phil, Which_State, How_Long, Which_Meal );
            -- Running state. Started is set to TRUE.
            -- This transition is triggered by Report_Sta        te execution request.
            -- No additional condition, neither exception code is required.
            -- Current state is not changed.
            Started := true;
          exception
            when others =>
              raise;
          end;
        end report_state;
      or
        when not report_state_accepted =>
        accept report_state (
          Which_Phil : IN Society.Unique_DNA_Codes; 
          Which_State : IN Phil.States; 
          How_Long : IN Natural := 0; 
          Which_Meal : IN Natural := 0);
        null;
      or
        terminate;
      end select;
    end loop;

  end OBCS;


end room;

END room