unit uEcpSvr;
{ define DEBUG_SVR}

interface

uses
{$ifdef DEBUG_SVR}
  Crt,
{$endif}
  NumToStr,
  CoEth01,
  CoUdp,
  CoInet,

  uGenIntf,
  HwSyst,
  Tick,

  Objects,
  Strings,
  CoBase,
  uEcpDef,
  uEcpGen{,
  uExit};

type

  TECPMessage = packed
  record
    Header : TECPHeader;
    Data   : TECPGenData;
  end;

  { ECP Server }

  TECPServer = object( TObject )
  private
    Sock         : PCoDevice;

    State        : Word;

    RecvBuff     : TECPMessage;
    RecvLen      : Word;
    RecvFrom     : TCoAddress;

    SendBuff     : TECPMessage;
    SendLen      : Word;
    SendReply    : Boolean;

    Overlapped   : TOverlapped;

    function  OpenSock: Boolean;
    procedure CloseSock;

    procedure SendACK;
    procedure SendACKData( AData: Pointer; ADataSize: Word );
    procedure SendACKDataEx( AHeader: Pointer; AHeaderSize: Word;
                AData: Pointer; ADataSize: Word );
    procedure SendNACK( AErrCode: Word; const AErrText: String );

    function  HandleMessage: Boolean;
    function  HandleProcessMessage: Boolean;

  public

    StopRequest  : Boolean;

    constructor Init;
    destructor  Done; virtual;

    function    Tick: Boolean;
  end;


implementation
uses LocGLB, uGenProc, Reports;

const

  ST_START_RECEIVE    = 1;
  ST_WAIT_RECEIVE     = 2;

  ST_PRE_HANDLE_MSG   = 3;
  ST_HANDLE_MSG       = 4;


  ST_SEND             = 5;
  ST_WAIT_SEND        = 6;

  ST_BIND             = 7;
  ST_WAIT_BIND        = 8;
  ST_BOUND            = 9;

  ST_UNBIND           = 10;
  ST_WAIT_UNBIND      = 11;
  ST_UNBOUND          = 12;

  ST_ERROR            = 13;
  ST_FINISH           = 14;


procedure Log( const S: String );
begin
  WriteLn( S );
end;


{-------------------------------
{ .Init
{---
{
{---
}

constructor TECPServer.Init;
begin
  inherited Init;

  State := ST_BIND;

  Sock := nil;

  if not OpenSock then Fail;
end;

destructor TECPServer.Done;
begin
  CloseSock;

  inherited Done;
end;


function TECPServer.OpenSock: Boolean;
var
  UdpMux : PCoDevice;
  Status : TCoStatus;
begin

  Status := NetOpenSocket( 'UDP',
              'LPORT=' + IntegerStrDec( Glb.ETHSettings.Port,0),
              '', Sock );

  {$ifdef DEBUG_SVR}
  if Status <> CST_SUCCESS then
  begin
    Log( 'NetOpenSocket() failed: ' + CoStatusToStr( Status ) );
  end;
  {$endif}


  OpenSock := Status = CST_SUCCESS;
end;

procedure TECPServer.CloseSock;
begin
  NetCloseSocket( Sock );
end;


{-------------------------------
{ .Tick
{---
{
{---
}

function TECPServer.Tick: Boolean;
label
  L_ST_HANDLE_MSG,
  L_ST_PRE_HANDLE_MSG,
  L_ST_SEND,
  L_ST_BOUND,
  L_ST_UNBOUND;
var
  Status : TCoStatus;
begin

  Tick := True;

  case State of

  {------------------------}
    ST_START_RECEIVE:
    begin

      if not StopRequest then
      begin

        Status := Sock^.CoRecvBufferFrom( @RecvBuff, SizeOf( RecvBuff ),
                          @RecvLen, 0, @RecvFrom, @Overlapped );
        case Status of

          CST_SUCCESS:
          begin
            State := ST_PRE_HANDLE_MSG;
            goto L_ST_PRE_HANDLE_MSG;
          end;

          CST_PENDING:
          begin
            State := ST_WAIT_RECEIVE;
          end;

        else

          { Nejaka chyba pri prijmu }
          { Zustaneme ve stejnem stavu, pokusime se znovu o prijem }

        end;

      end
      else
        State := ST_UNBIND;

    end;

  {------------------------}
    ST_WAIT_RECEIVE:
    begin

      if not StopRequest then
      begin
        Status := Sock^.CoGetIOResult( @Overlapped, 0 );

        case Status of

          CST_SUCCESS:
          begin
            State := ST_PRE_HANDLE_MSG;
            goto L_ST_PRE_HANDLE_MSG;
          end;

          CST_PENDING:
          begin
            { Zustaneme ve stavu ST_WAIT_MESSAGE }
          end;

        else
          { Nejaka chyba pri prijmu }
          { Pokusime se znovu o prijem }
          State := ST_START_RECEIVE;
        end;

      end
      else begin
        Sock^.CoCancelIO( @Overlapped, 0 );
        State := ST_UNBIND;
      end;

    end;

  {------------------------}
    ST_PRE_HANDLE_MSG:
  L_ST_PRE_HANDLE_MSG:
    begin
      SendReply := True;
      State := ST_HANDLE_MSG;
      goto L_ST_HANDLE_MSG;
    end;

  {------------------------}
    ST_HANDLE_MSG:
  L_ST_HANDLE_MSG:
    begin

      if HandleMessage then
      begin

        if SendReply then
        begin
          State := ST_SEND;
          goto L_ST_SEND;
        end
        else begin
          State := ST_START_RECEIVE;
        end;

      end;
    end;


  {------------------------}
    ST_SEND:
  L_ST_SEND:
    begin
      Status := Sock^.CoSendBufferTo( @SendBuff, SendLen, 0,
        @RecvFrom, @Overlapped );

      if Status = CST_PENDING then
      begin
        State := ST_WAIT_SEND;
      end
      else
        State := ST_START_RECEIVE;
    end;


  {------------------------}
    ST_WAIT_SEND:
    begin
      Status := Sock^.CoGetIOResult( @Overlapped, 0 );

      if Status <> CST_PENDING then
      begin
        State := ST_START_RECEIVE;
      end;

    end;

  {------------------------}
    ST_BIND:
    begin
      Status := Sock^.CoBind( nil, COF_PROPAGATE, @Overlapped );
      case Status of

        CST_SUCCESS:
        begin
          State := ST_BOUND;
          goto L_ST_BOUND;
        end;

        CST_PENDING:
        begin
          State := ST_WAIT_BIND;
        end;

      else
        { Chyba }

        {$ifdef DEBUG_SVR}
        Log( 'CoBind() failed: ' + Sock^.CoStatusToStr( Status ) );
        {$endif}

        State := ST_ERROR;
      end;
    end;

  {------------------------}
    ST_WAIT_BIND:
    begin
      Status := Sock^.CoGetIOResult( @Overlapped, 0 );

      case Status of

        CST_SUCCESS:
        begin
          State := ST_BOUND;
          goto L_ST_BOUND;
        end;

        CST_PENDING: { nic };

      else
        { Chyba }

        {$ifdef DEBUG_SVR}
        Log( 'CoUnbind() failed: ' + Sock^.CoStatusToStr( Status ) );
        {$endif}

        State := ST_ERROR;
      end;
    end;

  {------------------------}
    ST_BOUND:
  L_ST_BOUND:
    begin
      State := ST_START_RECEIVE;
    end;


  {------------------------}

    ST_UNBIND:
    begin
      Status := Sock^.CoUnbind( COF_PROPAGATE, @Overlapped );
      case Status of

        CST_SUCCESS:
        begin
          State := ST_UNBOUND;
          goto L_ST_UNBOUND;
        end;

        CST_PENDING:
        begin
          State := ST_WAIT_UNBIND;
        end;

      else
        { Chyba }

        {$ifdef DEBUG_SVR}
        Log( 'CoUnbind() failed: ' + Sock^.CoStatusToStr( Status ) );
        {$endif}

        State := ST_ERROR;
      end;
    end;

  {------------------------}
    ST_WAIT_UNBIND:
    begin
      Status := Sock^.CoGetIOResult( @Overlapped, 0 );

      case Status of

        CST_SUCCESS:
        begin
          State := ST_UNBOUND;
          goto L_ST_UNBOUND;
        end;

        CST_PENDING: { nic };

      else
        { Chyba }

        {$ifdef DEBUG_SVR}
        Log( 'CoUnbind() failed: ' + Sock^.CoStatusToStr( Status ) );
        {$endif}

        State := ST_ERROR;
      end;
    end;

  {------------------------}
    ST_UNBOUND:
  L_ST_UNBOUND:
    begin
      State := ST_FINISH;
    end;

  {------------------------}
    ST_ERROR:
    begin
      RunError( 255 );
      Tick := False;
    end;

  {------------------------}
    ST_FINISH:
    begin
      Tick := False;
    end;

  end;

end;

{-------------------------------
{ .SendACK
{---
{
{---
}

procedure TECPServer.SendACK;
begin
  SendLen := SizeOf( TECPHeader );

  with SendBuff.Header do
  begin
    Flg := ECP_FLAG_ACK;
    Seq := RecvBuff.Header.Seq;
    Cmd := RecvBuff.Header.Cmd;
    Len := SizeOf( TECPHeader );
  end;

  SendReply := True;
end;

{-------------------------------
{ .SendNACK
{---
{
{---
}

procedure TECPServer.SendNACK( AErrCode: Word; const AErrText: String );
var
  MsgLen  : Word;
  TextLen : Byte;
begin
  TextLen := Length( AErrText ) + 1;
  if TextLen > MAX_ECP_ERROR_LENGTH then TextLen := MAX_ECP_ERROR_LENGTH;

  MsgLen := SizeOf( TECPHeader ) + SizeOf( Word ) + TextLen;

  with SendBuff.Header do
  begin
    Flg := ECP_FLAG_ACK + ECP_FLAG_ERR;
    Seq := RecvBuff.Header.Seq;
    Cmd := RecvBuff.Header.Cmd;
    Len := MsgLen;
  end;

  SendBuff.Data.ERROR_RS.ErrCode := AErrCode;


  MemCopy( @SendBuff.Data.ERROR_RS.ErrText, @AErrText[1], TextLen - 1 );
  SendBuff.Data.ERROR_RS.ErrText[ TextLen - 1 ] := #0;

  SendLen := MsgLen;
  SendReply := True;
end;

{-------------------------------
{ .SendACKData
{---
{
{---
}

procedure TECPServer.SendACKData( AData: Pointer; ADataSize: Word );
var
  MsgLen : Word;

begin
  MsgLen := SizeOf( TECPHeader ) + ADataSize;

  if MsgLen <= SizeOf( SendBuff ) then
  begin

    with SendBuff.Header do
    begin
      Flg := ECP_FLAG_ACK;
      Seq := RecvBuff.Header.Seq;
      Cmd := RecvBuff.Header.Cmd;
      Len := MsgLen;
    end;

    { Pokud jsou AData nil, predpokladam ze uz byly nakopirovany
      do SendBuff.Data }

    if AData <> nil then MemCopy( @SendBuff.Data, AData, ADataSize );

    SendLen   := MsgLen;
    SendReply := True;
  end
  else
    RunError( 255 );
end;


procedure TECPServer.SendACKDataEx( AHeader: Pointer; AHeaderSize: Word;
  AData: Pointer; ADataSize: Word );
var
  MsgLen : Word;

begin
  MsgLen := SizeOf( TECPHeader ) + AHeaderSize + ADataSize;

  if MsgLen <= SizeOf( SendBuff ) then
  begin

    with SendBuff.Header do
    begin
      Flg := ECP_FLAG_ACK;
      Seq := RecvBuff.Header.Seq;
      Cmd := RecvBuff.Header.Cmd;
      Len := MsgLen;
    end;

    { Pokud jsou AData nil, predpokladam ze uz byly nakopirovany
      do SendBuff.Data }

    if AHeader <> nil then MemCopy (@SendBuff.Data, AHeader, AHeaderSize );

    if AData <> nil then
    begin
      MemCopy( @PByteArray(@SendBuff.Data)^[AHeaderSize], AData, ADataSize );
    end;

    SendLen   := MsgLen;
    SendReply := True;
  end
  else
    RunError( 255 );
end;


{-------------------------------
{ .HandleMessage
{---
{
{---
}

function TECPServer.HandleMessage;
begin

  HandleMessage := True;

  case RecvBuff.Header.Cmd and ECP_PROC_MASK of

    ECP_PROC_GENERATOR:
    begin
      HandleMessage := HandleProcessMessage;
    end;

  else
    {$ifdef DEBUG_SVR}
    Log( 'err: INVALID_PROC' );
    {$endif}
    { Neplatne cislo procesu }
    SendNACK( ECP_ERR_INVALID_PROC, '' );
  end;
end;

{-------------------------------
{ .HandleProcessMessage
{---
{
{---
}

function TECPServer.HandleProcessMessage: Boolean;
const
  ProcVerData  : TECP_PROC_VER_RS = ( Version: $00000001 );
  ProcIdData   : TECP_PROC_ID_RS  = ( Ident: 'SIGNAL GENERATOR' );
  StateData    : TECPGEN_STATE_RS = ();
  ReadDescData : TECPGEN_READ_DESC_RS = ();
  AddrData     : TECPGEN_GET_ADDR_RS = ();
  OptionsData  : TECPGEN_READ_OPT_RS = ();

var

  Data     : PEcpGenData;
  DataLen  : Word;
  StatePtr : PBlockStatus;
  PData    : Pointer;
  W        : Word;

{----}
  Index     : Integer;
  Result    : TResult;
{----}

 procedure SendResponse (Result : TResult);
 begin
   {$ifdef DEBUG_SVR}
   Log( 'SendResult :'+ResultStr (Result));
   {$endif}
   case Result of
        Res_Ok           : SendACK;
        Res_OutOfRng     : SendNACK ( ECPGEN_ERR_IndexOutOfRange, 'INDEX OUT OF RANGE');
        Res_Locked       : SendNACK ( ECPGEN_ERR_BlockIsLocked, 'BLOCK LOCKED');
        Res_NotLocked    : SendNACK ( ECPGEN_ERR_BlockIsLocked, 'BLOCK ISN''T LOCKED');
        Res_NoMem        : SendNACK ( ECPGEN_ERR_NoMemory,'NOT ENOUGH MEMORY');
        Res_NothingToSend: SendNACK ( ECPGEN_ERR_NoMemory,'NOTHING TO SEND');
        Res_BadDataSize  : SendNACK ( ECPGEN_ERR_NoMemory,'INCORRECT DATA SIZE');
        Res_Failed       : SendNACK ( ECPGEN_ERR_GeneralError,'GENERAL ERROR');
        Res_Invalid      : SendNACK ( ECPGEN_ERR_Invalid,'BLOCK IS INVALID');
        Res_UnknBlock    : SendNACK ( ECPGEN_ERR_Invalid,'UNKNOWN BLOCK TYPE');
        Res_NotSupported : SendNACK ( ECPGEN_ERR_NotSupported,'NOT SUPPORTED BY THIS BLOCK TYPE');
        Res_TimeOutOfRng : SendNACK ( ECPGEN_ERR_TimeOutOfRng,'TIME DIVIDER ISN''T BETWEEN (100us,55ms)');
        else
          SendNACK ( ECPGEN_ERR_Unknown, 'UNKNOWN ERROR');
       end;
 end;

begin

  HandleProcessMessage := True;

  Data    := @RecvBuff.Data;
  DataLen := RecvBuff.Header.Len - SizeOf( TECPHeader );

  { Zpracoani prikazu procesu Test }

  case RecvBuff.Header.Cmd and ECP_CMD_MASK of

  {------------------------------------------------}
    ECP_CMD_PROC_ID  : { Identifikace procesu      }
    begin
      {$ifdef DEBUG_SVR}
      Log( 'recv: ECP_CMD_PROC_ID' );
      {$endif}
      if DataLen = 0 then
      begin
        SendACKData( @ProcIdData, SizeOf( ProcIdData ) );
      end
      else begin
        {$ifdef DEBUG_SVR}
        Log( 'err: INVALID_LEN' );
        {$endif}
        SendNACK( ECP_ERR_INVALID_LEN, '' );
      end;
    end;

  {------------------------------------------------}
    ECP_CMD_PROC_VER : { Verze procesu             }
    begin
      {$ifdef DEBUG_SVR}
      Log( 'recv: ECP_CMD_PROC_VER' );
      {$endif}

      if DataLen = 0 then
      begin
        SendACKData( @ProcVerData, SizeOf( ProcVerData ) );
      end
      else begin
        {$ifdef DEBUG_SVR}
        Log( 'err: INVALID_LEN' );
        {$endif}
        SendNACK( ECP_ERR_INVALID_LEN, '' );
      end;
    end;

  {------------------------------------------------}

    ECPGEN_CMD_STATE: { Cteni stavu }
    begin
      {$ifdef DEBUG_SVR}
      Log( 'recv: STATE' );
      {$endif}

      if DataLen = 0 then
      begin
        for Index := 0 to cMaxDigGen-1 do
         with StateData.DigGen[Index] do
          begin
            Status := Glb.DigGenerators[Index].State ;
            MiscByte := 0;
            MiscWord := Glb.DigGenerators[Index].ActualStep;
          end;
        for Index := 0 to cMaxDigMon-1 do
         with StateData.DigMon[Index] do
          begin
            Status := Glb.DigMonitors[Index].State ;
            MiscByte := 0;
            MiscWord := Glb.DigMonitors[Index].DataSize div SizeOf(TDigMonRecord);
            if Glb.DigMonitors[Index].DataSize >=
               Glb.DigMonitors[Index].MaxOffset then
              Dec (MiscWord);
          end;
        for Index := 0 to cMaxAnaMon-1 do
         with StateData.AnaMon[Index] do
          begin
            Status := BST_INVALID;{AnaMonitors[Index].State ;{}
            MiscByte := 0;
            MiscWord := 0;
          end;
        for Index := 0 to cMaxCounter-1 do
         with StateData.Counter[Index] do
          begin
            Status := Glb.Counters[Index].State ;
            MiscByte := 0;
            MiscWord := Glb.Counters[Index].Counted;
          end;

        TimerSum := 0;
        for Index := 0 to cMaxTimerArrIdx-1 do
          TimerSum := TimerSum + TimerArr[Index];

        if Glb.TickTime < 0.010 then
          StateData.CpuLoad := 100 - LongDiv (
                   LongMul (100, LongDiv (TimerSum, cMaxTimerArrIdx)),
                   (Trunc (ITimerClk * {cTickTimer * TimerPresc}
                          Glb.TickTime)-1))
          else
            StateData.CpuLoad := 2;

        SendACKData( @StateData, SizeOf( StateData ) );
      end
      else begin
         {$ifdef DEBUG_SVR}
         Log( 'err: INVALID_LEN' );
         {$endif}
        SendNACK( ECP_ERR_INVALID_LEN, '' );
      end;
    end;


  {------------------------------------------------}

    ECPGEN_CMD_RESETKIT:
    begin
      {$ifdef DEBUG_SVR}
      Log( 'recv: RESET' );
      {$endif}

      if DataLen = 0 then
      begin

        SendACK;

        FlEnd := True;

      end
      else begin
        {$ifdef DEBUG_SVR}
        Log( 'err: INVALID_LEN' );
        {$endif}
        SendNACK( ECP_ERR_INVALID_LEN, '' );
      end;

    end;

  {------------------------------------------------}

    ECPGEN_CMD_START_ALL:
    begin
      {$ifdef DEBUG_SVR}
      Log( 'recv: START_ALL' );
      {$endif}

      if DataLen = 0 then
      begin

        StartAll(True);

        SendACK;

      end
      else begin
        {$ifdef DEBUG_SVR}
        Log( 'err: INVALID_LEN' );
        {$endif}
        SendNACK( ECP_ERR_INVALID_LEN, '' );
      end;

    end;
  {------------------------------------------------}

    ECPGEN_CMD_STOP_ALL:
    begin
      {$ifdef DEBUG_SVR}
      Log( 'recv: STOP_ALL' );
      {$endif}

      if DataLen = 0 then
      begin

        {!@# !ZAKAZAT PRERUSENI, doplnit analogy}
        StopAll;

        SendACK;

      end
      else begin
        {$ifdef DEBUG_SVR}
        Log( 'err: INVALID_LEN' );
        {$endif}
        SendNACK( ECP_ERR_INVALID_LEN, '' );
      end;

    end;

  {------------------------------------------------}

    ECPGEN_CMD_DELETE_ALL:
    begin
      {$ifdef DEBUG_SVR}
      Log( 'recv: DELETEALL' );
      {$endif}

      if DataLen = 0 then
      begin
        for Index := 0 to cMaxDigGen-1 do
          DeleteDigGenerator (Index);

        for Index := 0 to cMaxDigMon-1 do
          DeleteDigMonitor (Index);

        for Index := 0 to cMaxCounter-1 do
          DeleteCounter (Index);

        SendResponse (Res_Ok);

      end
      else begin
        {$ifdef DEBUG_SVR}
        Log( 'err: INVALID_LEN' );
        {$endif}
        SendNACK( ECP_ERR_INVALID_LEN, '' );
      end;

    end;

  {------------------------------------------------}

    ECPGEN_CMD_START:
    begin
      {$ifdef DEBUG_SVR}
      Log( 'recv: START' );
      {$endif}

      if DataLen = SizeOf( TECPGEN_START_RQ ) then
      begin
        case Data^.START_RQ.Id.BlockType of

          BT_DIGGEN: Result := StartDigGenerator(Data^.START_RQ.Id.Index,False);

          BT_DIGMON: Result := StartDigMonitor(Data^.START_RQ.Id.Index,False);

          BT_COUNTER: Result := StartCounter(Data^.START_RQ.Id.Index,False);

        else
          Result := Res_UnknBlock;
        end;

        SendResponse (Result);

      end
      else begin
        {$ifdef DEBUG_SVR}
        Log( 'err: INVALID_LEN' );
        {$endif}
        SendNACK( ECP_ERR_INVALID_LEN, '' );
      end;

    end;

  {------------------------------------------------}

    ECPGEN_CMD_STOP:
    begin
      {$ifdef DEBUG_SVR}
      Log( 'recv: STOP' );
      {$endif}

      if DataLen = SizeOf( TECPGEN_STOP_RQ ) then
      begin

        case Data^.STOP_RQ.Id.BlockType of

          BT_DIGGEN: Result := StopDigGenerator (Data^.STOP_RQ.Id.Index);

          BT_DIGMON: Result := StopDigMonitor (Data^.STOP_RQ.Id.Index);

          BT_COUNTER: Result := StopCounter (Data^.STOP_RQ.Id.Index);

        else
          Result := Res_UnknBlock;
        end;

        SendResponse (Result);

      end
      else begin
        {$ifdef DEBUG_SVR}
        Log( 'err: INVALID_LEN' );
        {$endif}
        SendNACK( ECP_ERR_INVALID_LEN, '' );
      end;

    end;

  {------------------------------------------------}

    ECPGEN_CMD_DELETE:
    begin
      {$ifdef DEBUG_SVR}
      Log( 'recv: DELETE' );
      {$endif}

      if DataLen = SizeOf( TECPGEN_DELETE_RQ ) then
      begin

        case Data^.DELETE_RQ.Id.BlockType of

          BT_DIGGEN: Result := DeleteDigGenerator (Data^.DELETE_RQ.Id.Index);

          BT_DIGMON: Result := DeleteDigMonitor (Data^.DELETE_RQ.Id.Index);

{         BT_ANAGEN:
          BT_ANAMON:
}
          BT_COUNTER: Result := DeleteCounter (Data^.DELETE_RQ.Id.Index);

        else
          Result := Res_UnknBlock;
        end;

        SendResponse (Result);

      end
      else begin
        {$ifdef DEBUG_SVR}
        Log( 'err: INVALID_LEN' );
        {$endif}
        SendNACK( ECP_ERR_INVALID_LEN, '' );
      end;


    end;

  {------------------------------------------------}

    ECPGEN_CMD_RESET:
    begin
      {$ifdef DEBUG_SVR}
      Log( 'recv: RESET' );
      {$endif}

      if DataLen = SizeOf( TECPGEN_RESET_RQ ) then
      begin
        with Data^.STOP_RQ.Id do
        case BlockType of

          BT_DIGGEN:
              begin
                Result := StopDigGenerator (Index);
                if Result = Res_Ok then
                  Result := StartDigGenerator (Index,True);
              end;

          BT_DIGMON:
              begin
                Result := ResetDigMonitor (Index,True);
              end;

          BT_COUNTER:
              begin
                Result := ResetCounter (Index,True);
              end;

        else
          Result := Res_UnknBlock;
        end;

        SendResponse (Result);

      end
      else begin
        {$ifdef DEBUG_SVR}
        Log( 'err: INVALID_LEN' );
        {$endif}
        SendNACK( ECP_ERR_INVALID_LEN, '' );
      end;


    end;


  {------------------------------------------------}

    ECPGEN_CMD_WRITE_DESC:
    begin
     {$ifdef DEBUG_SVR}
     Log( 'recv: WRITE_DESC' );
     {$endif}
     if DataLen = SizeOf( TECPGEN_WRITE_DESC_RQ ) then
     begin
       Index := Data^.WRITE_DESC_RQ.Id.Index;

       case Data^.WRITE_DESC_RQ.Id.BlockType of

         BT_DIGGEN:
            Result := InitDigGenerator(Index,
                                       Data^.WRITE_DESC_RQ.Descr.DigGen);

         BT_DIGMON:
            Result := InitDigMonitor(Index,
                                     Data^.WRITE_DESC_RQ.Descr.DigMon
                                     );

         BT_COUNTER:
            Result := InitCounter (Index,
                                   Data^.WRITE_DESC_RQ.Descr.Counter
                                   );

       else
         Result := Res_UnknBlock;
       end;

       SendResponse (Result);

     end
     else begin
       {$ifdef DEBUG_SVR}
       Log( 'err: INVALID_LEN' );
       {$endif}
       SendNACK( ECP_ERR_INVALID_LEN, '' );
     end;
    end;

  {------------------------------------------------}

    ECPGEN_CMD_READ_DESC:
    begin
      {$ifdef DEBUG_SVR}
      Log( 'recv: READ_DESC' );
      {$endif}
      if DataLen = SizeOf( TECPGEN_READ_DESC_RQ ) then
      begin
        Index := Data^.READ_DESC_RQ.Id.Index;

        ReadDescData.Id := Data^.READ_DESC_RQ.Id;

        Result := Res_OutOfRng;

        case Data^.READ_DESC_RQ.Id.BlockType of

          BT_DIGGEN:
           if Index < cMaxDigGen then
            begin
              ReadDescData.Descr.DigGen := Glb.DigGenerators[ Index ].Descriptor;
              ReadDescData.DataSize := Glb.DigGenerators[ Index ].DataSize;
              ReadDescData.AbsTime := Glb.DigGenerators[ Index ].Time;
              Result := Res_Ok;
            end;

          BT_DIGMON:
           if Index < cMaxDigMon then
            begin
              ReadDescData.Descr.DigMon := Glb.DigMonitors[ Index ].Descriptor;
              ReadDescData.DataSize := Glb.DigMonitors[Index].DataSize;
              if Glb.DigMonitors[Index].DataSize >=
                 Glb.DigMonitors[Index].MaxOffset then
                Dec (ReadDescData.DataSize, SizeOf(TDigMonRecord));
              ReadDescData.AbsTime := 0;
              Result := Res_Ok;
            end;

          BT_COUNTER:
           if Index < cMaxDigGen then
            begin
              ReadDescData.Descr.Counter := Glb.Counters[ Index ].Descriptor;
              ReadDescData.DataSize := 0;
              ReadDescData.AbsTime := 0;
              Result := Res_Ok;
            end;

        else
          Result := Res_UnknBlock;
        end;

        if Result = Res_Ok then
          SendACKData( @ReadDescData, SizeOf( ReadDescData ) )
          else SendResponse (Result);

      end
      else begin
        {$ifdef DEBUG_SVR}
        Log( 'err: INVALID_LEN' );
        {$endif}
        SendNACK( ECP_ERR_INVALID_LEN, '' );
      end;
    end;

  {------------------------------------------------}

    ECPGEN_CMD_WRITE_DATA:
    begin
      {$ifdef DEBUG_SVR}
      Log( 'recv: WRITE_DATA' );
      {$endif}

      if DataLen = SizeOf( TECPGEN_WRITE_DATA_RQ ) then
      begin
        { !@# }

        Index := Data^.WRITE_DATA_RQ.Ctrl.Id.Index;

        Result := Res_OutOfRng;

        case Data^.WRITE_DATA_RQ.Ctrl.Id.BlockType of

          BT_DIGGEN:
           if Index < cMaxDigGen then
            begin
              Result := WriteDigGenerator (Index,
                                           @Data^.WRITE_DATA_RQ.Data,
                                           Data^.WRITE_DATA_RQ.Ctrl.Offset,
                                           Data^.WRITE_DATA_RQ.Ctrl.Size);
            end;

          BT_DIGMON,
          BT_COUNTER:
              Result := Res_NotSupported;

        else
          Result := Res_UnknBlock;
        end;

        SendResponse (Result);

      end
      else begin
        {$ifdef DEBUG_SVR}
        Log( 'err: INVALID_LEN' );
        {$endif}
        SendNACK( ECP_ERR_INVALID_LEN, '' );
      end;

    end;

  {------------------------------------------------}

    ECPGEN_CMD_READ_DATA:
    begin
      {$ifdef DEBUG_SVR}
      Log( 'recv: READ_DATA');
      {$endif}

      if DataLen = SizeOf( TECPGEN_READ_DATA_RQ ) then
       with Data^.READ_DATA_RQ.Ctrl do
        begin
         W := Size;
         case Id.BlockType of

           BT_DIGGEN:  Result := ReadDigGenerator (Id.Index,Offset,W,PData);

           BT_DIGMON:  Result := ReadDigMonitor (Id.Index,Offset,Size,PData);

           BT_COUNTER: Result := Res_NotSupported;

         else
           Result := Res_UnknBlock;
         end;
         if Result <> Res_Ok then
           SendResponse ( Result )
           else
            begin
              {$ifdef DEBUG_SVR}
              Writeln ('READ_DATA with RqSize:',W,' GetSize:',Size);
              {$endif}
              SendACKDataEx( @Data^.READ_DATA_RQ,
                             SizeOf(TECPGEN_READ_DATA_RQ),
                             PData, Size);
            end;
        end
      else begin
        {$ifdef DEBUG_SVR}
        Log( 'err: INVALID_LEN' );
        {$endif}
        SendNACK( ECP_ERR_INVALID_LEN, '' );
      end;
    end;

  {------------------------------------------------}

    ECPGEN_CMD_LOCK:
    begin
      {$ifdef DEBUG_SVR}
      Log( 'recv: LOCK' );
      {$endif}

      if DataLen = SizeOf( TECPGEN_LOCK_RQ ) then
      begin

        case Data^.LOCK_RQ.Id.BlockType of

          BT_DIGGEN:  Result := LockDigGenerator (Data^.LOCK_RQ.Id.Index);

          BT_DIGMON:  Result := LockDigMonitor (Data^.LOCK_RQ.Id.Index);

          BT_COUNTER: Result := LockCounter (Data^.LOCK_RQ.Id.Index);

        else
          Result := Res_UnknBlock;
        end;
        SendResponse ( Result );
      end
      else begin
        {$ifdef DEBUG_SVR}
        Log( 'err: INVALID_LEN' );
        {$endif}
        SendNACK( ECP_ERR_INVALID_LEN, '' );
      end;


    end;

  {------------------------------------------------}

    ECPGEN_CMD_UNLOCK:
    begin
      {$ifdef DEBUG_SVR}
      Log( 'recv: UNLOCK' );
      {$endif}

      if DataLen = SizeOf( TECPGEN_UNLOCK_RQ ) then
      begin

        case Data^.UNLOCK_RQ.Id.BlockType of

          BT_DIGGEN:  Result := UnLockDigGenerator (Data^.LOCK_RQ.Id.Index);

          BT_DIGMON:  Result := UnLockDigMonitor (Data^.LOCK_RQ.Id.Index);

          BT_COUNTER: Result := UnLockCounter (Data^.LOCK_RQ.Id.Index);

        else
          Result := Res_UnknBlock;
        end;
        SendResponse ( Result );
      end

      else begin
        {$ifdef DEBUG_SVR}
        Log( 'err: INVALID_LEN' );
        {$endif}
        SendNACK( ECP_ERR_INVALID_LEN, '' );
      end;


    end;

  {------------------------------------------------}

    ECPGEN_CMD_GET_ADDR:
    begin
      {$ifdef DEBUG_SVR}
      Log( 'recv: GET_ADDR' );
      {$endif}

      if DataLen = 0 then
      begin

        with Glb.ETHSettings do
          begin
            AddrData.IPAddr := IPAddress;
            AddrData.NetMask := NetMask;
            AddrData.Port := Port;
          end;

        SendACKData ( @AddrData, SizeOf (AddrData));

      end
      else begin
        {$ifdef DEBUG_SVR}
        Log( 'err: INVALID_LEN' );
        {$endif}
        SendNACK( ECP_ERR_INVALID_LEN, '' );
      end;

    end;

  {------------------------------------------------}

    ECPGEN_CMD_SET_ADDR:
    begin
      {$ifdef DEBUG_SVR}
      Log( 'recv: SET_ADDR' );
      {$endif}

      if DataLen = SizeOf( TECPGEN_SET_ADDR_RQ ) then
      begin
        with Glb.ETHSettings do
          begin
            IPAddress := Data^.SET_ADDR_RQ.IPAddr;
            NetMask := Data^.SET_ADDR_RQ.NetMask;
            Port := Data^.SET_ADDR_RQ.Port;
          end;
        SendACK;
      end
      else begin
        {$ifdef DEBUG_SVR}
        Log( 'err: INVALID_LEN' );
        {$endif}
        SendNACK( ECP_ERR_INVALID_LEN, '' );
      end;

    end;

  {------------------------------------------------}

    ECPGEN_CMD_WRITE_OPT:
    begin
      {$ifdef DEBUG_SVR}
      Log( 'recv: WRITE_OPT' );
      {$endif}

      if DataLen = SizeOf( TECPGEN_WRITE_OPT_RQ ) then
      with Data^.WRITE_OPT_RQ do
      begin
        if (Options.TickTime < 1e-4) or
           (Options.TickTime > 55e-3) then
           SendResponse (res_TimeOutOfRng)
          else
          begin
            Glb.TickTime := Options.TickTime;
            SetTimeUser1(Glb.TickTime);
            SendACK;
          end;

      end
      else begin
        {$ifdef DEBUG_SVR}
        Log( 'err: INVALID_LEN' );
        {$endif}
        SendNACK( ECP_ERR_INVALID_LEN, '' );
      end;

    end;

  {------------------------------------------------}

    ECPGEN_CMD_READ_OPT:
    begin
      {$ifdef DEBUG_SVR}
      Log( 'recv: READ_OPT' );
      {$endif}

      if DataLen = 0 then
      begin
        OptionsData.Options.TickTime := Glb.TickTime;

        SendACKData( @OptionsData, SizeOf( OptionsData ) )
      end
      else begin
        {$ifdef DEBUG_SVR}
        Log( 'err: INVALID_LEN' );
        {$endif}
        SendNACK( ECP_ERR_INVALID_LEN, '' );
      end;

    end;

  {------------------------------------------------}

  else
    { Neplatne cislo prikazu }
    {$ifdef DEBUG_SVR}
    Log( 'err: INVALID_CMD' );
    {$endif}
    SendNACK( ECP_ERR_INVALID_CMD, '' );
  end;

end;


end.