
uses
  Crt,
  CoBase,
  CoIpV4,
  CoEth01,
  CoINET,
  CoUDP,
  NumToStr;


procedure PrintEthStatistics( Sock: PCoDevice );
const
  PAD = 6;
var
  Counters : TEthCounters;
  Status   : TCoStatus;
  Delta    : Longint;
begin
  Status := Sock^.CoIoctl( DEV_CLASS_NIC, 0,
              IOCTL_ETH01_GETCOUNTERS, @Counters, SizeOf( Counters ), nil );

  if Status = CST_SUCCESS then
  begin

    WriteLn( 'Frames Sent             ', Counters[ ETHCTR_TX_FRAMES ]: PAD );
    Write  ( 'Frames Received         ', Counters[ ETHCTR_RX_FRAMES ]: PAD );
    Delta := Counters[ ETHCTR_TX_FRAMES ] - Counters[ ETHCTR_RX_FRAMES ];
    if Delta >= 0 then
      WriteLn( '   +', Delta, ' ' )
    else
      WriteLn( '   ', Delta, ' ' );
    WriteLn;
    WriteLn( 'TX Single Collisions    ', Counters[ ETHCTR_TX_SINGLECOL ]: PAD );
    WriteLn( 'TX Multiple Collisions  ', Counters[ ETHCTR_TX_MULTIPLECOL ]: PAD  );
    WriteLn( 'TX Defferals            ', Counters[ ETHCTR_TX_DEFFERAL ]: PAD );
    WriteLn( 'TX Excessive Defferals  ', Counters[ ETHCTR_TX_EXCDEFFERAL ]: PAD );
    WriteLn( 'TX Carrier Lost         ', Counters[ ETHCTR_TX_LOSTCARRIER ]: PAD );
    WriteLn( 'TX SQET                 ', Counters[ ETHCTR_TX_SQET ]: PAD );
    WriteLn( 'TX Link Error           ', Counters[ ETHCTR_TX_LINKERROR ]: PAD );
    WriteLn( 'TX Underrun             ', Counters[ ETHCTR_TX_UNDERRUN ]: PAD );
    WriteLn( 'TX Late Collisions      ', Counters[ ETHCTR_TX_LATECOL ]: PAD );
    WriteLn( 'TX 16 Collisions        ', Counters[ ETHCTR_TX_16COL ]: PAD );
    WriteLn;
    WriteLn( 'RX Frame Too Short      ', Counters[ ETHCTR_RX_TOOSHORT ]: PAD );
    WriteLn( 'RX Frame Too Long       ', Counters[ ETHCTR_RX_TOOLONG ]: PAD );
    WriteLn( 'RX FIFO Overrun         ', Counters[ ETHCTR_RX_OVERRUN ]: PAD );
    WriteLn( 'RX Bad CRC              ', Counters[ ETHCTR_RX_BADCRC ]: PAD );
    WriteLn( 'RX Invalid Header       ', Counters[ ETHCTR_RX_INVHEADER ]: PAD );
    WriteLn( 'RX Discards             ', Counters[ ETHCTR_RX_DISCARDS ]: PAD );
    WriteLn( 'Reinitializations       ', Counters[ ETHCTR_REINITIALIZE ]: PAD );

    WriteLn( 'MemAvail: ', MemAvail div 1024 );

  end
  else
    WriteLn( 'CoIoctl() failed: ', CoStatusToStr( Status ) );
end;


procedure PrintIPStatistics( Sock: PCoDevice );
var
  Counters : TIPCounters;
  Status   : TCoStatus;
begin
  Status := Sock^.CoIoctl( DEV_CLASS_IPV4, 0, IOCTL_IPV4_GETCOUNTERS,
             @Counters,  SizeOf( Counters ), nil );

  if Status = CST_SUCCESS then
  begin
    WriteLn( 'IP   TX Requests         ', Counters[ IPCTR_TX_REQUESTS ] );
    WriteLn( '     TX Datagrams        ', Counters[ IPCTR_TX_DATAGRAMS ] );
    WriteLn( '     TX Routing Errors   ', Counters[ IPCTR_TX_ROUTINGERR ] );
    WriteLn( '     TX Discards         ', Counters[ IPCTR_TX_DISCARDS ] );
    WriteLn( '     TX Datagrams Frag.  ', Counters[ IPCTR_TX_DGRAMFRAG ] );
    WriteLn( '     TX Fragments        ', Counters[ IPCTR_TX_FRAGMENTS ] );
    WriteLn;
    WriteLn( '     RX Datagrams        ', Counters[ IPCTR_RX_DATAGRAMS ] );
    WriteLn( '     RX Header Errors    ', Counters[ IPCTR_RX_HEADERERR ] );
    WriteLn( '     RX Unknown Protocol ', Counters[ IPCTR_RX_UNKNPROTO ] );
    WriteLn( '     RX Discards         ', Counters[ IPCTR_RX_DISCARDS ] );
    WriteLn;
    WriteLn( 'ARP  TX Requests         ', Counters[ ARPCTR_TX_REQUESTS ] );
    WriteLn( '     TX Replies          ', Counters[ ARPCTR_TX_REPLIES ] );
    WriteLn( '     RX Requests         ', Counters[ ARPCTR_RX_REQUESTS ] );
    WriteLn( '     RX Replies          ', Counters[ ARPCTR_RX_REPLIES ] );
    WriteLn;
    WriteLn( 'ICMP TX Messages         ', Counters[ ICMPCTR_TX_MESSAGES ] );
    WriteLn( '     RX Messages         ', Counters[ ICMPCTR_RX_MESSAGES ] );
  end
  else
    WriteLn( 'CoIoctl() failed: ', CoStatusToStr( Status ) );

