📄 uniprotocol_cutehouse.pas
字号:
unit UniProtocol_CuteHouse;
{----------------------------------------------
UniProtocol_CuteHouse
无人值守野外机房协议插件
Ver 1.02
Copyright 1999-2001 AT Corp.Ltd
-----------------------------------------------}
interface
uses
UniCommX, SysUtils, Classes, Windows;
type
TEnvData = record
dtTime: TdateTime;
VoltageAC: Integer; // 交流电压
VoltageDC: Integer; // 直流电压
Humidity: Integer; // 湿度
DoorState: Integer; // 门禁状态
FanState: Integer; // 风扇状态 0-关 1-开
AirConditionState: Integer; // 空调状态 0-关 1-开
LightState: Integer; // 有无雷击 0-有 1-无
WaterState: Integer; // 是否进水 0-否 1-是
SmogState: Integer; // 有无烟雾 0-无 1-有
Temperatures: array[1..7] of Integer; // 温度
end;
TDoorData = record
dtTime: tdateTime;
CardSerial: string;
ReaderNO: Integer;
DoorState: Integer;
Reserved: array[1..4] of Integer;
end;
TAlertData = record
VoltageAC: Integer;
VoltageDC: Integer;
Humidity: Integer;
DoorState: Integer;
FanState: Integer;
AirConditionState: Integer;
CableState: array[1..8] of Integer;
Temperatures: array[1..7] of Integer;
Reserved: array[1..4] of Integer;
end;
TCableData = record
CableLength: array[1..8] of Integer;
end;
TCuteHouse = class(TCustomProtocolExecutor)
private
FSenderSerialNO: string;
FHostCmd: THostCommand;
FFrameNo: Integer;
FDataCount: Integer;
function AdjustTimeCommand: string;
public
class function ProtocolImplemented(ProtocolType: Integer; ProtocolVersion: Integer; isCommander: Boolean): Boolean; override;
class function SaveUncompletedData: Boolean; override;
class function ParseEnvData(Data: string): TEnvData;
class function ParseDoorData(Data: string): TDoorData;
class function ParseAlertData(Data: string): TAlertData;
class function ParseCableData(Data: string): TCableData;
function ProcessProtocol(var FPacketCommState: Integer; SendResult: Integer; Packet: string; Parameters: TStrings): THostCommand; override;
published
property Datas;
end;
implementation
const
csACK = #$A5#$A5; // ACK: 确认
csNAK = #$5A#$5A; // NAK: 否定回应
{------------------- TCuteHouse -----------------------------}
{--------------------------------------------------------------------------
class function TCuteHouse.ProtocolImplemented
功能: 判断是否实现了特定协议类型以及协议版本的通信协议。
输入: ProtocolType: Integer; 协议类型,即发送者标识
ProtocolVersion: Integer 协议版本
输出: boolean 是否实现协议
---------------------------------------------------------------------------}
class function TCuteHouse.ProtocolImplemented(ProtocolType: Integer; ProtocolVersion: Integer; isCommander: Boolean): Boolean;
begin
Result := (ProtocolType = cnCuteHouseID) and (ProtocolVersion = 1) and isCommander;
end;
{--------------------------------------------------------------------------
class function TCuteHouse.SaveUncompletedData
功能: 判断是否要保存通讯没有完全成功时接收到的数据
输入: 无
输出: boolean 是否保存
说明: 对于智能机房,应当保存
---------------------------------------------------------------------------}
class function TCuteHouse.SaveUncompletedData: Boolean;
begin
Result := True;
end;
{--------------------------------------------------------------------------
function TCuteHouse.AdjustTimeCommand: string;
功能: 生成调节时间的命令
输入: 无
输出: string 命令
---------------------------------------------------------------------------}
function TCuteHouse.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] := #$F1;
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 TCuteHouse.ProcessProtocol
功能: 处理当前通信状态,并生成新的命令
输入: var FPacketCommState: Integer; 当前的通信状态
SendResult: Integer; 上一次的发送结果
Packet: string; 上一次的数据
Parameters: TStrings 通信参数
输出: THostCommand 存放新的命令
var FPacketCommState: Integer; 存放下一步的通信状态
---------------------------------------------------------------------------}
{ 智能机房通信协议
一、握手:
●握手信号:
◆ "SWAT"
⊙ ACK+0003(监控仪标识)+0001(协议版本号) +CRC 【ACK】
二、参数设置:
● 校正监控仪时间
◆ 0Xf1+SSMMHHDDMMYY+CRC
⊙ ACK+0xF1+CRC 【ACK】
●设定主机及负责人电话号码
◆ 0xF2+1B(号码序号)+1B(号码长度)+电话号码+CRC 【1-4号为中心电话号码,5-8 为 负责人电话号码】
⊙ ACK+0xF2+1B(号码序号)+CRC 【ACK】
●设定密钥
◆ 0xF3+2B(帧号)+8B(数据)+CRC 【帧号为密钥位置,6B数据为4B密钥+1B权限(仅55H可开门1,AAH可开门2,5AH可开门1门2)+ 3B预留】
⊙ ACK+0xF3+2B(帧号)+CRC 【ACK】
●设定报警条件
◆ 0xF4+9B(报警范围)+1B(检测周期)+1B(重复报警周期)+CRC 【报警范围: 2B交流电压上下限(0-255),2B直流电压上下限(0-255),2B温度上下限(-128-+127),2B湿度上下限(0-100),1B开门报警(低四位为门0,高四位为门1;仅0x55H报警)。检测周期:1B(1-255分钟)重复报警周期:1B(1-255分钟)】
⊙ ACK+0xF4+CRC 【ACK】
三、数据传输:
●读取机号
◆0xF0+0x10+CRC
⊙ACK+0xF0+4B(机号)+2B(数据条数)+CRC 【ACK】
●历史数据传输
◆ 0xF5+2B(帧号)+ CRC
⊙ ACK+0xF5+2B(帧号)+1B(数据类型)+16B(记录数据)+CRC 【ACK】
●记录数据传输
⊙ ACK+0xF5+2B(帧号)+0x01+16B(记录数据)+CRC
【16B记录 MMHHDDMMYY(日期时间5B),
门磁空调风扇雷击浸水烟雾状态1B
(1B16进制数据,从高到低位为
第1位为1时门1开,
第2位为1时门2开,
第3位为1时空调开,
第4位为1时风扇开,
第5位为0时有雷击,
第6位为1时有浸水,
第7位为1时有烟雾),
交流电压1B,直流电压1B,湿度1B,温度7B。数据类型同上】
●门禁数据传输
⊙ ACK+0xF5+2B(帧号)+0x02+16B(记录数据)+CRC 【16B记录:SSMMHHDDMMYY(日期时间6B),卡号(4B),读卡器号(1B),门磁状态(1B),预留(4B)】
●报警数据传输
⊙ ACK+0xF5+2B(帧号)+0x03+16B(报警数据)+CRC 【16B报警:交流电压1B,直流电压1B,湿度1B,门磁状态1B,电缆断合1B,温度7B,预留(4B)】
●报警电缆长度
⊙ ACK+0xF5+2B(帧号)+0x04+16B(电缆长度)+CRC 【16B电缆长度:2B(前一字节为整数,后一字节为小数;电缆未断为0xFFFF)*8(8对电缆)】没数据后发16B0X00.
●实时电缆长度传输
◆ 0xF8+0x10+CRC
⊙ 'WAIT'(电缆长度未测出前每500豪秒发送一次)
⊙ ACK+0xF5+2B(0x0000)+0x05+16B(电缆长度)+CRC 【16B电缆长度:2B(前一字节为整数,后一字节为小数;电缆未断为0xFFFF)*8(8对电缆)】
四、控制数据:
● 空调风扇开门控制:
◆ 0xF9+3B(空调风扇开门控制)+CRC 【3B控制字节:空调1B,风扇1B,开门1B。(空调、风扇:仅为0x55时开,0xAA为关,其它都无关;开门:5A开门1,A5开门2,55开门1和2)】
⊙ ACK+0xF9+CRC 【ACK】
● 挂机命令:
◆ 0xFA+0x10+CRC
⊙ ACK+0xFA+CRC 【ACK】
● 命令接收错误应答:
⊙ NCK+4B(监控仪机号)+CRC 【NCK】
注:
1、固定数据取CRC只是为延长命令长度,减少"正确误码"或为处理方便。
2、当主机没有收到应答时,要求重发都由主机来执行。
3、当收到下一条命令,校验正确,就屏蔽上条命令,以次类推。
}
function TCuteHouse.ProcessProtocol(var FPacketCommState: Integer; SendResult: Integer; Packet: string; Parameters: TStrings): THostCommand;
const
csGetDataInfo = csUser + 1;
csAdjustTime = csUser + 2;
csSetPhoneCode = csUser + 3;
csSetSecureKey = csUser + 4;
csSetAlertLimit = csUser + 5;
csGetHistoryData = csUser + 6;
csGetCableLength = csUser + 7;
csControlDevice = csUser + 8;
var
i: Integer;
currentData: string;
function FindParams(Key: string): string;
begin
Result := Parameters.Values[IntToStr(cnCuteHouseID) + '_' + Key];
end;
function GetSubParams(Key: string; KeyIndex: Integer): string;
var
i: Integer;
curKeyIndex: Integer;
Params: string;
begin
Result := '';
Params := FindParams(Key);
if Params = '' then Exit;
curKeyIndex := 0;
if Params[Length(Params)] <> ',' then Params := Params + ',';
while Pos(',', Params) > 0 do
begin
i := Pos(',', Params);
if curKeyIndex = KeyIndex then
begin
Result := Copy(Params, 1, i - 1);
Break;
end;
Delete(Params, 1, i);
Inc(curKeyIndex);
end;
end;
begin
FProcessRatio := 0;
// 处理状态改变
case FPacketCommState of
csHandShake:
begin
FPacketCommState := csGetDataInfo;
FFrameNo := 0;
end;
csGetDataInfo:
begin
case SendResult of
srGetData:
begin
FSenderSerialNO := '';
for i := 1 to 4 do
FSenderSerialNO := FSenderSerialNO + IntToHex(ord(Packet[3 + i]), 2);
FDatas.Add('SenderSerialNo=' + FSenderSerialNO);
FDataCount := (Ord(Packet[8]) shl 8) + Ord(Packet[9]);
if (FindParams(FSenderSerialNO + '_AirCondition') <> '') or
(FindParams(FSenderSerialNO + '_Fan') <> '') or
(FindParams(FSenderSerialNO + '_Door') <> '') or
(FindParams(FSenderSerialNO + '_ClearAlert') = '1') then
FPacketCommState := csControlDevice
else
FPacketCommState := csAdjustTime;
end;
else
FPacketCommState := csHalt;
end;
end;
csControlDevice:
begin
case SendResult of
srACK:
begin
// 空调
if FindParams(FSenderSerialNO + '_AirCondition') = '0' then
FDatas.Add('AirCondition=OFF');
if FindParams(FSenderSerialNO + '_AirCondition') = '1' then
FDatas.Add('AirCondition=ON');
// 风扇
if FindParams(FSenderSerialNO + '_Fan') = '0' then
FDatas.Add('Fan=OFF');
if FindParams(FSenderSerialNO + '_Fan') = '1' then
FDatas.Add('Fan=ON');
if FindParams(FSenderSerialNO + '_Door') <> '' then
FDatas.Add('Door=' + FindParams(FSenderSerialNO + '_Door'));
if FindParams(FSenderSerialNO + '_ClearAlert') = '1' then
FDatas.Add('Alert=OFF');
FFrameNO := 0;
FPacketCommState := csAdjustTime;
end;
else
FPacketCommState := csHalt;
end;
end;
csAdjustTime:
begin
case SendResult of
srACK:
begin
FFrameNo := 0;
if FindParams(FSenderSerialNO + '_Phone') <> '' then
FPacketCommState := csSetPhoneCode
else
begin
if (FindParams('SetSecureKey') = '1') and (GetSubParams(FSenderSerialNO + '_Key', 0) <> '') then
FPacketCommState := csSetSecureKey
else
begin
if FindParams(FSenderSerialNO + '_AlertLimit') <> '' then
FPacketCommState := csSetAlertLimit
else
FPacketCommState := csGetHistoryData;
end;
end;
end;
else
FPacketCommState := csHalt;
end;
end;
csSetPhoneCode:
begin
case SendResult of
srACK:
begin
Inc(FFrameNo);
//FProcessRatio := FFrameNo * 100 div 8;
if (FFrameNo >= 8) then
begin
FFrameNO := 0;
if (FindParams('SetSecureKey') = '1') and (GetSubParams(FSenderSerialNO + '_Key', 0) <> '') then
FPacketCommState := csSetSecureKey
else
begin
if FindParams(FSenderSerialNO + '_AlertLimit') <> '' then
FPacketCommState := csSetAlertLimit
else
FPacketCommState := csGetHistoryData;
end;
end;
end;
else
FPacketCommState := csHalt;
end;
end;
csSetSecureKey:
begin
case SendResult of
srACK:
begin
Inc(FFrameNo);
if (GetSubParams(FSenderSerialNO + '_Key', FFrameNO) = '') then
begin
FFrameNO := 0;
if FindParams(FSenderSerialNO + '_AlertLimit') <> '' then
FPacketCommState := csSetAlertLimit
else
FPacketCommState := csGetHistoryData;
end;
end;
else
FPacketCommState := csHalt;
end;
end;
csSetAlertLimit:
begin
case SendResult of
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -