%%SAMPLE=SAMPLE
 {
 ***********************************************************************
 %SAMPLE% Modbus Driver.
 ***********************************************************************
 Next text uses by @Help command. Do not remove it.
 ***********************************************************************
[@Help]
|StdIn Command list: "@cmd=arg" or "@cmd arg"
|********************************************************
| @Modbus.XXX    - response come from &ModbusProxy server:
| @Modbus.Reply    ref cid tim port uid fid $$ans
| @Modbus.Refuse   ref cid tim port uid fid   msg
| @Modbus.Timeout  ref cid tim port uid fid $$req
|  ref  - sender device reference, expected &ModbusProxy.
|  cid  - command id, any user defined custom data.
|  tim  - request-response time, ms.
|  port - logical port on &ModbusProxy, expected 1..16.
|  uid  - unit id, i.e.MODBUS address, expected 1..247.
|  fid  - function id, expected 1,2,3,4,5,6,15,16.
|  ans  - answer  (response), in HEX_ENCODE format.
|  req  - request (original), in HEX_ENCODE format.
|  msg  - plain text message to explain reason of Refuse.
|********************************************************
[]
 Configuration:
 ***********************************************************************
  [DeviceList]
  &%SAMPLE%.DRIVER = device software program
  [&%SAMPLE%.DRIVER]
  Comment        = %SAMPLE% MODBUS DRIVER.
  InquiryPeriod  = 0
  DevicePolling  = 10, tpNormal
  ProgramSource  = ..\DaqPas\%SAMPLE%_Modbus_Driver.pas
  DigitalFifo    = 1024
  AnalogFifo     = 1024
  DebugFlags     = 3
  OpenConsole    = 2
  Simulator      = 0
  ModbusPort     = 1
  ModbusUnitId   = 1
  ModbusTimeout  = 250
  ModbusDeadline = 60000
  ModbusPolling  = 100
  DelayOnStart   = 1000
  tagPrefix      = %SAMPLE%
  ...etc...
 ***********************************************************************
 }
program %SAMPLE%_Modbus_Driver;    { %SAMPLE% Modbus Driver             }
const
 {------------------------------}{ Declare uses program constants:  }
 {$I _con_StdLibrary}            { Include all Standard constants,  }
 {------------------------------}{ And add User defined constants:  }
 {$I _con_NetLibrary}            { NetLibrary constants             }
 dfStatist       = 32;           { DebugFlags - polling statistics  }
 MaxCmdNum       = 32;           { Maximal number of commands:      }
 cm_ReadHR       = 1;            { - Read Holding Registers         }
 cm_ReadCS       = 2;            { - Read Coil Statuses             }
 cm_Faults       = 3;            { - Write Coils - Fault flags      }
 cm_Ackno1       = 4;            { - Write Coil  - Ackno 1          }
 cm_Ackno2       = 5;            { - Write Coil  - Ackno 2          }
 cm_Ackno3       = 6;            { - Write Coil  - Ackno 3          }
 cm_Ackno4       = 7;            { - Write Coil  - Ackno 4          }
 cm_Ackno5       = 8;            { - Write Coil  - Ackno 5          }
 cm_TestI1       = 9;            { - Write HReg  - Test I1 int16    }
 cm_TestI2       = 10;           { - Write HReg  - Test I2 int16    }
 cm_TestL1       = 11;           { - Write HReg  - Test L1 int32    }
 cm_TestL2       = 12;           { - Write HReg  - Test L2 int32    }
 cm_TestF1       = 13;           { - Write HReg  - Test F1 float    }
 cm_TestF2       = 14;           { - Write HReg  - Test F2 float    }
 cm_TestD1       = 15;           { - Write HReg  - Test D1 double   }
 cm_TestD2       = 16;           { - Write HReg  - Test D2 double   }
 do_POLLRATERX   = 70;           { DigitalOutput - Poll rate Rx     }
 do_POLLRATETX   = 71;           { DigitalOutput - Poll rate Tx     }
 do_POLLRATEEX   = 72;           { DigitalOutput - Poll rate Ex     }
 do_ERRORCOUNT   = 73;           { DigitalOutput - Error counter    }

type
 TTagRef    = record tag,nai,ndi,nao,ndo:Integer; val,tim:Real; end;
 
