📄 uniprotocol_downloader.pas
字号:
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 + -