1. -- 
  2. -- Uwe R. Zimmer, Australia 2015 
  3. -- 
  4.  
  5. --  with Ada.Exceptions;            use Ada.Exceptions; 
  6. with Ada.Numerics.Float_Random; use Ada.Numerics.Float_Random; 
  7. with Ada.Strings.Bounded;       use Ada.Strings.Bounded; 
  8. with Ada.Text_IO;               use Ada.Text_IO; 
  9. with GNAT.Command_Line;         use GNAT.Command_Line; 
  10. with GNAT.Sockets;              use GNAT.Sockets; 
  11. with Neat_Sockets;              use Neat_Sockets; 
  12.  
  13. procedure Ring_Node is 
  14.  
  15.    type Command_Line_Options is record 
  16.       This_Addr   : Inet_Addr_Type := Addresses (Get_Host_By_Name (Host_Name), 1); 
  17.       This_Port   : Port_Type      := 45042; 
  18.       Next_Addr   : Inet_Addr_Type := Addresses (Get_Host_By_Name (Host_Name), 1); 
  19.       Next_Port   : Port_Type      := 45042; 
  20.    end record; 
  21.  
  22.    CLP : Command_Line_Options; 
  23.  
  24.    Float_Generator : Generator; 
  25.  
  26.    procedure Print_Options is 
  27.  
  28.    begin 
  29.       New_Line; Put ("accepted options:"); 
  30.       New_Line; Put ("   [-a {IP address of this node : String    }] -> "); Put (Image (CLP.This_Addr)); 
  31.       New_Line; Put ("   [-n {IP address of next node : String    }] -> "); Put (Image (CLP.Next_Addr)); 
  32.       New_Line; Put ("   [-p {This node's port        : Port_Type }] ->");  Put (Port_Type'Image (CLP.This_Port)); 
  33.       New_Line; Put ("   [-q {Next node's port        : Port_Type }] ->");  Put (Port_Type'Image (CLP.Next_Port)); 
  34.       New_Line; 
  35.       New_Line; 
  36.    end Print_Options; 
  37.  
  38. begin 
  39.    Initialize_Option_Scan; 
  40.    loop 
  41.       declare 
  42.          Option : constant Character := Getopt ("a: n: p: q:"); 
  43.       begin 
  44.          case Option is 
  45.             when ASCII.NUL => exit; 
  46.             when 'a' => CLP.This_Addr := Inet_Addr (Parameter); 
  47.             when 'n' => CLP.Next_Addr := Inet_Addr (Parameter); 
  48.             when 'p' => CLP.This_Port := Port_Type'Value (Parameter); 
  49.             when 'q' => CLP.Next_Port := Port_Type'Value (Parameter); 
  50.             when others => raise Program_Error; 
  51.          end case; 
  52.       exception 
  53.          when others => raise Program_Error with ("---> Error in option -" & Option); 
  54.       end; 
  55.    end loop; 
  56.  
  57.    Print_Options; 
  58.  
  59.    Put_Line ("This is ring node at IP: " & Image (Addresses (Get_Host_By_Name (Host_Name))) & " serving port: " & Port_Type'Image (CLP.This_Port)); 
  60.  
  61.    Reset (Float_Generator); 
  62.  
  63.    declare 
  64.  
  65.       package String_80 is new Generic_Bounded_Length (80); 
  66.       use String_80; 
  67.  
  68.       type Election_Message is record 
  69.          Random_Id : Uniformly_Distributed := Random (Float_Generator); 
  70.          IP_Addr   : Bounded_String        := To_Bounded_String (Image (Addresses (Get_Host_By_Name (Host_Name), 1))); 
  71.       end record; 
  72.  
  73.       function "<" (M1, M2 : Election_Message) return Boolean is 
  74.         (M1.Random_Id < M2.Random_Id or else 
  75.            (M1.Random_Id = M2.Random_Id and then M1.IP_Addr < M2.IP_Addr)); 
  76.  
  77.       Server_Socket : constant Socket_Type := Open_Server_Port (Port => CLP.This_Port); 
  78.  
  79.       Incoming_Connection_Socket, 
  80.       Outgoing_Connection_Socket  : Socket_Type; 
  81.  
  82.       Incoming_Connection_Address : Sock_Addr_Type; 
  83.  
  84.       Incoming_Channel, 
  85.       Outgoing_Channel : Stream_Access; 
  86.  
  87.       Election_Bid, Message : Election_Message; 
  88.  
  89.    begin 
  90.  
  91.       -- 
  92.       -- The following two, blocking code sections need to be completed before 
  93.       -- the node can start operations. Make sure that you are not deadlocking 
  94.       -- your ring and also not begin to operate on channels which have not been 
  95.       -- established yet. 
  96.       -- 
  97.       -------------------------------------------------------------------------- 
  98.       --  Put_Line ("Waiting for previous node to connect"); 
  99.       --  Incoming_Channel := Accept_Connection (Server_Socket, Incoming_Connection_Socket, Incoming_Connection_Address); 
  100.       --  Put_Line ("--> Previous node connected from: " & Image (Incoming_Connection_Address)); 
  101.       -------------------------------------------------------------------------- 
  102.       --  Put_Line ("Waiting for next node to become available"); 
  103.       --  Outgoing_Channel := 
  104.       --    Connect (Server_Addr       => CLP.Next_Addr, 
  105.       --             Port              => CLP.Next_Port, 
  106.       --             Connection_Socket => Outgoing_Connection_Socket); 
  107.       --  Put_Line ("--> Connected to next node at: " & Image (CLP.Next_Addr) & ":" & Port_Type'Image (CLP.Next_Port)); 
  108.       -------------------------------------------------------------------------- 
  109.  
  110.       -- The following ring election algorithm is a slight simplification of 
  111.       -- Ernest Chang, Rosemary Roberts - 1979: 
  112.       -- "An improved algorithm for decentralized extrema-finding in circular 
  113.       --   configurations of processes". 
  114.  
  115.       Put_Line ("Sending out election bid"); 
  116.  
  117.       Election_Message'Write (Outgoing_Channel, Election_Bid); 
  118.  
  119.       loop 
  120.  
  121.          begin 
  122.             Election_Message'Read (Incoming_Channel, Message); 
  123.             Put ("Incoming message .. "); 
  124.  
  125.          exception 
  126.             when End_Error => 
  127.                Put_Line ("No more messages --> End of election"); 
  128.                exit; 
  129.          end; 
  130.  
  131.          if Message = Election_Bid then 
  132.             Put_Line ("--> Found own message -> I win!"); 
  133.             exit; 
  134.          elsif Election_Bid < Message then 
  135.             Put_Line ("--> Forward"); 
  136.             Election_Message'Write (Outgoing_Channel, Message); 
  137.          else 
  138.             Put_Line ("--> Drop"); 
  139.          end if; 
  140.  
  141.       end loop; 
  142.       Close_Connection (Outgoing_Connection_Socket); 
  143.       Close_Connection (Incoming_Connection_Socket); 
  144.       Close_Socket (Server_Socket); 
  145.    end; 
  146. end Ring_Node;