Delphi 10.4 解释 TidBytes TidTCPServer

问题描述

我知道有很多这样的问题,但我找不到我的具体问题的答案。 我想读取 RFID 读卡器发送的数据。 之前没用过像TidBytes这样的类型,不知道怎么解释规范,

规格如下:

事件数据

字段 大小(字节) 说明
时间 4 保留数据类型
数据大小 1 数据缓冲区数组的大小(0~16)。
数据缓冲区 16 数据缓冲区数组(卡 UID 或扇区数据)。
设备名称 16 名称(字符串类型)
XID 4 储备

借助一些演示,我可以使用以下代码读取数据:

我使用 TIdTcpserver.onExecute 过程:

procedure Ttid.IdTcpserverExecute(AContext: TIdContext);
    
    var
      Buffer : TIdBytes ;
      s: UTF8String;
      i,BufSize: Integer;
    begin

    BufSize := 40;

      if AContext.Connection.IOHandler.InputBufferIsEmpty then begin
    
         if AContext.Connection.IOHandler.CheckForDataOnSource(1000) then begin
          AContext.Connection.IOHandler.ReadBytes(Buffer,BufSize,true);
    
          for i := 0 to BufSize do
            s :=s +  IntToStr(buffer[i]);
    
          Memo1.Lines.Add(s) ;
         end;
      end;
    end;

结果如下:

3 次扫描:

1122051201414814812062000000000000698255553484568101109111000000211231215 11620512014148191362000000000000698255534845681011091111000000211231215 11720512014148191362000000000000698255534845681011091111000000211231215

但使用读卡器随附的演示软件,我知道卡 ID(设备名称)通常应如下所示:

3E03BF94、3E789494 ...

所以,问题是,如何使用规范将这些数字转换为“真实”ID。

非常感谢任何帮助/提示

在 Tom Brunberg 的帮助下,我将代码更改为:

procedure Ttid.IdTcpserverExecute(AContext: TIdContext);
var
  Buffer : TIdBytes ;
  RxBufSize: Integer;
begin

  RxBufSize := 10;

  if AContext.Connection.IOHandler.InputBufferIsEmpty then begin

     if AContext.Connection.IOHandler.CheckForDataOnSource(seTimeout.Value) then begin

      AContext.Connection.IOHandler.ReadBytes(Buffer,RxBufSize,true);

      Memo1.Lines.Add( IntToHex(buffer[9])
                     + IntToHex(buffer[8])
                     + IntToHex(buffer[7])
                     + IntToHex(buffer[6])
                        );
     end;

  end;

end;

扫描 3 张卡的结果与演示软件显示的 Card-ID 相同:

3E789494
3E03BF94
3E803194

亲切的问候,

德克·詹森斯。

解决方法

首先,TIdTCPServer 是一个多线程组件,它的 OnConnectOnDisconnectOnExecuteOnException在为管理每个接受的客户端连接而创建的工作线程的上下文中触发事件。因此,从主 UI 线程之外直接访问 UI 控件是不安全的。您必须同步对它们的访问,例如使用 TThread.Synchronize()TThread.Queue()

因此,您显示的代码需要看起来更像这样:

procedure Ttid.IdTCPServerExecute(AContext: TIdContext);
var
  Buffer: TIdBytes;
  s: string;
  i: Integer;
begin
  AContext.Connection.IOHandler.ReadBytes(Buffer,41);
    
  s := IntToHex(Buffer[0],2);
  for i := 1 to High(Buffer) do
    s := s + ' ' + IntToHex(Buffer[i],2);
    
  TThread.Synchronize(nil,procedure
    begin
      Memo1.Lines.Add(s);
    end
  );
end;

话虽如此,您可以通过不同的方式使用 TIdTCPServer 实现该 RFID 规范。请注意,在读取字节时,您必须考虑到 RFID 数据的“数据缓冲区”字段是可变长度(您的原始代码根本没有考虑到这一点): >

  • TIdIOHandler 有许多方法可以读取各种数据类型,包括固定大小的整数:
procedure Ttid.IdTCPServerExecute(AContext: TIdContext);
var
  iTime,XID: UInt32;
  Data: TIdBytes;
  DeviceName: string;
begin
  with AContext.Connection.IOHandler do
  begin
    iTime := ReadUInt32;
    ReadBytes(Data,ReadUInt8);
    DeviceName := ReadString(16);
    XID := ReadUInt32;
  end;
  ...
end;
  • Indy 的 IdGlobal 单元具有从 TIdBytes 缓冲区读取各种数据类型的类似功能:
procedure Ttid.IdTCPServerExecute(AContext: TIdContext);
var
  iTime,XID: UInt32;
  Buffer,Data: TIdBytes;
  DeviceName: string;
  Offset: Integer;
begin
  with AContext.Connection.IOHandler do
  begin
    ReadBytes(Buffer,5);
    ReadBytes(Buffer,ReadUInt8 + 20,True);
  end;

  SetLength(Data,Length(Buffer)-25);

  Offset := 0;
  iTime := BytesToUInt32(Buffer,Offset);
  Inc(Offset,4);
  CopyTIdBytes(Buffer,Offset,Data,Length(Data));
  Inc(Offset,Length(Data));
  DeviceName := BytesToString(Buffer,16);
  Inc(Offset,16);
  XID := BytesToUInt32(Buffer,Offset);

  ...
end;