end;


procedure OpenCloseTest;
var
  Status : TCoStatus;
  Sock   : PCoDevice;
begin
  Status := NetOpenSocket( 'UDP', 'LPORT=5000', '', Sock );


  if Status <> CST_SUCCESS then
  begin
    WriteLn( 'NetOpenSocket() failed: ', CoStatusToStr( Status ) );
  end;

  Status := Sock^.CoBind( nil, COF_PROPAGATE, nil );

  if Status <> CST_SUCCESS then
  begin
    WriteLn( 'CoBind() failed: ', Sock^.CoStatusToStr( Status ) );
    CoReleaseDevicePtr( Sock );
    Exit;
  end;

  Status := Sock^.CoUnbind( 0, nil );

  if Status <> CST_SUCCESS then
  begin
    WriteLn( 'CoUnbind() failed: ', Sock^.CoStatusToStr( Status ) );
  end;

  NetCloseSocket( Sock );

end;

procedure EchoTest;
var
  Sock     : PCoDevice;
  Status   : TCoStatus;
  RecvBuff : array[0..4095] of Byte;
  RecvLen  : Word;
  SrcAddr  : TCoAddress;
  Timeouts : TCoDeviceTimeouts;

  S        : string;

begin

  Status := NetOpenSocket( 'UDP', 'LPORT=5000', '', Sock );

  if Status <> CST_SUCCESS then
  begin
    WriteLn( 'NetOpenSocket() failed: ', CoStatusToStr( Status ) );
  end;

  Status := Sock^.CoGetOptionString( DEV_CLASS_UDPMUX, @S,
              SizeOf(S) - 1, nil );

  Sock^.CoGetTimeouts( Timeouts );
  Timeouts.RecvTimeout := 100;
  Sock^.CoSetTimeouts( Timeouts );

  Status := Sock^.CoBind( nil, COF_PROPAGATE, nil );

  if Status <> CST_SUCCESS then
  begin
    WriteLn( 'CoBind() failed: ', Sock^.CoStatusToStr( Status ) );
    CoReleaseDevicePtr( Sock );
    Exit;
  end;

  WriteLn( 'UDP port initialized' );

  WriteLn( 'Press any key to leave test' );

  ClrScr;

  repeat

    Status := Sock^.CoRecvBufferFrom( @RecvBuff, SizeOf( RecvBuff ),
                                        @RecvLen, 0, @SrcAddr, nil );

    GotoXY( 1, 2 );

    PrintEthStatistics( Sock );

    WriteLn;

    PrintIpStatistics( Sock );

    case Status of

      CST_SUCCESS:
      begin

        Status := Sock^.CoSendBufferTo( @RecvBuff, RecvLen, 0,
                                          @SrcAddr, nil );

        if Status <> CST_SUCCESS then
        begin
          WriteLn( 'CoSendBufferTo() failed: ', Sock^.CoStatusToStr( Status ) );
        end;


      end;

      CST_ERR_TIMEOUT:
      begin
      end;

    else

      WriteLn( 'CoRecvBufferFrom() failed: ', Sock^.CoStatusToStr( Status ) );

    end;

  until Keypressed;


  Status := Sock^.CoUnbind( 0, nil );

  if Status <> CST_SUCCESS then
  begin
    WriteLn( 'CoUnbind() failed: ', Sock^.CoStatusToStr( Status ) );
  end;

  NetCloseSocket( Sock );

end;


var
  Status: TCoStatus;

begin

  WriteLn( 'MemAvail: ', MemAvail );

  { Otevreni TCP/IP stacku }

  Status := NetOpenStack( 'ETH01', 'IOBASE=$300',
             'IPADDR="192.168.1.210" REASMTO=5', '' );


  if Status <> CST_SUCCESS then
  begin
    WriteLn( 'NetOpenStack() failed: ', CoStatusToStr( Status ) );
    Exit;
  end;

  WriteLn( 'TCP/IP stack initialized' );

  { Spusteni testu }

  EchoTest;

  { Uzavreni TCP/IP stacku }

  NetCloseStack( '' );

  WriteLn( 'MemAvail: ', MemAvail );

end.