var
 {------------------------------}{ Declare uses program variables:  }
 {$I _var_StdLibrary}            { Include all Standard variables,  }
 {------------------------------}{ And add User defined variables:  }
 {$I _var_NetLibrary}            { NetLibrary variables             }
 %SAMPLE%     : record             { %SAMPLE% Driver data record        }
  Simulator : Boolean;           { Simulator or Driver mode         }
  Modbus    : record             { Modbus data record               }
   Port     : Integer;           { Logical Port on &ModbusProxy     }
   UnitId   : Integer;           { Modbus unit id                   }
   Timeout  : Integer;           { Modbus timeout, ms               }
   Polling  : Integer;           { Modbus polling period, ms        }
   Deadline : Integer;           { Modbus deadline time, ms         }
   DelayOnStart : Integer;       { Command cycle delay on start     }
   Cmd      : record             { Command cycle data record        }
    Num     : Integer;           { Current running command number   }
    Enabled : array [1..MaxCmdNum] of Boolean; { Enable polling     }
    FuncId  : array [1..MaxCmdNum] of Integer; { Modbus Function Id }
    SAddr   : array [1..MaxCmdNum] of Integer; { Start Address      }
    Quant   : array [1..MaxCmdNum] of Integer; { Quantity of data   }
    OpData  : array [1..MaxCmdNum] of Real;    { Operation data     }
    OpBuff  : array [1..MaxCmdNum] of Real;    { Operation buffer   }
   end;                          {                                  }
   Poll     : record             { Polling transaction data record  }
    ref     : Integer;           { Last sent device reference       }
    cid     : Integer;           { Last sent command id             }
    tim     : Real;              { Last polling time, ms            }
    port    : Integer;           { Last polling port                }
    uid     : Integer;           { Last polling unit id             }
    fid     : Integer;           { Last sent function id            }
    saddr   : Integer;           { Last sent start address          }
    quant   : Integer;           { Last sent quantity of registers  }
    dat     : String;            { Last sent PDU=(fid+dat) data     }
    Rate    : record             { Poll rate on last second         }
     Rx     : Real;              { Rx poll rate - receiver          }
     Tx     : Real;              { Tx poll rate - transmitter       }
     Ex     : Real;              { Ex poll rate - errors            }
    end;                         {                                  }
   end;                          {                                  }
  end;                           {                                  }
  Tester    : record             { Tester data                      }
   I1,I2,L1,L2,F1,F2,D1,D2,FAULT,ACKNO,HELP : TTagRef; {            }
  end;                           {                                  }
  Sim       : record             { Simulator data                   }
   Param    : array [0..31] of Real;    {                           }
   State    : array [0..31] of Integer; {                           }
   Ready    : Integer;           {                                  }
  end;                           {                                  }
 end;                            {                                  }

 {------------------------------}{ Declare procedures & functions:  }
 {$I _fun_StdLibrary}            { Include all Standard functions,  }
 {------------------------------}{ And add User defined functions:  }
 {$I _fun_NetLibrary}            { NetLibrary functions             }

 //
 // Command cycle routines.
 //
 function IsValidCmdNum(Num:Integer):Boolean;
 begin
  IsValidCmdNum:=(1<=Num) and (Num<=MaxCmdNum);
 end;
 function ValidateCmdNum(Num:Integer):Integer;
 begin
  if (1<=Num) and (Num<=MaxCmdNum)
  then ValidateCmdNum:=Num
  else ValidateCmdNum:=1;
 end;
 function IsUsableCmdNum(Num:Integer):Boolean;
 begin
  if (1<=Num) and (Num<=MaxCmdNum)
  then IsUsableCmdNum:=(%SAMPLE%.Modbus.Cmd.FuncId[Num]<>0)
  else IsUsableCmdNum:=False;
 end;
 function IsEnabledCmdNum(Num:Integer):Boolean;
 begin
  if (1<=Num) and (Num<=MaxCmdNum)
  then IsEnabledCmdNum:=(%SAMPLE%.Modbus.Cmd.FuncId[Num]<>0) and %SAMPLE%.Modbus.Cmd.Enabled[Num]
  else IsEnabledCmdNum:=False;
 end;
 function NextEnabledCmdNum(Num:Integer):Integer;
 var i:Integer;
 begin
  i:=0;
  while (i<MaxCmdNum) do begin
   Num:=ValidateCmdNum(Num+1);
   if IsEnabledCmdNum(Num)
   then i:=MaxCmdNum
   else i:=i+1;
  end;
  NextEnabledCmdNum:=Num;
 end;
 procedure EnableCmdNum(Num:Integer; Enabled:Boolean);
 begin
  if IsUsableCmdNum(Num) then %SAMPLE%.Modbus.Cmd.Enabled[Num]:=Enabled;
 end;
 procedure HoldCmdOpData(Num:Integer; OpData:Real);
 begin
  if IsUsableCmdNum(Num) then begin
   %SAMPLE%.Modbus.Cmd.Enabled[Num]:=not IsNaN(OpData);
   %SAMPLE%.Modbus.Cmd.OpBuff[Num]:=OpData;
  end;
 end;
 procedure ApplyCmdOpData(Num:Integer);
 begin
  if IsUsableCmdNum(Num) then
  if not IsNaN(%SAMPLE%.Modbus.Cmd.OpBuff[Num]) then begin
   %SAMPLE%.Modbus.Cmd.OpData[Num]:=%SAMPLE%.Modbus.Cmd.OpBuff[Num];
   %SAMPLE%.Modbus.Cmd.OpBuff[Num]:=_NaN;
  end;
 end;
 procedure ReleaseCmdOpData(Num:Integer);
 begin
  if IsUsableCmdNum(Num) then HoldCmdOpData(Num,%SAMPLE%.Modbus.Cmd.OpBuff[Num]);
 end;
 function GetCmdOpData(Num:Integer):Real;
 begin
  if IsUsableCmdNum(Num)
  then GetCmdOpData:=%SAMPLE%.Modbus.Cmd.OpData[Num]
  else GetCmdOpData:=_NaN;
 end;
 procedure InitCmdItem(Num:Integer; Enabled:Boolean; FuncId,SAddr,Quant:Integer; OpData,OpBuff:Real);
 begin
  if IsValidCmdNum(Num) then begin
   %SAMPLE%.Modbus.Cmd.Enabled[Num]:=Enabled;
   %SAMPLE%.Modbus.Cmd.FuncId[Num]:=FuncId;
   %SAMPLE%.Modbus.Cmd.SAddr[Num]:=SAddr;
   %SAMPLE%.Modbus.Cmd.Quant[Num]:=Quant;
   %SAMPLE%.Modbus.Cmd.OpData[Num]:=OpData;
   %SAMPLE%.Modbus.Cmd.OpBuff[Num]:=OpBuff;
  end;
 end;
 //
 // Command table cleanup and initialization.
 //
 procedure ClearCmdTable;
 var Num:Integer;
 begin
  %SAMPLE%.Modbus.Cmd.Num:=0;
  for Num:=1 to MaxCmdNum do InitCmdItem(Num,False,0,0,0,0,_NaN);
 end;
 procedure InitCmdTable;
 begin
  ClearCmdTable;
  // Table of CommandId  Enabled FunctionId        SAddr Quant OpData OpBuff
  InitCmdItem(cm_ReadHR, True,   modbus_fn_ReadHR, 0,    97,   0,     _NaN);
  InitCmdItem(cm_ReadCS, True,   modbus_fn_ReadCS, 0,    37,   0,     _NaN);
  InitCmdItem(cm_Faults, False,  modbus_fn_WritMC, 0,    32,   0,     _NaN);
  InitCmdItem(cm_Ackno1, False,  modbus_fn_WritSC, 32,   1,    0,     _NaN);
  InitCmdItem(cm_Ackno2, False,  modbus_fn_WritSC, 33,   1,    0,     _NaN);
  InitCmdItem(cm_Ackno3, False,  modbus_fn_WritSC, 34,   1,    0,     _NaN);
  InitCmdItem(cm_Ackno4, False,  modbus_fn_WritSC, 35,   1,    0,     _NaN);
  InitCmdItem(cm_Ackno5, False,  modbus_fn_WritSC, 36,   1,    0,     _NaN);
  InitCmdItem(cm_TestI1, False,  modbus_fn_WritSR, 100,  1,    0,     _NaN);
  InitCmdItem(cm_TestI2, False,  modbus_fn_WritSR, 101,  1,    0,     _NaN);
  InitCmdItem(cm_TestL1, False,  modbus_fn_WritMR, 102,  2,    0,     _NaN);
  InitCmdItem(cm_TestL2, False,  modbus_fn_WritMR, 104,  2,    0,     _NaN);
  InitCmdItem(cm_TestF1, False,  modbus_fn_WritMR, 106,  2,    0,     _NaN);
  InitCmdItem(cm_TestF2, False,  modbus_fn_WritMR, 108,  2,    0,     _NaN);
  InitCmdItem(cm_TestD1, False,  modbus_fn_WritMR, 110,  4,    0,     _NaN);
  InitCmdItem(cm_TestD2, False,  modbus_fn_WritMR, 114,  4,    0,     _NaN);
 end;
 //
 // Assign modbus last sent polling request record.
 //
 procedure AssignModbusPoll(ref,cid:Integer; tim:Real; port,uid,fid,saddr,quant:Integer; dat:String);
 begin
  %SAMPLE%.Modbus.Poll.ref:=ref;     %SAMPLE%.Modbus.Poll.cid:=cid;      %SAMPLE%.Modbus.Poll.tim:=tim;
  %SAMPLE%.Modbus.Poll.port:=port;   %SAMPLE%.Modbus.Poll.uid:=uid;      %SAMPLE%.Modbus.Poll.fid:=fid;
  %SAMPLE%.Modbus.Poll.saddr:=saddr; %SAMPLE%.Modbus.Poll.quant:=quant;  %SAMPLE%.Modbus.Poll.dat:=dat;
 end;
 //
 // Clear modbus polling request to be ready for next polling.
 //
 procedure ClearModbusPoll;
 begin
  %SAMPLE%.Modbus.Poll.cid:=0;
  %SAMPLE%.Modbus.Poll.dat:='';
 end;
 //
 // Clear modbus poll rate counters.
 //
 procedure ClearModbusRate;
 begin
  %SAMPLE%.Modbus.Poll.Rate.Rx:=0;
  %SAMPLE%.Modbus.Poll.Rate.Tx:=0;
  %SAMPLE%.Modbus.Poll.Rate.Ex:=0;
 end;
 //
 // Find the current Proxy device to send @Modbus.Poll message.
 //
 function devTheProxy:Integer;
 begin
  if %SAMPLE%.Simulator               // In simulation mode
  then devTheProxy:=devMySelf       // send messages to himself
  else devTheProxy:=devModbusProxy; // otherwise use &ModbusProxy
 end;
 //
 // Check if communication port is opened or not.
 // As long as we use Proxy device, check Port number & device reference.
 //
 function IsPortOpened:Boolean;
 begin
  IsPortOpened:=(%SAMPLE%.Modbus.Port>0) and (devTheProxy<>0);
 end;
 //
 // Calculate Modbus request PDU by command id.
 // cid   - (in)  command id number.
 // fid   - (out) Modbus function id.
 // saddr - (out) zero-based start address of registers to read/write.
 // quant - (out) data quantity, i.e. number of r/w registers or single register r/w value.
 // dat   - (out) Modbus PDU data unit, PDU=(fid+dat).
 //
 function %SAMPLE%_CalcPDU(cid:Integer; var fid,saddr,quant:Integer; var dat:String):Boolean;
 begin
  fid:=0; saddr:=0; quant:=0; dat:='';
  if IsEnabledCmdNum(cid) then begin
   fid:=%SAMPLE%.Modbus.Cmd.FuncId[cid];
   saddr:=%SAMPLE%.Modbus.Cmd.SAddr[cid];
   quant:=%SAMPLE%.Modbus.Cmd.Quant[cid];
   if (cid=cm_ReadHR) then begin
    dat:=modbus_encode_pdu('R',fid,saddr,quant,'');
   end else
   if (cid=cm_ReadCS) then begin
    dat:=modbus_encode_pdu('R',fid,saddr,quant,'');
   end else
   if (cid=cm_Faults) then begin
    dat:=modbus_encode_pdu('R',fid,saddr,quant,modbus_dump_int32(Round(GetCmdOpData(cid)),modbus_sw_native));
   end else
   if (cid>=cm_Ackno1) and (cid<=cm_Ackno5) then begin
    quant:=modbus_0xFF00*Ord(GetCmdOpData(cid)<>0);
    dat:=modbus_encode_pdu('R',fid,saddr,quant,'');
   end else
   if (cid>=cm_TestI1) and (cid<=cm_TestI2) then begin
    quant:=Round(GetCmdOpData(cid));
    dat:=modbus_encode_pdu('R',fid,saddr,quant,'');
   end else
   if (cid>=cm_TestL1) and (cid<=cm_TestL2) then begin
    dat:=modbus_encode_pdu('R',fid,saddr,quant,modbus_dump_int32(Round(GetCmdOpData(cid)),modbus_sw_normal));
   end else
   if (cid>=cm_TestF1) and (cid<=cm_TestF2) then begin
    dat:=modbus_encode_pdu('R',fid,saddr,quant,modbus_dump_float(GetCmdOpData(cid),modbus_sw_normal));
   end else
   if (cid>=cm_TestD1) and (cid<=cm_TestD2) then begin
    dat:=modbus_encode_pdu('R',fid,saddr,quant,modbus_dump_double(GetCmdOpData(cid),modbus_sw_normal));
   end else
   Trouble('Invalid command id '+Str(cid));
  end;
  %SAMPLE%_CalcPDU:=modbus_func_ok(fid) and (Length(dat)>0);
 end;
 //
 // Main Modbus command handler to process Modbus device reply.
 // cid   - (in)  command id number.
 // fid   - (in)  Modbus function id.
 // saddr - (in)  zero-based start address of registers to read/write.
 // quant - (in)  data quantity, i.e. number of r/w registers or single register r/w value.
 // raw   - (in)  raw coils/registers data array.
 //
 procedure %SAMPLE%_OnCommand(cid,fid,saddr,quant:Integer; raw:String);
 var addr,offs,nao,ndo:Integer; r:Real;
 begin
  if IsValidCmdNum(cid) then begin 
   // Read holding registers: (float+int16)*32+int16
   if (cid=cm_ReadHR) then begin
    for offs:=0 to quant-1 do begin
     addr:=saddr+offs; // PLC reg.address
     if (addr>=0) and (addr<=95) then begin
      nao:=addr div 3; ndo:=addr div 3;
      if (nao<32) and (addr mod 3 = 0) and (Length(raw)>=offs*2+4) then begin
       r:=modbus_ext_float(raw,offs,modbus_sw_normal);
       Details(RefInfo(RefAo(nao),'Name')+' '+Str(nao)+' '+Str(r));
       UpdateAo(nao,time,r);
      end;
      if (ndo<32) and (addr mod 3 = 2) and (Length(raw)>=offs*2+2) then begin
       r:=modbus_ext_int16(raw,offs,modbus_sw_normal);
       Details(RefInfo(RefDo(ndo),'Name')+' '+Str(ndo)+' '+Str(r));
       UpdateDo(ndo,time,r);
      end;
     end;
     if addr=96 then UpdateDo(32,time,modbus_ext_int32(raw,offs,modbus_sw_normal));
    end;
   end else
   // Read coils: 37 coils
   if (cid=cm_ReadCS) then begin
    for offs:=0 to quant-1 do begin
     addr:=saddr+offs;
     UpdateDo(33+addr,time,modbus_ext_coil(raw,offs));
    end;
   end else
   // Write register/coils: release confirmed command
   // to avoid rewrite the data which already written
   if (cid=cm_Faults) then ReleaseCmdOpData(cid) else
   if (cid=cm_Ackno1) then ReleaseCmdOpData(cid) else
   if (cid=cm_Ackno2) then ReleaseCmdOpData(cid) else
   if (cid=cm_Ackno3) then ReleaseCmdOpData(cid) else
   if (cid=cm_Ackno4) then ReleaseCmdOpData(cid) else
   if (cid=cm_Ackno5) then ReleaseCmdOpData(cid) else
   if (cid=cm_TestI1) then ReleaseCmdOpData(cid) else
   if (cid=cm_TestI2) then ReleaseCmdOpData(cid) else
   if (cid=cm_TestL1) then ReleaseCmdOpData(cid) else
   if (cid=cm_TestL2) then ReleaseCmdOpData(cid) else
   if (cid=cm_TestF1) then ReleaseCmdOpData(cid) else
   if (cid=cm_TestF2) then ReleaseCmdOpData(cid) else
   if (cid=cm_TestD1) then ReleaseCmdOpData(cid) else
   if (cid=cm_TestD2) then ReleaseCmdOpData(cid) else
   Trouble('Unexpected command '+Str(cid));
  end;
 end;
 //
 // Data handler on @Modbus.Reply event. Process reply comes from Modbus device.
 //
 procedure %SAMPLE%_OnReply(ref,cid,tim,port,uid,fid:Integer; dat:String);
 var raw:String; saddr,quant:Integer;
 begin
  raw:=''; // Check is coming reply corresponded to sent request
  if not IsValidCmdNum(cid) then Trouble('Bad reply command '+Str(cid)) else
  if (port<>%SAMPLE%.Modbus.Poll.port) then Trouble('Bad reply port '+Str(port)) else
  if (uid<>%SAMPLE%.Modbus.Poll.uid) then Trouble('Bad reply unit id '+Str(uid)) else
  if (cid<>%SAMPLE%.Modbus.Poll.cid) then Trouble('Bad reply command id '+Str(cid)) else
  if (ref<>%SAMPLE%.Modbus.Poll.ref) then Trouble('Bad reply device '+RefInfo(ref,'Name')) else
  if (modbus_un_except(fid)<>%SAMPLE%.Modbus.Poll.fid) then Trouble('Bad reply function id '+Str(fid)) else begin
   saddr:=%SAMPLE%.Modbus.Poll.saddr; quant:=%SAMPLE%.Modbus.Poll.quant;
   if modbus_decode_pdu('A',fid,dat,saddr,quant,raw)>0 then begin
    if (saddr<>%SAMPLE%.Modbus.Poll.saddr) then Trouble('Bad reply saddr '+Str(saddr)) else
    if (quant<>%SAMPLE%.Modbus.Poll.quant) then Trouble('Bad reply quant '+Str(quant)) else
    if modbus_is_except(fid) then begin
     Problem('MODBUS EXCEPTION '+Str(modbus_ext_byte(raw,0))+' ON COMMAND '+Str(cid));
     %SAMPLE%.Modbus.Poll.Rate.Ex:=%SAMPLE%.Modbus.Poll.Rate.Ex+1;
    end else begin
     %SAMPLE%.Modbus.Poll.Rate.Rx:=%SAMPLE%.Modbus.Poll.Rate.Rx+1;
     %SAMPLE%_OnCommand(cid,fid,saddr,quant,raw);
    end;
   end else Trouble('Bad PDU format '+modbus_errmsg(modbus_errno));
  end;
  raw:='';
 end;
 //
 // Data handler on @Modbus.Poll event.
 // This procedure calls in simulation mode only.
 //
 procedure %SAMPLE%_OnSimPoll(ref,cid,tim,port,uid,fid:Integer; dat:String);
 var raw:String; saddr,quant,offs,addr:Integer; r:Real;
 begin
  raw:=''; // Check is coming reply corresponded to sent request
  if not IsValidCmdNum(cid) then Trouble('Bad reply command '+Str(cid)) else
  if (port<>%SAMPLE%.Modbus.Poll.port) then Trouble('Bad reply port '+Str(port)) else
  if (uid<>%SAMPLE%.Modbus.Poll.uid) then Trouble('Bad reply unit id '+Str(uid)) else
  if (cid<>%SAMPLE%.Modbus.Poll.cid) then Trouble('Bad reply command id '+Str(cid)) else
  if (ref<>%SAMPLE%.Modbus.Poll.ref) then Trouble('Bad reply device '+RefInfo(ref,'Name')) else
  if (modbus_un_except(fid)<>%SAMPLE%.Modbus.Poll.fid) then Trouble('Bad reply function id '+Str(fid)) else begin
   saddr:=%SAMPLE%.Modbus.Poll.saddr; quant:=%SAMPLE%.Modbus.Poll.quant;
   if modbus_decode_pdu('R',fid,dat,saddr,quant,raw)>0 then begin
    if (saddr<>%SAMPLE%.Modbus.Poll.saddr) then Trouble('Bad reply saddr '+Str(saddr)) else
    if (quant<>%SAMPLE%.Modbus.Poll.quant) then Trouble('Bad reply quant '+Str(quant)) else begin
     if DebugFlagEnabled(dfDetails) then Details('Simulate polling '+Str(cid));
     if (cid=cm_ReadHR) then begin // 97 registers (float+int16)*32+int16
      raw:='';
      for offs:=0 to quant-1 do begin
       addr:=saddr+offs;
       if (0<=addr) and (addr<=96) then
       if (addr=96) then raw:=raw+modbus_dump_int16(%SAMPLE%.Sim.Ready,modbus_sw_normal) else
       if (addr mod 3 = 0) then raw:=raw+modbus_dump_float(%SAMPLE%.Sim.Param[addr div 3],modbus_sw_normal) else
       if (addr mod 3 = 2) then raw:=raw+modbus_dump_int16(%SAMPLE%.Sim.State[addr div 3],modbus_sw_normal);
      end;
      dat:=modbus_encode_pdu('A',fid,saddr,quant,raw);
      DevSendCmd(devMySelf,modbus_proxy_poll('@Modbus.Reply',devMySelf,cid,0,port,uid,fid,dat));
     end else
     if (cid=cm_ReadCS) then begin // 37 coils
      raw:=modbus_dump_int32(Round(DiWord(0,32)),modbus_sw_native)+Chr(Round(DiWord(32,5)));
      dat:=modbus_encode_pdu('A',fid,saddr,quant,raw);
      DevSendCmd(devMySelf,modbus_proxy_poll('@Modbus.Reply',devMySelf,cid,0,port,uid,fid,dat));
     end else
     if (cid=cm_Faults) then begin
      r:=modbus_ext_int32(raw,0,modbus_sw_native);
      bNul(iSetTag(%SAMPLE%.TESTER.FAULT.tag,Round(r)));
      dat:=modbus_encode_pdu('A',fid,saddr,quant,'');
      DevSendCmd(devMySelf,modbus_proxy_poll('@Modbus.Reply',devMySelf,cid,0,port,uid,fid,dat));
     end else
     if (cid>=cm_Ackno1) and (cid<=cm_Ackno5) then begin
      bNul(iSetTagBitState(%SAMPLE%.TESTER.ACKNO.tag,cid-cm_Ackno1,quant<>0));
      dat:=modbus_encode_pdu('A',fid,saddr,quant,'');
      DevSendCmd(devMySelf,modbus_proxy_poll('@Modbus.Reply',devMySelf,cid,0,port,uid,fid,dat));
     end else
     if (cid=cm_TestI1) then begin
      bNul(iSetTag(%SAMPLE%.TESTER.I1.tag,quant));
      dat:=modbus_encode_pdu('A',fid,saddr,quant,'');
      DevSendCmd(devMySelf,modbus_proxy_poll('@Modbus.Reply',devMySelf,cid,0,port,uid,fid,dat));
     end else
     if (cid=cm_TestI2) then begin
      bNul(iSetTag(%SAMPLE%.TESTER.I2.tag,quant));
      dat:=modbus_encode_pdu('A',fid,saddr,quant,'');
      DevSendCmd(devMySelf,modbus_proxy_poll('@Modbus.Reply',devMySelf,cid,0,port,uid,fid,dat));
     end else
     if (cid=cm_TestL1) then begin
      bNul(iSetTag(%SAMPLE%.TESTER.L1.tag,modbus_ext_int32(raw,0,modbus_sw_normal)));
      dat:=modbus_encode_pdu('A',fid,saddr,quant,'');
      DevSendCmd(devMySelf,modbus_proxy_poll('@Modbus.Reply',devMySelf,cid,0,port,uid,fid,dat));
     end else
     if (cid=cm_TestL2) then begin
      bNul(iSetTag(%SAMPLE%.TESTER.L2.tag,modbus_ext_int32(raw,0,modbus_sw_normal)));
      dat:=modbus_encode_pdu('A',fid,saddr,quant,'');
      DevSendCmd(devMySelf,modbus_proxy_poll('@Modbus.Reply',devMySelf,cid,0,port,uid,fid,dat));
     end else
     if (cid=cm_TestF1) then begin
      bNul(rSetTag(%SAMPLE%.TESTER.F1.tag,modbus_ext_float(raw,0,modbus_sw_normal)));
      dat:=modbus_encode_pdu('A',fid,saddr,quant,'');
      DevSendCmd(devMySelf,modbus_proxy_poll('@Modbus.Reply',devMySelf,cid,0,port,uid,fid,dat));
     end else
     if (cid=cm_TestF2) then begin
      bNul(rSetTag(%SAMPLE%.TESTER.F2.tag,modbus_ext_float(raw,0,modbus_sw_normal)));
      dat:=modbus_encode_pdu('A',fid,saddr,quant,'');
      DevSendCmd(devMySelf,modbus_proxy_poll('@Modbus.Reply',devMySelf,cid,0,port,uid,fid,dat));
     end else
     if (cid=cm_TestD1) then begin
      bNul(rSetTag(%SAMPLE%.TESTER.D1.tag,modbus_ext_double(raw,0,modbus_sw_normal)));
      dat:=modbus_encode_pdu('A',fid,saddr,quant,'');
      DevSendCmd(devMySelf,modbus_proxy_poll('@Modbus.Reply',devMySelf,cid,0,port,uid,fid,dat));
     end else
     if (cid=cm_TestD2) then begin
      bNul(rSetTag(%SAMPLE%.TESTER.D2.tag,modbus_ext_double(raw,0,modbus_sw_normal)));
      dat:=modbus_encode_pdu('A',fid,saddr,quant,'');
      DevSendCmd(devMySelf,modbus_proxy_poll('@Modbus.Reply',devMySelf,cid,0,port,uid,fid,dat));
     end else
     DevSendCmd(devMySelf,modbus_proxy_poll('@Modbus.Reply',devMySelf,cid,0,port,uid,
                modbus_as_except(fid,true),Dump(Chr(modbus_er_ILLFUN))));
    end;
   end else Trouble('Bad PDU format '+modbus_errmsg(modbus_errno));
  end;
  raw:='';
 end;
 //
 // %SAMPLE% Driver Simulator mode polling.
 // This procedure calls in simulation mode only.
 //
 procedure %SAMPLE%_SIM_POLL;
 var i:Integer;
 begin
  if %SAMPLE%.Simulator then begin
   for i:=0 to 31 do %SAMPLE%.Sim.Param[i]:=10+2*i+2*sin(2*pi*time*60)+0.1*random(-1,1);
  end;
 end;
 //
 // %SAMPLE% Driver GUI polling.
 //
 procedure %SAMPLE%_GUI_POLL;
 var s:String; ClickCurve:Integer;
  //
  // Send to device (dev) command (cmd data) to set new tag value if one in range (min,max).
  //
  procedure DevSendCmdTag(dev,tag:Integer; cmd,data:String; min,max:Real);
  var v:Real;
  begin
   if IsRefTag(tag) then
   if IsRefDevice(dev) then
   if not IsEmptyStr(cmd) then
   if not IsEmptyStr(data) then
   if TypeTag(tag)<=2 then begin
    v:=rVal(data);
    if IsNaN(v) then Trouble('Invalid tag edit') else
    if (v<min) or (v>max) then Trouble('Tag edit out of range') else
    if TypeTag(tag)=1 then DevSendCmd(devMySelf,cmd+' '+Str(Round(v))) else
    if TypeTag(tag)=2 then DevSendCmd(devMySelf,cmd+' '+Trim(data));
   end else
   if TypeTag(tag)=3 then begin
    DevSendCmd(devMySelf,cmd+' '+Trim(data));
   end;
  end;
  //
  // Send command (cmd) to device (dev) on tag click.
  //
  procedure ClickTagDevSendCmd(tag,dev:Integer; cmd:String);
  begin
   if Length(cmd)>0 then
   if IsRefTag(tag) then
   if IsRefDevice(dev) then
   if ClickTag=tag then begin
    bNul(Voice(snd_Click));
    DevSendCmd(dev,cmd);
   end;
  end;
  //
  // Xor bit on click (local version).
  //
  procedure ClickTagXorLocal(tag,XorMask:Integer);
  var nv:Integer;
  begin
   if (ClickTag=tag) then begin
    bNul(iSetTagXor(tag,XorMask));
    bNul(Voice(snd_Click));
   end;
  end;
 begin
  s:='';
  //
  // Process data...
  //
  if not %SAMPLE%.Simulator then begin
   bNul(iSetTag(%SAMPLE%.TESTER.FAULT.tag,Round(DiWord(0,32))));
   bNul(iSetTag(%SAMPLE%.TESTER.ACKNO.tag,Round(DiWord(32,5))));
  end;
  //
  // Handle commands...
  //
  if iGetTag(%SAMPLE%.TESTER.HELP.tag)<>0 then begin
   rNul(Eval('@Global @Async @Silent @Run -hide cmd /c '+DaqFileRef(ReadIni('[DAQ] HelpFile'),'.htm')));
   bNul(iSetTag(%SAMPLE%.TESTER.HELP.tag,0));
   DevSendCmd(devMySelf,'@Help');
  end;
  //
  // Handle sensor clicks...
  //
  if ClickButton=1 then begin
   //
   // Press button(s) on Click.
   //
   ClickTagXorLocal(%SAMPLE%.TESTER.HELP.tag,1);
   //
   // Send command to edit on Click.
   //
   ClickTagDevSendCmd(%SAMPLE%.TESTER.I1.tag,devMySelf,'@Edit TEST.I1');
   ClickTagDevSendCmd(%SAMPLE%.TESTER.I2.tag,devMySelf,'@Edit TEST.I2');
   ClickTagDevSendCmd(%SAMPLE%.TESTER.L1.tag,devMySelf,'@Edit TEST.L1');
   ClickTagDevSendCmd(%SAMPLE%.TESTER.L2.tag,devMySelf,'@Edit TEST.L2');
   ClickTagDevSendCmd(%SAMPLE%.TESTER.F1.tag,devMySelf,'@Edit TEST.F1');
   ClickTagDevSendCmd(%SAMPLE%.TESTER.F2.tag,devMySelf,'@Edit TEST.F2');
   ClickTagDevSendCmd(%SAMPLE%.TESTER.D1.tag,devMySelf,'@Edit TEST.D1');
   ClickTagDevSendCmd(%SAMPLE%.TESTER.D2.tag,devMySelf,'@Edit TEST.D2');
   ClickTagDevSendCmd(%SAMPLE%.TESTER.ACKNO.tag,devMySelf,'@Edit TEST.ACKNO');
   ClickTagDevSendCmd(%SAMPLE%.TESTER.FAULT.tag,devMySelf,'@Edit TEST.FAULT');
   //
   // Plot & Tab windows
   //
   ClickCurve:=RefFind('Curve '+ClickParams('Curve'));
   if IsRefCurve(ClickCurve) then begin
    iNul(WinSelectByCurve(ClickCurve,ClickCurve));
    bNul(Voice(snd_Wheel));
   end;
  end;
  //
  // Edit tags...
  //
  if EditState=ef_Done then begin
   //
   // If tag edit complete, send command to apply changes
   //
   if CheckEditTag(%SAMPLE%.TESTER.I1.tag,s)
   then DevSendCmdTag(devMySelf,%SAMPLE%.TESTER.I1.tag,'@Test.I1',s,0,modbus_MaxWord);
   if CheckEditTag(%SAMPLE%.TESTER.I2.tag,s)
   then DevSendCmdTag(devMySelf,%SAMPLE%.TESTER.I2.tag,'@Test.I2',s,0,modbus_MaxWord);
   if CheckEditTag(%SAMPLE%.TESTER.L1.tag,s)
   then DevSendCmdTag(devMySelf,%SAMPLE%.TESTER.L1.tag,'@Test.L1',s,_MinusInf,_PlusInf);
   if CheckEditTag(%SAMPLE%.TESTER.L2.tag,s)
   then DevSendCmdTag(devMySelf,%SAMPLE%.TESTER.L2.tag,'@Test.L2',s,_MinusInf,_PlusInf);
   if CheckEditTag(%SAMPLE%.TESTER.F1.tag,s)
   then DevSendCmdTag(devMySelf,%SAMPLE%.TESTER.F1.tag,'@Test.F1',s,_MinusInf,_PlusInf);
   if CheckEditTag(%SAMPLE%.TESTER.F2.tag,s)
   then DevSendCmdTag(devMySelf,%SAMPLE%.TESTER.F2.tag,'@Test.F2',s,_MinusInf,_PlusInf);
   if CheckEditTag(%SAMPLE%.TESTER.D1.tag,s)
   then DevSendCmdTag(devMySelf,%SAMPLE%.TESTER.D1.tag,'@Test.D1',s,_MinusInf,_PlusInf);
   if CheckEditTag(%SAMPLE%.TESTER.D2.tag,s)
   then DevSendCmdTag(devMySelf,%SAMPLE%.TESTER.D2.tag,'@Test.D2',s,_MinusInf,_PlusInf);
   if CheckEditTag(%SAMPLE%.TESTER.ACKNO.tag,s)
   then DevSendCmdTag(devMySelf,%SAMPLE%.TESTER.ACKNO.tag,'@Test.ACKNO',s,_MinusInf,_PlusInf);
   if CheckEditTag(%SAMPLE%.TESTER.FAULT.tag,s)
   then DevSendCmdTag(devMySelf,%SAMPLE%.TESTER.FAULT.tag,'@Test.FAULT',s,_MinusInf,_PlusInf);
   {
   Warning.
   }
   if IsSameText(ExtractWord(1,edit('?ans 0')),'Warning') then sNul(Edit(''));
   {
   Information.
   }
   if IsSameText(ExtractWord(1,edit('?ans 0')),'Information') then sNul(Edit(''));
  end;
  if EditState=ef_Done then begin
   Problem('Unknown tag edition!');
   sNul(Edit(''));
  end;
  if iAnd(EditState,ef_ErrorFound)<>0 then begin
   Problem('Dialog error detected!');
   sNul(Edit(''));
  end;
  s:='';
 end;
 //
 // %SAMPLE% Driver Command Cycle polling.
 //
 procedure %SAMPLE%_CMD_POLL;
  //
  // Send @Modbus.Poll ... command to &ModbusProxy and save sent request data in Modbus.Poll record.
  //
  procedure PollModbusProxy(cid:Integer);
  var dev,ref,tot,port,uid,fid,saddr,quant:Integer; dat:String;
  begin
   dat:='';
   ApplyCmdOpData(cid);
   if %SAMPLE%_CalcPDU(cid,fid,saddr,quant,dat) then begin
    dev:=devTheProxy; ref:=devMySelf; tot:=%SAMPLE%.Modbus.Timeout; port:=%SAMPLE%.Modbus.Port; uid:=%SAMPLE%.Modbus.UnitId;
    if DevSend(dev,modbus_proxy_poll('@Modbus.Poll',ref,cid,tot,port,uid,fid,dat)+CRLF)>0 then begin
     if DebugFlagEnabled(dfViewExp) then 
     ViewExp('COM: '+modbus_proxy_nice('@Modbus.Poll',ref,cid,tot,port,uid,fid,dat,32));
     AssignModbusPoll(dev,cid,mSecNow,port,uid,fid,saddr,quant,dat);
     %SAMPLE%.Modbus.Poll.Rate.Tx:=%SAMPLE%.Modbus.Poll.Rate.Tx+1;
    end else begin
     Trouble('Fail to send command '+Str(cid));
     ClearModbusPoll;
    end;
   end else begin
    Trouble('Fail to calc command '+Str(cid));
    ClearModbusPoll;
   end;
   dat:='';
  end;
 begin
  if IsPortOpened then begin
   if IsValidCmdNum(%SAMPLE%.Modbus.Poll.cid) then begin
    //
    // Request in progress, waiting @Modbus.Reply/@Modbus.Refuse/@Modbus.Timeout.
    // Handle Deadline if was no responce for too long time: repeat polling again.
    //
    if (mSecNow>%SAMPLE%.Modbus.Poll.tim+%SAMPLE%.Modbus.Deadline) then begin
     %SAMPLE%.Modbus.Poll.Rate.Ex:=%SAMPLE%.Modbus.Poll.Rate.Ex+1;
     Trouble('Deadline detected, repeat polling again...');
     PollModbusProxy(%SAMPLE%.Modbus.Poll.cid);
    end;
   end else begin
    //
    // If request is cleared, send new @Modbus.Poll request by timer.
    //
    if (mSecNow>=%SAMPLE%.Modbus.Poll.tim+%SAMPLE%.Modbus.Polling) then begin
     %SAMPLE%.Modbus.Cmd.Num:=NextEnabledCmdNum(%SAMPLE%.Modbus.Cmd.Num);
     if IsEnabledCmdNum(%SAMPLE%.Modbus.Cmd.Num)
     then PollModbusProxy(%SAMPLE%.Modbus.Cmd.Num);
    end;
   end;
   //
   // Update Poll Rate.
   //
   if SysTimer_Pulse(1000)>0 then begin
    UpdateDo(do_POLLRATERX,time,%SAMPLE%.Modbus.Poll.Rate.Rx);
    UpdateDo(do_POLLRATETX,time,%SAMPLE%.Modbus.Poll.Rate.Tx);
    UpdateDo(do_POLLRATEEX,time,%SAMPLE%.Modbus.Poll.Rate.Ex);
    UpdateDo(do_ERRORCOUNT,time,GetErrCount(-1));
    if DebugFlagEnabled(dfStatist) then
    Success('PollRate: Rx='+Str(%SAMPLE%.Modbus.Poll.Rate.Rx)
                   +'  Tx='+Str(%SAMPLE%.Modbus.Poll.Rate.Tx)
                   +'  Ex='+Str(%SAMPLE%.Modbus.Poll.Rate.Ex));
    ClearModbusRate;
   end;
  end;
 end;
 //
 // %SAMPLE% tags initialization.
 //
 procedure %SAMPLE%_InitTags(Prefix:String);
 begin
  if not IsEmptyStr(Prefix) then begin
   InitTag(%SAMPLE%.TESTER.I1.tag,       Prefix+'.TESTER.I1',       1);
   InitTag(%SAMPLE%.TESTER.I2.tag,       Prefix+'.TESTER.I2',       1);
   InitTag(%SAMPLE%.TESTER.L1.tag,       Prefix+'.TESTER.L1',       1);
   InitTag(%SAMPLE%.TESTER.L2.tag,       Prefix+'.TESTER.L2',       1);
   InitTag(%SAMPLE%.TESTER.F1.tag,       Prefix+'.TESTER.F1',       2);
   InitTag(%SAMPLE%.TESTER.F2.tag,       Prefix+'.TESTER.F2',       2);
   InitTag(%SAMPLE%.TESTER.D1.tag,       Prefix+'.TESTER.D1',       2);
   InitTag(%SAMPLE%.TESTER.D2.tag,       Prefix+'.TESTER.D2',       2);
   InitTag(%SAMPLE%.TESTER.ACKNO.tag,    Prefix+'.TESTER.ACKNO',    1);
   InitTag(%SAMPLE%.TESTER.FAULT.tag,    Prefix+'.TESTER.FAULT',    1);
   InitTag(%SAMPLE%.TESTER.HELP.tag,     Prefix+'.TESTER.HELP',     1);
  end;
 end;
 //
 // %SAMPLE% Driver cleanup.
 //
 procedure %SAMPLE%_Clear;
 begin
  %SAMPLE%.Simulator:=False;
  %SAMPLE%.Modbus.Port:=0;
  %SAMPLE%.Modbus.UnitId:=0;
  %SAMPLE%.Modbus.Timeout:=0;
  %SAMPLE%.Modbus.Polling:=0;
  %SAMPLE%.Modbus.Deadline:=0;
  ClearModbusPoll;
  ClearModbusRate;
  ClearCmdTable;
 end;
 //
 // %SAMPLE% Driver initialization...
 //
 procedure %SAMPLE%_Init;
 begin
  //
  // Read ini file variables
  //
  %SAMPLE%_InitTags(ReadIni('tagPrefix'));
  %SAMPLE%.Simulator:=iValDef(ReadIni('Simulator'),0)<>0;
  Success('Simulator='+Str(Ord(%SAMPLE%.Simulator)));
  %SAMPLE%.Modbus.Port:=iValDef(ReadIni('ModbusPort'),1);
  Success('ModbusPort='+Str(%SAMPLE%.Modbus.Port));
  %SAMPLE%.Modbus.UnitId:=iValDef(ReadIni('ModbusUnitId'),1);
  Success('ModbusUnitId='+Str(%SAMPLE%.Modbus.UnitId));
  %SAMPLE%.Modbus.Polling:=iValDef(ReadIni('ModbusPolling'),1000);
  Success('ModbusPolling='+Str(%SAMPLE%.Modbus.Polling));
  %SAMPLE%.Modbus.Timeout:=iValDef(ReadIni('ModbusTimeout'),250);
  Success('ModbusTimeout='+Str(%SAMPLE%.Modbus.Timeout));
  %SAMPLE%.Modbus.Deadline:=iValDef(ReadIni('ModbusDeadline'),60000);
  Success('ModbusDeadline='+Str(%SAMPLE%.Modbus.Deadline));
  %SAMPLE%.Modbus.DelayOnStart:=iValDef(ReadIni('DelayOnStart'),1000);
  Success('DelayOnStart='+Str(%SAMPLE%.Modbus.DelayOnStart));
  //
  // Initialize Cmd command table & clear Poll record.
  //
  ClearModbusPoll;
  ClearModbusRate;
  InitCmdTable;
 end;
 //
 // %SAMPLE% Driver Finalization.
 //
 procedure %SAMPLE%_Free;
 begin
  ClearModbusPoll;
  ClearModbusRate;
  ClearCmdTable;
 end;
 //
 // %SAMPLE% Driver Polling.
 //
 procedure %SAMPLE%_Poll;
 begin
  %SAMPLE%_GUI_POLL;
  if mSecNow-FixmSecNow>%SAMPLE%.Modbus.DelayOnStart then
  if IsPortOpened then begin
   if %SAMPLE%.Simulator
   then %SAMPLE%_SIM_POLL;
   %SAMPLE%_CMD_POLL;
  end;
 end;
 {
 Clear user application strings...
 }
 procedure ClearApplication;
 begin
  ClearNetLibrary;
  %SAMPLE%_Clear;
 end;
 {
 User application Initialization...
 }
 procedure InitApplication;
 begin
  StdIn_SetScripts('@StartupScript','');
  StdIn_SetTimeouts(0,0,MaxInt,0);
  InitNetLibrary;
  %SAMPLE%_Init;
 end;
 {
 User application Finalization...
 }
 procedure FreeApplication;
 begin
  FreeNetLibrary;
  %SAMPLE%_Free;
 end;
 {
 User application Polling...
 }
 procedure PollApplication;
 begin
  PollNetLibrary;
  %SAMPLE%_Poll;
 end;
 {
 Process data coming from standard input...
 }
 procedure StdIn_Processor(var Data:String);
 var cmd,arg,dat:String; ref,cid,tim,port,uid,fid,n:Integer;
 begin
  if DebugFlagEnabled(dfViewImp) then ViewImp('CON: '+Data);
  {
  Handle "@cmd=arg" or "@cmd arg" commands:
  }
  cmd:='';
  arg:='';
  dat:='';
  if GotCommand(Data,cmd,arg) then begin
   {
   @Modbus.Reply ref cid tim port uid fid $$dat
   }
   if IsSameText(cmd,'@Modbus.Reply') then begin
    if modbus_proxy_reply(cmd,arg,ref,cid,tim,port,uid,fid,dat)
    then %SAMPLE%_OnReply(ref,cid,tim,port,uid,fid,dat)
    else Trouble('Bad '+cmd+' format');
    ClearModbusPoll;
    Data:='';
   end else
   {
   @Modbus.Poll ref cid tim port uid fid $$dat
   This message uses in simulation mode only
   }
   if IsSameText(cmd,'@Modbus.Poll') then begin
    if modbus_proxy_reply(cmd,arg,ref,cid,tim,port,uid,fid,dat)
    then %SAMPLE%_OnSimPoll(ref,cid,tim,port,uid,fid,dat)
    else Trouble('Bad '+cmd+' format');
    Data:='';
   end else
   {
   @Modbus.Refuse ...
   }
   if IsSameText(cmd,'@Modbus.Refuse') then begin
    %SAMPLE%.Modbus.Poll.Rate.Ex:=%SAMPLE%.Modbus.Poll.Rate.Ex+1;
    if modbus_proxy_reply(cmd,arg,ref,cid,tim,port,uid,fid,dat)
    then Trouble(modbus_proxy_nice(cmd,ref,cid,tim,port,uid,fid,dat,0))
    else Trouble(cmd+' '+arg);
    ClearModbusPoll;
    Data:='';
   end else
   {
   @Modbus.Timeout ...
   }
   if IsSameText(cmd,'@Modbus.Timeout') then begin
    %SAMPLE%.Modbus.Poll.Rate.Ex:=%SAMPLE%.Modbus.Poll.Rate.Ex+1;
    if modbus_proxy_reply(cmd,arg,ref,cid,tim,port,uid,fid,dat)
    then Trouble(modbus_proxy_nice(cmd,ref,cid,tim,port,uid,fid,dat,64))
    else Trouble(cmd+' '+arg);
    ClearModbusPoll;
    Data:='';
   end else
   {
   @Test.Fault $0102F
   }
   if IsSameText(cmd,'@Test.Fault') then begin
    HoldCmdOpData(cm_Faults,rVal(ExtractWord(1,arg)));
    Data:='';
   end else
   {
   @Test.Ackno $10
   }
   if IsSameText(cmd,'@Test.Ackno') then begin
    n:=Val(ExtractWord(1,arg));
    HoldCmdOpData(cm_Ackno1,Ord(IsBit(n,0)));
    HoldCmdOpData(cm_Ackno2,Ord(IsBit(n,1)));
    HoldCmdOpData(cm_Ackno3,Ord(IsBit(n,2)));
    HoldCmdOpData(cm_Ackno4,Ord(IsBit(n,3)));
    HoldCmdOpData(cm_Ackno5,Ord(IsBit(n,4)));
    Data:='';
   end else
   {
   @Test.I1 123
   }
   if IsSameText(cmd,'@Test.I1') then begin
    HoldCmdOpData(cm_TestI1,rVal(ExtractWord(1,arg)));
    Data:='';
   end else
   {
   @Test.I2 123
   }
   if IsSameText(cmd,'@Test.I2') then begin
    HoldCmdOpData(cm_TestI2,rVal(ExtractWord(1,arg)));
    Data:='';
   end else
   {
   @Test.L1 123
   }
   if IsSameText(cmd,'@Test.L1') then begin
    HoldCmdOpData(cm_TestL1,rVal(ExtractWord(1,arg)));
    Data:='';
   end else
   {
   @Test.L2 123
   }
   if IsSameText(cmd,'@Test.L2') then begin
    HoldCmdOpData(cm_TestL2,rVal(ExtractWord(1,arg)));
    Data:='';
   end else
   {
   @Test.F1 1.23
   }
   if IsSameText(cmd,'@Test.F1') then begin
    HoldCmdOpData(cm_TestF1,rVal(ExtractWord(1,arg)));
    Data:='';
   end else
   {
   @Test.F2 1.23
   }
   if IsSameText(cmd,'@Test.F2') then begin
    HoldCmdOpData(cm_TestF2,rVal(ExtractWord(1,arg)));
    Data:='';
   end else
   {
   @Test.D1 1.23
   }
   if IsSameText(cmd,'@Test.D1') then begin
    HoldCmdOpData(cm_TestD1,rVal(ExtractWord(1,arg)));
    Data:='';
   end else
   {
   @Test.D2 1.23
   }
   if IsSameText(cmd,'@Test.D2') then begin
    HoldCmdOpData(cm_TestD2,rVal(ExtractWord(1,arg)));
    Data:='';
   end else
   {
   @Edit Test.I1
   }
   if IsSameText(cmd,'@Edit') then begin
    if IsSameText(ExtractWord(1,arg),'Test.I1')
    then StartEditTag(%SAMPLE%.TESTER.I1.tag,'Уставка I1');
    if IsSameText(ExtractWord(1,arg),'Test.I2')
    then StartEditTag(%SAMPLE%.TESTER.I2.tag,'Уставка I2');
    if IsSameText(ExtractWord(1,arg),'Test.L1')
    then StartEditTag(%SAMPLE%.TESTER.L1.tag,'Уставка L1');
    if IsSameText(ExtractWord(1,arg),'Test.L2')
    then StartEditTag(%SAMPLE%.TESTER.L2.tag,'Уставка L2');
    if IsSameText(ExtractWord(1,arg),'Test.F1')
    then StartEditTag(%SAMPLE%.TESTER.F1.tag,'Уставка F1');
    if IsSameText(ExtractWord(1,arg),'Test.F2')
    then StartEditTag(%SAMPLE%.TESTER.F2.tag,'Уставка F2');
    if IsSameText(ExtractWord(1,arg),'Test.D1')
    then StartEditTag(%SAMPLE%.TESTER.D1.tag,'Уставка D1');
    if IsSameText(ExtractWord(1,arg),'Test.D2')
    then StartEditTag(%SAMPLE%.TESTER.D2.tag,'Уставка D2');
    if IsSameText(ExtractWord(1,arg),'Test.ACKNO')
    then StartEditTag(%SAMPLE%.TESTER.ACKNO.tag,'Уставка ACKNO');
    if IsSameText(ExtractWord(1,arg),'Test.FAULT')
    then StartEditTag(%SAMPLE%.TESTER.FAULT.tag,'Уставка FAULT');
    Data:='';
   end else
   {
   Handle other commands by default handler...
   }
   StdIn_DefaultHandler(Data,cmd,arg);
  end;
  Data:='';
  cmd:='';
  arg:='';
  dat:='';
 end;

{***************************************************}
{***************************************************}
{***                                             ***}
{***  MMM    MMM        AAA   IIII   NNN    NN   ***}
{***  MMMM  MMMM       AAAA    II    NNNN   NN   ***}
{***  MM MMMM MM      AA AA    II    NN NN  NN   ***}
{***  MM  MM  MM     AA  AA    II    NN  NN NN   ***}
{***  MM      MM    AAAAAAA    II    NN   NNNN   ***}
{***  MM      MM   AA    AA   IIII   NN    NNN   ***}
{***                                             ***}
{***************************************************}
{$I _std_main}{*** Please never change this code ***}
{***************************************************}
