⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 uniprotocol_downloader.pas

📁 很好用的串口通信工具软件。Comport目录下是用到的通信控件。
💻 PAS
📖 第 1 页 / 共 2 页
字号:
unit UniProtocol_DownLoader;

{----------------------------------------------
         UniProtocol_DownLoader

         通信座协议插件
           Ver 1.04

  Copyright 1999-2001 AT Corp.Ltd
-----------------------------------------------}

interface

uses
  UniCommX, SysUtils, Classes, Windows;

type

  PUndoneBuff = ^TUndoneBuff;
  TUndoneBuff = record
    Tag: string; //本次传送通信座的特征值
    Address_From: Integer; //数据起始地址
    Address_To: Integer; //数据终止地址
    DownLoaderSN: string; //本次通信通信座序列号
    ProbeSN: string; //本次通信识读器序列号
    Data: string; //已接收到的数据
  end;

  TDownLoader = class(TCustomProtocolExecutor)
  private
    FSenderSerialNO: string;
    FHostCmd: THostCommand;
    FFrameNo: Integer;
    FFrame_Size: Integer;
    FHZKBuff: TMemoryStream;
    FCurData: string;
    FCurGUID: string;
    Address_From, Address_To, ResumeAddress_From, ResumeItem, curAddress_From, curAddress_To, last_FrameSize, curFrame_Size: Integer;
    DownLoader_Tag: string;
    FUndoneBuff: PUndoneBuff;
    FStartTransTime: Cardinal;
    function AdjustTimeCommand: string;
    function AdjustPhoneCodeCommand(Phone: string): string;
    function ParseData(FCurData: string): string;
  public
    constructor Create(SenderID, ProtocolVersion: Integer; Hwnd: THandle); override;
    destructor Destroy; override;
    class function ProtocolImplemented(ProtocolType: Integer; ProtocolVersion: Integer; isCommander: Boolean): Boolean; override;
    class function SaveUncompletedData: Boolean; override;
    function ProcessProtocol(var FCommState: Integer; SendResult: Integer; Packet: string; Parameters: TStrings): THostCommand; override;
  published
    property Datas;
  end;

implementation

const

  csACK = #$A5#$A5; // csACK: 确认
  csNAK = #$5A#$5A; // csNAK: 否定回应
  CRLF = #13#10;
  Band_Size = 32 * 1024; //通信座存储数据分界点。如果在此Band尾部不能存放一整条数据,则转下一Band起始存放

var
  UnDoneList: TThreadList;

{-------------------  TDownLoader   -----------------------------}

constructor TDownLoader.Create(SenderID, ProtocolVersion: Integer; Hwnd: THandle);
begin
  inherited;
  FHzkBuff := TMemoryStream.Create;
  if ProtocolVersion = 2 then
    FFrame_Size := 80
  else
    FFrame_Size := 160;
end;

destructor TDownLoader.Destroy;
begin
  FHzkBuff.Free;
  inherited;
end;

{--------------------------------------------------------------------------
class function TDownLoader.ProtocolImplemented
功能: 判断是否实现了特定协议类型以及协议版本的通信协议。
输入: ProtocolType: Integer;   协议类型,即发送者标识
       ProtocolVersion: Integer 协议版本
输出: boolean 是否实现协议
---------------------------------------------------------------------------}

class function TDownLoader.ProtocolImplemented(ProtocolType: Integer; ProtocolVersion: Integer; isCommander: Boolean): Boolean;
begin
  Result := (ProtocolType = cnDownLoaderID) and isCommander;
end;

{--------------------------------------------------------------------------
class function TDownLoader.SaveUncompletedData
功能: 判断是否要保存通讯没有完全成功时接收到的数据
输入: 无
输出: boolean 是否保存
---------------------------------------------------------------------------}

class function TDownLoader.SaveUncompletedData: Boolean;
begin
  Result := False;
end;

{--------------------------------------------------------------------------
function TDownLoader.AdjustTimeCommand: string;
功能: 生成调节时间的命令
输入: 无
输出: string 命令
---------------------------------------------------------------------------}


function TDownLoader.AdjustTimeCommand: string;
var
  Year, Month, day, Hour, Min, Sec, Msec: Word;
begin
  decodeDate(now, Year, Month, day);
  decodeTime(now, Hour, Min, Sec, msec);
  Year := Year mod 100;
  SetLength(Result, 8);
  Result[1] := #$FD;
  Result[2] := Chr(convBCDToHex(Sec));
  Result[3] := Chr(convBCDToHex(Min));
  Result[4] := Chr(convBCDToHex(Hour));
  Result[5] := Chr(convBCDToHex(day));
  Result[6] := Chr(convBCDToHex(Month));
  Result[7] := Chr(convBCDToHex(Year));
  Result[8] := Chr(GenerateCRC(Result[1], 7));
