-- UDP transmissions tester for the libUDP -- S.MG, 2018 with Ada.Text_IO; use Ada.Text_IO; with Ada.Calendar; use Ada.Calendar; -- for local time with Ada.Calendar.Time_Zones; use Ada.Calendar.Time_Zones; -- UTC_Time_Offset with Ada.Calendar.Formatting; use Ada.Calendar.Formatting; -- for unix-like with Ada.Directories; use Ada.Directories; -- for log files checks with Interfaces; use Interfaces; --Unsigned_8 for payload with UDP; with MT; package body UDP_Tester is procedure Create_If_Not_Exists( Filename: in String; Header: in String) is F : File_Type; begin begin Open(File => F, Mode => Append_File, Name => Filename); Close(File => F); exception when Ada.Text_IO.Name_Error => Create( File => F, Mode => Out_File, Name => Filename); Put_Line(F, Header); Close(F); Put_Line("Created file " & Filename); end; end Create_If_Not_Exists; procedure Sender( Receiver_IP: in String; Receiver_Port: in Unsigned_16; Sender_Port: in Unsigned_16 ) is -- payload sizes to choose from Sizes : array(1..Max_Len-Header_Len+1) of Natural; Len_Sizes : Natural := Sizes'Length; Packet_Size : Positive; Pos : Natural; -- log file for sender OutFile : File_Type; OutFileName : constant String := "sender_udp_log.txt"; HeaderOutFile : constant String := "SizeSent" & ",TimeSent" & ",DestinationIP" & ",DestinationPort" & ",Seed"; -- seed for initialising the MT PRNG Seed : constant MT.U32 := MT.U32(Ada.Calendar.Clock - Epoch); begin -- create log file and add header to it if it doesn't exist already Create_If_Not_Exists( OutFileName, HeaderOutFile); -- initialize MT rpng MT.Init_Genrand(Seed); -- initialize payload sizes to choose from for I in Sizes'Range loop Sizes(I) := I - 1; end loop; -- send packages in a loop Sending_Loop: while Len_Sizes > 0 loop --a delay of 1 sec to avoid saturating the link with burst-mode delay 1.0; -- pick randomly one of the available sizes for payload Pos := Natural(MT.Gen_U32 mod MT.U32(Len_Sizes)) + 1; Packet_Size := Sizes( Pos ) + Header_Len; -- shift available sizes to exclude the latest pick if PosSizes'First then Sizes(Pos..Len_Sizes-1) := Sizes(Pos+1..Len_Sizes); end if; -- decrease length of available sizes Len_Sizes := Len_Sizes - 1; -- instantiate UDP sender of picked size and send packet declare K : constant Positive := Packet_Size; -- sender will have *current* size of UDP packet package UDP_Sdr is new UDP( Payload_Size => K ); -- socket, addr, port Socket : UDP_Sdr.Socket; Local_Endpoint : UDP_Sdr.Endpoint := (Address => UDP_Sdr.INADDR_ANY, Port => Sender_Port); Remote_Endpoint : UDP_Sdr.Endpoint := (Address => UDP_Sdr.IP_From_String(Receiver_IP), Port => Receiver_Port); Sent_Payload : UDP_Sdr.Payload; TimeUTC : Ada.Calendar.Time; TimeUnix : Interfaces.Unsigned_32; begin -- message to console Put_Line("Sending packet Len_Sizes= " & Integer'Image(Len_Sizes) & " with length " & Positive'Image(Sent_Payload'Length)); -- fill the payload, so starting after header octets for I in (Sent_Payload'First + Header_Len) .. Sent_Payload'Last loop Sent_Payload(I) := Interfaces.Unsigned_8(I mod 256); end loop; -- fill the header part -- time (UTC): year, month, day, seconds since midnight (2 octets) TimeUTC := Ada.Calendar.Clock; TimeUnix := Interfaces.Unsigned_32(TimeUTC - Epoch); Sent_Payload(Sent_Payload'First+3) := Interfaces.Unsigned_8( TimeUnix mod 256 ); TimeUnix := Shift_Right(TimeUnix, 8); Sent_Payload(Sent_Payload'First+2) := Interfaces.Unsigned_8( TimeUnix mod 256 ); TimeUnix := Shift_Right(TimeUnix, 8); Sent_Payload(Sent_Payload'First+1) := Interfaces.Unsigned_8( TimeUnix mod 256 ); TimeUnix := Shift_Right(TimeUnix, 8); Sent_Payload(Sent_Payload'First) := Interfaces.Unsigned_8( TimeUnix mod 256 ); -- size of message (full, payload+header) Sent_Payload(Sent_Payload'First + 4) := Interfaces.Unsigned_8( Sent_Payload'Length / 256); Sent_Payload(Sent_Payload'First + 5) := Interfaces.Unsigned_8( Sent_Payload'Length mod 256); -- send packet UDP_Sdr.Open_Socket(Socket, Local_Endpoint); UDP_Sdr.Transmit(Socket, Remote_Endpoint, Sent_Payload); UDP_Sdr.Close_Socket(Socket); -- log the packet TimeUnix := Interfaces.Unsigned_32(TimeUTC - Epoch); Open(File => OutFile, Mode => Append_File, Name => OutFileName); Put(OutFile, Integer'Image(Sent_Payload'Length)); Put(OutFile, "," & Unsigned_32'Image(TimeUnix)); Put(OutFile, "," & Receiver_IP); Put(OutFile, "," & Unsigned_16'Image(Receiver_Port)); Put_Line(OutFile, "," & MT.U32'Image(Seed)); Close(File => OutFile); end; end loop Sending_Loop; end Sender; procedure Receiver( Receiver_Port: in Unsigned_16 ) is -- receiver HAS to have max len for UDP size; it doesn't know it in advance package UDP_Rcv is new UDP( Payload_Size => Max_Len ); -- socket and local endpoint to receive on Socket : UDP_Rcv.Socket; Local_Endpoint : UDP_Rcv.Endpoint := (Address => UDP_Rcv.INADDR_ANY, Port => Receiver_Port); -- received payload and source information Received_Payload : UDP_Rcv.Payload; Received_Origin : UDP_Rcv.Endpoint; Received_Len : Unsigned_32; -- for logging TimeUTC : Ada.Calendar.Time; TimeUnix : Interfaces.Unsigned_32; TimeSent : Interfaces.Unsigned_32; SizeSent : Interfaces.Unsigned_32; Expected : Interfaces.Unsigned_8; --one octet of payload ErrCount : Natural; -- log file OutFile : File_Type; ErrFile : File_Type; OutFileName : constant String := "receiver_udp_log.txt"; ErrFileName : constant String := "receiver_udp_err_log.txt"; HeaderOutFile : constant String := "SizeSent" & ",TimeSent" & ",SourceIP" & ",SourcePort" & ",SizeReceived" & ",TimeReceived" & ",ErrorOctetsCount"; HeaderErrFile : constant String := "SizeSent" & ",TimeSent" & ",SourceIP" & ",SourcePort" & ",SizeReceived" & ",TimeReceived" & "(,Position" & --position in payload of err octet ",ValueReceived" & ",ValueExpected)*"; begin -- create log files and add headers to them if they don't exist already Create_If_Not_Exists( OutFileName, HeaderOutFile); Create_If_Not_Exists( ErrFileName, HeaderErrFile); Put_Line("Opening socket on local endpoint " & UDP_Rcv.IP_To_String(Local_Endpoint.Address) & " :" & UDP_Rcv.IP_Port'Image(Local_Endpoint.Port) & "..."); UDP_Rcv.Open_Socket(Socket, Local_Endpoint); Put_Line("Waiting for payloads..."); -- endless, receiving loop Receiving_Loop: loop -- receive a payload UDP_Rcv.Receive(Socket, Received_Origin, Received_Payload, Received_Len); -- log the received payload -- -- get local, arrival time TimeUTC := Ada.Calendar.Clock; TimeUnix := Unsigned_32(TimeUTC - Epoch); -- local, arrival time -- -- append to log file(s) Open(File => OutFile, -- main log Mode => Append_File, Name => OutFileName); Open(File => ErrFile, -- error log Mode => Append_File, Name => ErrFileName); -- -- info from the received package itself TimeSent := 0; for I in 1..4 loop TimeSent := Shift_Left(TimeSent, 8); TimeSent := TimeSent + Unsigned_32(Received_Payload(I)); end loop; SizeSent := Shift_Left(Unsigned_32(Received_Payload(5)), 8) + Unsigned_32(Received_Payload(6)); Put(OutFile, Unsigned_32'Image(SizeSent)); Put(OutFile, "," & Unsigned_32'Image(TimeSent)); -- -- info from receiver end Put(OutFile, "," & UDP_Rcv.IP_To_String(Received_Origin.Address)); Put(OutFile, "," & UDP_Rcv.IP_Port'Image(Received_Origin.Port)); Put(OutFile, "," & Unsigned_32'Image(Received_Len)); Put(OutFile, "," & Unsigned_32'Image(TimeUnix)); -- any octets that are different from expected (i.e. errors) ErrCount := 0; for I in Header_Len+1 .. Natural(Received_Len) loop Expected := Unsigned_8(I mod 256); if Received_Payload(I) /= Expected then ErrCount := ErrCount + 1; -- first time *only*, add all the package info as well if ErrCount = 1 then Put(ErrFile, Unsigned_32'Image(SizeSent)); Put(ErrFile, "," & Unsigned_32'Image(TimeSent)); Put(ErrFile, "," & UDP_Rcv.IP_To_String(Received_Origin.Address)); Put(ErrFile, "," & UDP_Rcv.IP_Port'Image(Received_Origin.Port)); Put(ErrFile, "," & Unsigned_32'Image(Received_Len)); Put(ErrFile, "," & Unsigned_32'Image(TimeUnix)); end if; -- at all times: add the error details - position, received, expected Put(ErrFile, "," & Natural'Image(I) & "," & Unsigned_8'Image( Received_Payload(I) ) & "," & Unsigned_8'Image( Expected ) ); end if; end loop; -- if there was at least an error, move to next line in error log if ErrCount > 0 then New_Line(File => ErrFile); end if; -- in all cases, record ErrCount in main log file too Put_Line(OutFile, "," & Natural'Image(ErrCount)); -- logging done for this entry, close the log files Close(File => OutFile); Close(File => ErrFile); Put_Line("Received payload from " & UDP_Rcv.IP_To_String(Received_Origin.Address) & " :" & UDP_Rcv.IP_Port'Image(Received_Origin.Port) & " with length " & Unsigned_32'Image(Received_Len)); end loop Receiving_Loop; -- infinite, receiving loop -- close the socket and tidy up UDP_Rcv.Close_Socket(Socket); end Receiver; end UDP_Tester;