unit DevicesFrame;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, Mask, ToolEdit, ComboDigEdit, ComCtrls, Buttons, ExtCtrls,
  CheckLst, ImgList, ToolWin, StrUtils, HexGrid, SendThread,VCLUtils,
  RUtilites, CommInt, Math;


const
  strDELIMTR = '|' ;
  strA = 'Area_';
  strC = '_content';
  strD = '_data';
  strP = 'Param_';
  strS = '_choice';
  strI = '_icon';
  strDDELIMTR = ',';

Type
  TfrmDeviceClass = class of TfrmDevice;


//  TDinArray = array of array of Integer;

  TNodeData = record
    strCapt:string;      // Название в списке
    strID:string;        // Строковый идентификатор
    strAreaID:string;    // Идентификатор для привязки к
                         // программируемой области
    intSelSubIt:integer; // абсолютный индекс выбранного субузла
    strMask:string;      // битовая маска данных
    strMask2:string;     // битовая маска данных 2
    DataArr:array [0..2] of cardinal; // дополнительные данные
  end;

  TNodesData = array of TNodeData;


  TAreaData = record
    strCapt:string;          // Название в списке
    strID:string;            // Строковый идентификатор
    DataArr:array [0..2] of cardinal;// дополнительные данные
  end;

  TAreasData = array of TAreaData;

  TfrmDevice = class(TFrame)
    pnDevice: TPanel;
    pnlDevName: TPanel;
    Label16: TLabel;
    tlbButtons: TToolBar;
    tbRead: TToolButton;
    tbWrite: TToolButton;
    tbVerify: TToolButton;
    tbBlankCheck: TToolButton;
    tbErase: TToolButton;
    tbStop: TToolButton;
    ImageList1: TImageList;
    lblDeviceName: TLabel;
    pnlBufAdresses: TPanel;
    Label1: TLabel;
    Label7: TLabel;
    Label13: TLabel;
    edBufFrom: TComboDigEdit;
    edChipFrom: TComboDigEdit;
    edChipTo: TComboDigEdit;
    pnlFusesLabel: TPanel;
    lblParameters: TLabel;
    lsvAreas: TListView;
    Splitter1: TSplitter;
    ImageList2: TImageList;
    trvParameters: TTreeView;
    procedure lsvAreasInfoTip(Sender: TObject; Item: TListItem;
      var InfoTip: String);
    procedure trvParametersClick(Sender: TObject);
    procedure tbReadClick(Sender: TObject);
    procedure tbVerifyClick(Sender: TObject);
    procedure tbWriteClick(Sender: TObject);
    procedure tbEraseClick(Sender: TObject);
    procedure tbBlankCheckClick(Sender: TObject);
    procedure tbStopClick(Sender: TObject);
  private

    { Private declarations }
    procedure InitChipClass;      // Инициализация в программаторе класса чипов
//    procedure FillAreaAndParams(SectName:String);
    procedure FillAreasData(Indx:Integer;strData:string;aCaption:string);
    procedure FillSubNodesData(Indx:Integer;strData:string;aCaption:string);

    procedure InsertItemToArea(SectName:String;N:integer);
//    procedure InsVectorToDinArray(var DinArray:TDinArray;Indx:Integer;strData:string);
    procedure InsPictToNode(TreeNode:TTreeNode;str:String);
    procedure InsertItemToParams(SectName:String;N:integer);
    procedure InsertSubItemsToParams(TreeNode:TTreeNode;SectName:String;Key:string;N:integer);
  protected
    Buf:TThreadBuf;
    FDataMask:word;
    AreasData: TAreasData;     //
    NodesData: TNodesData;
    FVerify:boolean;

    StrList_ErrorsRep:TStringList;
//    SubNodesData: TDinArray;  // Двумерный массив
    procedure FreeSendThread;
    procedure ReadDataToGrid(ArIndx:integer;Grid:THexGrid;sfxStr:string;len:byte);
    procedure ReadDataToBuf(ArIndx:integer;var DataBuf: array of byte;sfxStr:string;len:byte);
    procedure WriteDataFromGrid(ArIndx:integer;Grid:THexGrid;sfxStr:string;len:byte);
    procedure WriteDataFromStr(ArIndx:integer;st:string;sfxStr:string);
    function  GetDataMask(bitn:integer):word;


    // Найти индекс узла по идентификатору
    function FindNodeIndex(ID:string;var indx:integer):boolean;
    procedure SelectSubNode(Node,SubNode:TTreeNode);

  public

    InitRequired:boolean;
    FSendThread:TSendThread;
    CommPort:TComm;
    ChipClassInitialised : boolean; // Класс чипов в программаторе инициализирован
    ChipClassID: string;          // Строковый идентификатор класса чипов

    FProcessProc:procedure of object;
    procedure CheckSendThread;
    class procedure CreateInPlace(Owner:TWinControl;ChipName:String;var aCommPort:TComm);virtual;
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    procedure FillAreaAndParams(SectName:String);
  end;


implementation

uses MainForm,CreateMenu, Connecting, InProgress;
{$R *.DFM}

class procedure TfrmDevice.CreateInPlace(Owner:TWinControl;ChipName:String;var aCommPort:TComm);
var frm:TfrmDevice;
    SectName:string;
begin
  frm:=Create(Owner);
  SectName:='Chip '+ChipName;
  frm.lblDeviceName.Caption:=MemIniFile.ReadString(sectName,'ItemCaption','');
  frm.Parent:=Owner;
  frm.Align:=alClient;
  frm.Visible:=true;
  frm.CommPort:=aCommPort;
  //  Заполнение области Areas
  frm.FillAreaAndParams(SectName);
//  frm.InitChipClass;  //слишком рано порт еще не сконструирован
end;

constructor TfrmDevice.Create(AOwner: TComponent);
begin
  inherited;
  InitRequired:=true;
  ChipClassInitialised :=false;
  StrList_ErrorsRep:=TStringList.Create;
end;


destructor TfrmDevice.Destroy;
begin
  inherited;
  AreasData:=nil;
  NodesData:=nil;
//  SubNodesData:=nil;
  frmMain.TabSheet1.Caption:='Data';
  frmMain.TabSheet2.TabVisible:=false;
  StrList_ErrorsRep.Free;

end;

procedure TfrmDevice.FillAreaAndParams(SectName:String);
begin
  AreasData:=nil;
  NodesData:=nil;
//  SubNodesData:=nil;
  InsertItemToArea(SectName,1);
  InsertItemToParams(SectName,1);
end;


// рекурсивная процедура заполнения списка программируемых областей
// вместе с сопутствующими данными
procedure TfrmDevice.InsertItemToArea(SectName:String;N:integer);
var
  strContent,strData,str:String;
  ListItem: TListItem;
  bitn:integer;
  msk:word;
  procedure InitDataGrids(N:integer);
  Var K:Cardinal;
  begin
    with frmMain do
    begin
    bitn:=AreasData[N].DataArr[2];
    msk:=GetDataMask(bitn);
    if AreasData[N].strID='CODE' then
      begin
      TabSheet1.Caption:=AreasData[N].strCapt;
      CodeGrid.BegAddr:=AreasData[N].DataArr[0];
      K:=(AreasData[N].DataArr[1]-AreasData[N].DataArr[0]+1)*Ceil(bitn/ 8);
      CodeGrid.DataCount:=K;
      CodeGrid.DataSize:=TDataSize(Ceil(bitn/8)-1);
      if Ceil(bitn/8)>1 then
        CodeGrid.DefaultData:=msk
      else
        CodeGrid.DefaultData:=msk or (msk shl 8);
      edChipTo.MaxValue:= LongInt(CodeGrid.BegAddr)+ CodeGrid.DataCount-1;
      edChipFrom.MaxValue:=edChipTo.MaxValue;
      end;
    if AreasData[N].strID='EEPROM' then
      begin
      frmMain.TabSheet2.TabVisible:=true;
      frmMain.TabSheet2.Caption:=AreasData[N].strCapt;
      EEPROMGrid.BegAddr:=AreasData[N].DataArr[0];
      K:=(AreasData[N].DataArr[1]-AreasData[N].DataArr[0]+1)*Ceil(bitn/8);
      EEPROMGrid.DataCount:=K;
      EEPROMGrid.DataSize:=TDataSize(Ceil(bitn/8)-1);
      if Ceil(bitn/8)>1 then
        EEPROMGrid.DefaultData:=msk
      else
        EEPROMGrid.DefaultData:=msk or (msk shl 8);

      end;
    UpdateStatusBar;
    end;
  end;


begin
  strContent:= MemIniFile.ReadString(sectName,strA+IntToStr(N)+strC,'');
  if strContent<>'' then
  begin
    str:=Trim(ExtractWord(1, strContent,[strDELIMTR]));
    if str<>'' then
    begin
      ListItem:=lsvAreas.Items.Add;  // взять указатель на новый созданный элемент
      ListItem.Caption := str;       // присвоить название
      ListItem.SubItems.Add(Trim(ExtractWord(2, strContent,[strDELIMTR])));
      strData:= MemIniFile.ReadString(sectName,strA+IntToStr(N)+strD,'');
      // Запонить данные асоциированные с программируемой областью
      FillAreasData(N-1,strData,str);
      // инициализировать сетку данных если необходимо
      InitDataGrids(N-1);
    end;
    InsertItemToArea(SectName,N+1);

  end;
end;

//  Заполнение данных асоциированных с областями программирования
//
procedure TfrmDevice.FillAreasData(Indx:Integer;strData:string;aCaption:string);
var str:string;
    K,i:Integer;
begin
  K:=Indx-Length(AreasData)+1;  // Проверить, нужно ли увеличить длину массива
  if K>0 then SetLength(AreasData,Length(AreasData)+K);
  AreasData[Indx].strCapt:=aCaption;
  if strData<>'' then
  begin
    K:=WordCount(strData,[strDDELIMTR]);
    for i:=1 to K do
    begin
      str:=Trim(ExtractWord(i, strData,[strDDELIMTR]));
      try
        if i=1 then
          AreasData[indx].strID:=str
        else
          AreasData[Indx].DataArr[i-2]:=StrToInt(str);
      except
        AreasData[Indx].DataArr[i-2]:=0;
      end;
    end;
  end;
end;




// назначение узлу необходимой пиктограммы
procedure TfrmDevice.InsPictToNode(TreeNode:TTreeNode;str:String);
begin
  if str<>'' then
  begin
    TreeNode.ImageIndex:=StrToInt(str);
    TreeNode.SelectedIndex:=StrToInt(str);
    TreeNode.StateIndex:=-1;
  end
  else
  begin
    TreeNode.ImageIndex:=-1;
    TreeNode.SelectedIndex:=-1;
    TreeNode.StateIndex:=-1;
  end;
end;

//  Заполнение древовидного списка элементами
//  каждый элемент представляет определенный параметр программирования
procedure TfrmDevice.InsertItemToParams(SectName:String;N:integer);
var
  strContent,strData,str,strCapt:String;
  TreeNode: TTreeNode;
  K:integer;
begin
  strContent:= MemIniFile.ReadString(sectName,strP+IntToStr(N)+strC,'');
  if strContent<>'' then
  begin
    strCapt:=Trim(ExtractWord(1, strContent,[strDELIMTR]));
    TreeNode:= trvParameters.Items.Add(nil, strCapt);

    {добавить пиктограмму к субузлу}
    str:=MemIniFile.ReadString(sectName,strP+IntToStr(N)+strI,'');
    InsPictToNode(TreeNode,str);

    { связать узел с вектором данных }
    strData:=MemIniFile.ReadString(sectName,strP+IntToStr(N)+strD,'');

    // Сохранить данные связанные с узлом
    K:=TreeNode.AbsoluteIndex-Length(NodesData)+1;
    // Проверить, нужно ли увеличить длину массива
    if K>0 then SetLength(NodesData,Length(NodesData)+K);
    K:=High(NodesData);

    NodesData[K].strCapt:=strCapt;
    NodesData[K].strID:= Trim(ExtractWord(2, strContent,[strDELIMTR]));
    NodesData[K].strAreaID:=Trim(ExtractWord(3, strContent,[strDELIMTR]));

    InsertSubItemsToParams(TreeNode,SectName,strP+IntToStr(N)+strS,1);

    { Присвоить узлу данные первого субузла }
    with TreeNode do
      if GetFirstChild<>nil then
      begin
        Text:=NodesData[AbsoluteIndex].strCapt+': '+item[0].Text;
        ImageIndex:=item[0].ImageIndex;
        SelectedIndex:=item[0].SelectedIndex;
        NodesData[K].intSelSubIt:=item[0].AbsoluteIndex;
      end;


    InsertItemToParams(SectName,N+1);
  end;
end;




procedure TfrmDevice.InsertSubItemsToParams(TreeNode:TTreeNode;SectName:String;Key:string;N:integer);
var
  strContent,strData,str:String;
  ChildTreeNode:TTreeNode;
begin
  strContent:= MemIniFile.ReadString(sectName,key+IntToStr(N),'');
  if strContent<>'' then
  begin
    ChildTreeNode:=trvParameters.Items.AddChild(TreeNode, strContent);

    {добавить пиктограмму к субузлу}
    str:=MemIniFile.ReadString(sectName,key+IntToStr(N)+strI,'');
    InsPictToNode(ChildTreeNode,str);

    { связать субузел с вектором данных }
    strData:=MemIniFile.ReadString(sectName,key+IntToStr(N)+strD,'');
//    InsVectorToDinArray(SubNodesData,ChildTreeNode.AbsoluteIndex,strData);
    FillSubNodesData(ChildTreeNode.AbsoluteIndex,strData,'');
    InsertSubItemsToParams(TreeNode,SectName,Key,N+1);
  end;
end;

procedure TfrmDevice.FillSubNodesData(Indx:Integer;strData:string;aCaption:string);
var str:string;
    K,i:Integer;
begin
  K:=Indx-Length(NodesData)+1;  // Проверить, нужно ли увеличить длину массива
  if K>0 then SetLength(NodesData,Length(NodesData)+K);
  NodesData[Indx].strCapt:=aCaption;
  if strData<>'' then
  begin
    K:=WordCount(strData,[strDDELIMTR]);
    for i:=1 to K do
    begin
      str:=Trim(ExtractWord(i, strData,[strDDELIMTR]));
      try
        case i of
        1: NodesData[indx].strMask:=str;
        2: NodesData[indx].strMask2:=str;
        else
          NodesData[Indx].DataArr[i-3]:=StrToInt(str);
        end;
      except
        NodesData[Indx].DataArr[i-2]:=0;
      end;
    end;
  end;
end;


// разложение строки на составляющие представляющие собой целые числа
// сохранение чисел в динамическом двумерном массиве
{
procedure TfrmDevice.InsVectorToDinArray(var DinArray:TDinArray;Indx:Integer;strData:string);
var str:string;
    K,i:Integer;
begin
  K:=Indx-Length(DinArray)+1;  // Проверить, нужно ли увеличить длину массива
  if K>0 then SetLength(DinArray,Length(DinArray)+K);
  if strData<>'' then
  begin
    K:=WordCount(strData,[strDDELIMTR]);
    SetLength(DinArray[Indx],K);
    for i:=1 to K do
    begin
      str:=Trim(ExtractWord(i, strData,[strDDELIMTR]));
      try
        DinArray[Indx][i-1]:=StrToInt(str);
      except
        DinArray[Indx][i-1]:=0;
      end;
    end;
  end;
end;
}

procedure TfrmDevice.lsvAreasInfoTip(Sender: TObject; Item: TListItem;
  var InfoTip: String);
begin
  InfoTip:=Item.SubItems.Text;
end;

procedure TfrmDevice.trvParametersClick(Sender: TObject);
begin
  if trvParameters.Selected.Level=1 then
//  with trvParameters.Selected do
//  begin
    SelectSubNode(trvParameters.Selected.parent,trvParameters.Selected);
//    Parent.Text:=NodesData[Parent.AbsoluteIndex].strCapt+': '+Text;
//    Parent.ImageIndex:=ImageIndex;
//    Parent.SelectedIndex:=SelectedIndex;
//    NodesData[Parent.AbsoluteIndex].intSelSubIt:=AbsoluteIndex;
//  end;

end;

procedure TfrmDevice.SelectSubNode(Node,SubNode:TTreeNode);
begin
  Node.Text:=NodesData[Node.AbsoluteIndex].strCapt+': '+SubNode.Text;
  Node.ImageIndex:=SubNode.ImageIndex;
  Node.SelectedIndex:=SubNode.SelectedIndex;
  NodesData[Node.AbsoluteIndex].intSelSubIt:=SubNode.AbsoluteIndex;
end;


Function TfrmDevice.FindNodeIndex(ID:string;var indx:integer):boolean;
var i:integer;
begin
  result:=false;
  for i:=0 to High(NodesData) do
    if NodesData[i].strID=ID then
    begin
      result:=true;
      indx:=i;
      exit;
    end;
end;


procedure TfrmDevice.CheckSendThread;
begin
  if FSendThread<>nil then
  begin
    Abort
  end
  else
  begin
    FSendThread:=TSendThread.Create(true,CommPort);
    FSendThread.OnEndProcess:= FreeSendThread;
  end;
end;



procedure TfrmDevice.InitChipClass;
begin
  if not InitRequired then
  begin
    ChipClassInitialised:=true;
    exit;
  end;
  if ChipClassInitialised then exit;
  frmConnecting:=TfrmConnecting.Create(Self);
  frmConnecting.FfrmDevice:=Self;
  frmConnecting.ShowModal;
  if not ChipClassInitialised then abort;
end;


procedure TfrmDevice.tbReadClick(Sender: TObject);
begin
  InitChipClass;
  frmInProgress:=TfrmInProgress.Create(Self);
  with frmInProgress do
  begin
    FfrmDevice:=Self;
    lbStatus.Caption:='Reading';
    lbStatus.font.Color:=clLime;
    ShowModal;
  end;
  frmMain.HexGrid.Invalidate;
end;

procedure TfrmDevice.tbVerifyClick(Sender: TObject);
begin
  InitChipClass;
  frmInProgress:=TfrmInProgress.Create(Self);
  with frmInProgress do
  begin
    FfrmDevice:=Self;
    lbStatus.Caption:='Verifing';
    lbStatus.font.Color:=clYellow;
    ShowModal;
  end;
end;


procedure TfrmDevice.tbWriteClick(Sender: TObject);
begin
  InitChipClass;
  frmInProgress:=TfrmInProgress.Create(Self);
  with frmInProgress do
  begin
    FfrmDevice:=Self;
    lbStatus.Caption:='Writing';
    lbStatus.font.Color:=clRed;
    ShowModal;
  end;
end;


procedure TfrmDevice.tbEraseClick(Sender: TObject);
begin
  InitChipClass;
  frmInProgress:=TfrmInProgress.Create(Self);
  with frmInProgress do
  begin
    FfrmDevice:=Self;
    lbStatus.Caption:='Erasing';
    lbStatus.font.Color:=clFuchsia;
    ShowModal;
  end;
end;

procedure TfrmDevice.tbBlankCheckClick(Sender: TObject);
begin
  InitChipClass;
  frmInProgress:=TfrmInProgress.Create(Self);
  with frmInProgress do
  begin
    FfrmDevice:=Self;
    lbStatus.Caption:='Blank checking';
    lbStatus.font.Color:=clWhite;
    ShowModal;
  end;
end;

procedure TfrmDevice.tbStopClick(Sender: TObject);
begin
  ChipClassInitialised:=false;
  InitChipClass;
//
end;

// ============================================================================
//
//  Procedures called from SendThread
//
// ============================================================================
//

procedure TfrmDevice.ReadDataToGrid(ArIndx:integer; // индекс области программирования
                         Grid:THexGrid;        // текущая сетка данных
                         sfxStr:string;        // дополнительный суффикс к команде программатора
                         len:byte              // количество принимаемых байт
                         );                    // для формирования одного байта
                                               // данных
var
  cmdStr,str:string;
  firstAddr,lastAddr:Word;
  InBuf:TThreadBuf;
  Cnt:word;
  SizeCoeff:byte;
  indx:Longint;
  bufStr:string;
  k:Cardinal;
begin
  with FSendThread do
  begin
    Synchronize(frmInProgress.InitGauge);
    indx:=0;
    bufStr:='';
    firstAddr:=AreasData[ArIndx].DataArr[0];
    lastAddr:=AreasData[ArIndx].DataArr[1];
    SizeCoeff:= AreasData[ArIndx].DataArr[2];
    k:= (lastAddr-firstAddr+1)*SizeCoeff;
    cmdStr:='R '+ format('%.4x %.4x',[firstAddr,lastAddr])+ sfxStr + #13;
    WriteStrToComm(cmdStr);
    ReadNChar(str,Length(cmdStr));
    if cmdStr<>str then
       Raise Exception.Create(strIsNotAnswer);

    repeat
      Cnt:=ReadUntilChar(InBuf);
      if cnt>0 then
      begin

        if not DecodeReceivedBuf(bufStr,InBuf,Cnt,Grid.DataBufer,Indx,len) then
          Cnt:=0;
        frmInProgress.ProgrPos:=Round(100*indx/k);
        Synchronize(frmInProgress.UpdateGauge);
      end;
    until Cnt=0;
  end;
end;


procedure TfrmDevice.ReadDataToBuf(ArIndx:integer;// индекс области программирования
                  var DataBuf: array of byte;// буфер данных
                     sfxStr:string;          // дополнительный суффикс к команде программатора
                     len:byte);              // количество принимаемых байт
                                             // для формирования одного байта
                                             // данных
var
  cmdStr,str:string;
  firstAddr,lastAddr:Word;
  InBuf:TThreadBuf;
  Cnt:word;
  indx:Longint;
  bufStr:string;
begin
  with FSendThread do
  begin
    indx:=0;
    bufStr:='';
    firstAddr:=AreasData[ArIndx].DataArr[0];
    lastAddr:=AreasData[ArIndx].DataArr[1];
    cmdStr:='R '+ format('%.4x %.4x',[firstAddr,lastAddr])+ sfxStr + #13;
    WriteStrToComm(cmdStr);
    ReadNChar(str,Length(cmdStr));
    if cmdStr<>str then
       Raise Exception.Create(strIsNotAnswer);

    repeat
      Cnt:=ReadUntilChar(InBuf);
      if cnt>0 then
      begin
        if not  DecodeReceivedBufToArray(bufStr,InBuf,Cnt,DataBuf,Indx,len) then
          Cnt:=0;
      end;
    until Cnt=0;
  end;

end;


Procedure TfrmDevice.WriteDataFromGrid(ArIndx:integer;
                                             Grid:THexGrid;
                                             sfxStr:string;
                                             len:byte);
var
  cmdStr,str:string;
  firstAddr,lastAddr:Word;
  k:Cardinal;
  Addr,Cnt,indx:Longint;
  SizeCoeff:byte;
begin
  with FSendThread do
  begin
    Synchronize(frmInProgress.InitGauge);
    SizeCoeff:= AreasData[ArIndx].DataArr[2];
    firstAddr:=AreasData[ArIndx].DataArr[0];
    lastAddr:=AreasData[ArIndx].DataArr[1];
    cmdStr:='W '+ format('%.4x %.4x',[firstAddr,lastAddr])+ sfxStr + #13;
    // Перевести в исчисление в байтах
    firstAddr:=AreasData[ArIndx].DataArr[0]*SizeCoeff;
    lastAddr:=(AreasData[ArIndx].DataArr[1]+1)*SizeCoeff-1;
    k:= (lastAddr-firstAddr+1);

    WriteStrToComm(cmdStr);
    ReadNChar(str,Length(cmdStr)+3);
    if Pos(cmdStr,str)=0 then
       Raise Exception.Create(strIsNotAnswer);

    cmdStr:='';
    cnt:=0;
    indx:=0;
    Addr:=firstAddr;
    repeat
      cmdStr:=cmdStr+format('%.'+IntToStr(len)+'x',[Grid.Adata[Addr]]);
      inc(indx);
      inc(cnt);
      if (cnt=16) or (Addr>=lastAddr) then
      begin
        cmdStr:=cmdStr+#13;
        WriteStrToComm(cmdStr);
        ReadNChar(str,Length(cmdStr)+3);
        if Pos(cmdStr,str)=0 then
          Raise Exception.Create(strIsNotValidAnswer);
        cmdStr:='';
        Cnt:=0;
        frmInProgress.ProgrPos:=Round(100*indx/k);
        Synchronize(frmInProgress.UpdateGauge);
      end;
      inc(Addr);
    until Addr>lastAddr;
  end;
end;

procedure TfrmDevice.WriteDataFromStr(ArIndx:integer;st:string;sfxStr:string);
var
  cmdStr,str:string;
  firstAddr,lastAddr:Word;
begin
  with FSendThread do
  begin
    // Перевести в исчисление в байтах
    firstAddr:=AreasData[ArIndx].DataArr[0];
    lastAddr:=AreasData[ArIndx].DataArr[1];
    cmdStr:='W '+ format('%.4x %.4x',[firstAddr,lastAddr])+ sfxStr + #13;
    WriteStrToComm(cmdStr);
    ReadNChar(str,Length(cmdStr)+3);
    if (cmdStr+#13+#10+'-')<>str then
       Raise Exception.Create(strIsNotAnswer);
    cmdStr:=st+#13;
    WriteStrToComm(cmdStr);
    ReadNChar(str,Length(cmdStr)+3);
    if (cmdStr+#13+#10+'>')<>str then
      Raise Exception.Create(strIsNotValidAnswer);
  end;
end;

function  TfrmDevice.GetDataMask(bitn:integer):word;
var i:integer;
    m:word;
begin
  m:=0;
  for i:=0 to bitn-1 do
    m:=m or (1 shl i);
  GetDataMask:=m;
end;

procedure TfrmDevice.FreeSendThread;
begin
  FSendThread:=nil;
end;



end.