end;

function TDownLoader.AdjustPhoneCodeCommand(Phone: string): string;
var
  cmdLen: Integer;
begin
  cmdLen := length(Phone) + 3;
  SetLength(Result, cmdLen);
  Result[1] := #$FC;
  Result[2] := Chr(cmdLen);
  Move(Phone[1], Result[3], Length(Phone));
  Result[cmdLen] := Chr(GenerateCRC(Result[1], cmdLen - 1));
end;

function TDownLoader.ParseData(FCurData: string): string;
var
  i, j, k, UnitSize, ItemCount, UnitCount: Integer;
  Data, TableNo, Year, Mon, Day, Hour, Min, Sec: string;
  AUnit, ProbeSN, ProbeFamily, IBSN: string;
  NullData1, NullData2: string;
begin
  Result := '';
  ProbeSN := FDatas.Values['ProbeSerialNo'];
  ProbeFamily := Copy(ProbeSN, 3, 2);
  if (ProbeFamily = '01') or (ProbeFamily = '02') then
  begin // 铝壳识读器
    TableNo := 'FF';
    UnitSize := 16;
    NullData1 := StringOfChar(#0, UnitSize);
    NullData2 := StringOfChar(#$FF, UnitSize);
    for i := 0 to (Length(FCurData) div UnitSize) - 1 do
    begin
      AUnit := Copy(FCurData, i * UnitSize + 1, UnitSize);
      if (AUnit = NullData1) or (AUnit = NullData2) or (AUnit = '') then
        Continue;
      IBSN := '';
      for j := 7 downto 2 do
        IBSN := IBSN + IntToHex(Ord(AUnit[j]), 2);
      Year := IntToHex(Ord(AUnit[9]), 2);
      Mon := IntToHex(Ord(AUnit[10]), 2);
      Day := IntToHex(Ord(AUnit[11]), 2);
      Hour := IntToHex(Ord(AUnit[12]), 2);
      Min := IntToHex(Ord(AUnit[13]), 2);
      Result := Result +  'DATA=' + ProbeSN + ',' + IBSN + ',' +
        Year + ',' + Mon + ',' + Day + ',' + Hour + ',' + Min + ',00,' + TableNo + ',0,' + CRLF;
    end;
  end
  else if (ProbeFamily = '04') then
  begin // 新型25项识读器数据
    UnitSize := 96;
    NullData1 := StringOfChar(#0, UnitSize);
    NullData2 := StringOfChar(#$FF, UnitSize);
    for i := 1 to Length(FCurData) div UnitSize do
    begin
      AUnit := Copy(FCurData, (i - 1) * UnitSize + 1, UnitSize);
      if (AUnit = NullData1) or (AUnit = NullData2) then
        Continue;
      IBSN := '';
      for j := 1 to 6 do
        IBSN := IBSN + IntToHex(Ord(AUnit[j]), 2);
      Year := IntToHex(Ord(AUnit[7]), 2);
      Mon := IntToHex(Ord(AUnit[8]), 2);
      Day := IntToHex(Ord(AUnit[9]), 2);
      Hour := IntToHex(Ord(AUnit[10]), 2);
      Min := IntToHex(Ord(AUnit[11]), 2);
      Sec := IntToHex(Ord(AUnit[12]), 2);
      TableNo := IntToHex(Ord(AUnit[13]), 2); // 汉字识读器数据中有表格编号
      Data := '';
      for j := 1 to 5 do
        for k := 0 to 4 do
          Data := Data + IntToHex(Ord(AUnit[j * 16 + k * 3 + 1]), 2) + IntToHex(Ord(AUnit[j * 16 + k * 3 + 2]), 2) + IntToHex(Ord(AUnit[j * 16 + k * 3 + 3]), 2) + ',';
      Result := Result +  'DATA=' + ProbeSN + ',' + IBSN + ',' +
        year + ',' + mon + ',' + day + ',' + Hour + ',' + min + ',' + Sec + ',' + TableNo + ',25,' + Data + CRLF;
    end;
  end
  else if (ProbeFamily = '05') then
  begin // 新型单项数据识读器
    TableNo := 'FF';
    UnitSize := 16;
    NullData1 := StringOfChar(#0, UnitSize);
    NullData2 := StringOfChar(#$FF, UnitSize);
    for i := 1 to Length(FCurData) div UnitSize do
    begin
      AUnit := Copy(FCurData, (i - 1) * UnitSize + 1, UnitSize);
      if (AUnit = NullData1) or (AUnit = NullData2) then
        Continue;
      IBSN := '';
      for j := 1 to 6 do
        IBSN := IBSN + IntToHex(Ord(AUnit[j]), 2);
      Data := IntToHex(Ord(AUnit[7]), 2) + IntToHex(Ord(AUnit[8]), 2) + IntToHex(Ord(AUnit[9]), 2) + IntToHex(Ord(AUnit[10]), 2);
      Year := IntToHex(Ord(AUnit[11]), 2);
      Mon := IntToHex(Ord(AUnit[12]), 2);
      Day := IntToHex(Ord(AUnit[13]), 2);
      Hour := IntToHex(Ord(AUnit[14]), 2);
      Min := IntToHex(Ord(AUnit[15]), 2);
      Result := Result + 'DATA=' +  ProbeSN + ',' + IBSN + ',' +
        year + ',' + mon + ',' + day + ',' + Hour + ',' + min + ',00,' + TableNo + ',1,' + Data + ',' + CRLF;
    end;
  end
  else if (ProbeFamily = '06') then
  begin // 消防识读器数据
    TableNo := 'FF';
    UnitSize := 16;
    NullData1 := StringOfChar(#0, UnitSize);
    NullData2 := StringOfChar(#$FF, UnitSize);
    for i := 1 to Length(FCurData) div UnitSize do
    begin
      AUnit := Copy(FCurData, (i - 1) * UnitSize + 1, UnitSize);
      if (AUnit = NullData1) or (AUnit = NullData2) then
        Continue;
      IBSN := '';
      for j := 1 to 6 do
        IBSN := IBSN + IntToHex(Ord(AUnit[j]), 2);
      Data := IntToHex(Ord(AUnit[7]), 2) + IntToHex(Ord(AUnit[8]), 2) + IntToHex(Ord(AUnit[9]), 2) + IntToHex(Ord(AUnit[10]), 2);
      Year := IntToHex(Ord(AUnit[11]), 2);
      Mon := IntToHex(Ord(AUnit[12]), 2);
      Day := IntToHex(Ord(AUnit[13]), 2);
      Hour := IntToHex(Ord(AUnit[14]), 2);
      Min := IntToHex(Ord(AUnit[15]), 2);
      Result := Result + 'DATA=' + ProbeSN + ',' + IBSN + ',' +
        year + ',' + mon + ',' + day + ',' + Hour + ',' + min + ',00,' + TableNo + ',1,' + Data + ',' + CRLF;
    end;
  end
  else if (ProbeFamily = '07') or (ProbeFamily = '08') then
  begin // 新型单表多项识读器数据
    TableNo := 'FF';
    UnitSize := 16;
    NullData1 := StringOfChar(#0, UnitSize);
    NullData2 := StringOfChar(#$FF, UnitSize);
    i := 1;
    while i < Length(FCurData) do
    begin
      AUnit := Copy(FCurData, i, UnitSize);
      if (AUnit = NullData1) or (AUnit = NullData2) then
        break;
      IBSN := '';
      for j := 1 to 6 do
        IBSN := IBSN + IntToHex(Ord(AUnit[j]), 2);
      Year := IntToHex(Ord(AUnit[9]), 2);
      Mon := IntToHex(Ord(AUnit[10]), 2);
      Day := IntToHex(Ord(AUnit[11]), 2);
      Hour := IntToHex(Ord(AUnit[12]), 2);
      Min := IntToHex(Ord(AUnit[13]), 2);
      Sec := IntToHex(Ord(AUnit[14]), 2);
      ItemCount := Ord(AUnit[7]);   // 汉字识读器数据中总项数
      UnitCount := (ItemCount+2) div 3;
      TableNo := IntToHex(Ord(AUnit[2]), 2);

      AUnit := '';
      for j := 1 to UnitCount do
      begin
        AUnit := AUnit + Copy(FCurData, i+j*16, 15);
      end;
      Inc(i, UnitSize); // 数据头
      Inc(i, UnitCount* UnitSize); // 项值

      Data := '';
      for j:=0 to ItemCount-1 do
          Data := Data + IntToHex(Ord(AUnit[j * 5 + 1]), 2) +
                  IntToHex(Ord(AUnit[j * 5 + 2]), 2) +
                  IntToHex(Ord(AUnit[j * 5 + 3]), 2) +
                  IntToHex(Ord(AUnit[j * 5 + 4]), 2) +
                  IntToHex(Ord(AUnit[j * 5 + 5]), 2) + ',';
      Result := Result + 'DATA=' + ProbeSN + ',' + IBSN + ',' +
        year + ',' + mon + ',' + day + ',' + Hour + ',' + min + ',' + Sec + ',' + TableNo + ',' + IntToStr(ItemCount) + ',' + Data + CRLF;
    end;
  end
end;

{--------------------------------------------------------------------------
function TDownLoader.ProcessProtocol
功能: 处理当前通信状态,并生成新的命令
输入: var FCommState: Integer;  当前的通信状态
       SendResult: Integer;            上一次的发送结果
       Packet: string;                 上一次的数据
       Parameters: TStrings            通信参数
输出: THostCommand 存放新的命令
       var FCommState: Integer;  存放下一步的通信状态
---------------------------------------------------------------------------}
{
命令及应答:

注:◆为主机命令    ⊙为通信座应答  【】内为注解
    csACK = 0xA5 + 0xA5   csNAK = 0x5A + 0x5A

    ● 握手
        ◆ 0x53 + 0x57 + 0x41 + 0x54 (‘SWAT’)
        ⊙ csACK  + 2B(传送者标识) + 2B(协议版本号) + CRC
    ● 读取通信座数据信息
        ◆ 0xFE + 0x10 + CRC
        ⊙ csACK  + 3B(首地址) + 3B(末地址) + 16B(特征值) + 4B(通信座号) + 4B(识读器号) + CRC
           【首地址指通信座内有效数据的起始地址。末地址为有效数据的终止地址。
            如果通信座内有多块数据芯片,则地址必须连续】
    ● 校正识读器时间
        ◆ 0xFD + SSMMHHDDMMYY + CRC
        ⊙ csACK  + 0xFD + CRC           【ACK】
        ⊙ csNAK  + 0xFD + CRC           【NAK】
    ● 校正通信座号码
        ◆ 0xFC + 1B(号码长度) + 号码 + CRC
        ⊙ csACK  + 0xFC + CRC           【ACK】
        ⊙ csNAK  + 0xFC + CRC           【NAK】
    ● 请求数据
       ◆ 0xFB + 3B(首地址) + 3B(末地址) + CRC
       ⊙ csACK  + 3B(首地址) + N*16B(数据) + CRC
          【数据长度必须是16的整数倍。 N <= 10】
    ● 校正汉字库 【强行校正】
       ◆ 0xF9 + 3B(地址) + CRC + 64B(数据) + CRC
       ⊙ csACK  + 3B(地址) + CRC     【ACK】
       ⊙ csNAK  + 3B(地址) + CRC     【NAK】
    ● 主机要求挂机
       ◆ 0xF8 + 16B(特征值) + CRC
       ⊙ csACK  + 0xF8 + CRC
【通信座连续2次收到挂机命令,且校验正确,应答后挂机,否则可以处理其他命令。
主机收到通信座第二次应答后挂机,否则连续发送10次后挂机】。

注:
1、 固定数据取CRC只是为延长命令长度,减少“正确误码”或为处理方便。
2、 当主机没有收到应答时,要求重发都由主机来执行。
3、 16字节特征值为主机生成的随机数。每次通信成功后,通信座应保存此随机数。
4、 主机发送的所有命令前都额外加       0XFFFF 2个字节,不计其CRC
5、 通信座发给主机的一切应答前都额外加 0XFFFF 2个字节,不计其CRC
6、 通信座发送数据时,如没有发完数据时收到挂机命令,如果确认是挂机,则认为为通信失败,挂机后重新连接发送数据。
7、 普通通信中无校正汉字库命令。如需校正,流程为:握手—>校正—>挂机
8、 传送者标识为传送者类别代码。如通信座为 0x00+0x01,电子门锁可能为 0x00+0x02等等
9.  串口速率为57600bps
通信座数据存储说明:
 通信座数据存储以32K为分隔,如果前32K尾部不能存储一整条数据,则跳到下一个32K首地址开始存数据。
主机读取通信座数据时,以32K为分隔,即一帧数据当中不能跨32K或64K。
}

function TDownLoader.ProcessProtocol(var FCommState: Integer; SendResult: Integer; Packet: string; Parameters: TStrings): THostCommand;
const
  csAdjustHZK = csUser + 1;
  csGetDataInfo = csUser + 2;
  csAdjustTime = csUser + 3;
  csAdjustPhoneCode = csUser + 4;
  csRequestData = csUser + 5;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -