我通过PCB通过TCP进行ATMEL-microcontroller通信Lantronix Xport。它发送一些带有asci字符串的继电器和传感器的状态报告,长度为30个字节,格式如下:
400000000000000000414243303031303030303030303030303030303339
| | | |
| byte[0] = $40 | | |
| | |- byte[11] = $43 // 'C' Always
| |--- byte[10] = $42 // 'B' Always
|----- byte[9] = $41 // 'A' Always
byte[1..8] = boolean values := Bool(byte[x]);
byte[12..29] = 6 different numbers as string, 3 chars long each, range 0..999
我已经对加载状态的数据包进行了简单的验证:
procedure Load(iData: TBytes);
const vsp = 9; //Validation String Postition
buflen = 30;
begin
If (Length(iData) = buflen) And (iData[0] = $40) And (iData[vsp] = $41) And (iData[vsp + 1] = $42) And (iData[vsp + 2] = $43) Then
SetStatus(iData) //Function for loading validated packet
Else Begin
DoError(1, 'Invalid packet. Length = ' + IntToStr(Length(iData)) + #13#10 +
'iData[0] = ' + String(ByteToHex(iData[0])) + #13#10 +
'iData[vsp] = ' + String(ByteToHex(iData[vsp])) + #13#10 +
'iData[vsp + 1] = ' + String(ByteToHex(iData[vsp + 1])) + #13#10 +
'iData[vsp + 2] = ' + String(ByteToHex(iData[vsp + 2]))
);
End;
End;
问题是,有时asci字符串被分成几个数据包或连接到数据包上(第一个数字是tickcount):
4239483 4000 // Could be missing in case of disconnect-reconnect.
4239514 00000000000000414243303030303030303030303030303030303338400000000000000000414243
4239545 303031303030303030303030303030303339
4239576 400000000000000000414243303031303030303030303030303030303339400000000000000000414243303031303030303030303030303030303338
4239670 40000000000000000041424330303130
4239701 3030303030303030303030303339
如果断开连接 - 重新连接部分状态信息将丢失。
我如何利用TMemoryStream或Move来阅读收到的所有数据包而不丢弃任何信息?
编辑: SetStatus看起来像这样,所以我想避免将状态报告移植到字符串:
Procedure TTractor.SetStatus(AData: TBytes);
begin
StatusStream := AData;
pStatus.HighSpeed := Bool(AData[1]);
pStatus.RevDir := Bool(AData[2]);
pStatus.FwdDir := Bool(AData[3]);
pStatus.FrontExpanded := Bool(AData[4]);
pStatus.RearExpanded := Bool(AData[5]);
pStatus.AntiSpin := Bool(AData[6]);
pStatus.Unknown1 := DecodePCBNumber(AData[12], AData[13], AData[14]);
pStatus.PumpPressureVoltage := DecodePCBNumber(AData[15], AData[16], AData[17]);
pStatus.Unknown2 := DecodePCBNumber(AData[18], AData[19], AData[20]);
pStatus.WheelPressureVoltage := DecodePCBNumber(AData[21], AData[22], AData[23]);
pStatus.OilTemperatureVoltage := DecodePCBNumber(AData[24], AData[25], AData[26]);
pStatus.PCBTemperatureVoltage := DecodePCBNumber(AData[27], AData[28], AData[29]);
End;
阅读代码:
procedure TForm1.FormCreate(Sender: TObject);
begin
If Traktor = nil Then Traktor := TTractor.Create(500);
//...
End;
// TidConnectionIntercept
procedure TForm1.trCItcReceive(ASender: TIdConnectionIntercept; var ABuffer: TArray<System.Byte>);
Var
tmpList: TList;
i: Integer;
Begin
If Traktor <> nil Then Traktor.StatusTBytes := ABuffer;
AppendConLog(TraktorReceive, False, False, BytesToHexStr(ABuffer) );
End;
procedure TForm1.AppendConLog(LogTyp: TLogType; ShowInConsole, PlainText: Boolean; Text: String);
begin
If ShowInConsole Or (PlainText And DebugMode) Then Begin
MemoConsole.Lines.Add(FormatDateTime('[hh:nn:ss] ', Now) + Text);
Text := String2Hex(AnsiString(Text));
End;
LogFile.Add(IntToHex(DateTimeToUNIXTimeFAST(Now()), 8) + ' ' + IntToHex(GetTickCount, 8) + ' ' +
IntToHex(Word(ShowInConsole), 1) + IntToHex(Word(PlainText), 1) + ' ' +
IntToHex( Ord(LogTyp), 2) + ' ' + Text);
end;
Type TTractor = class
constructor Create(LoadInterval: Cardinal; SyncInterval: Cardinal = 25);
//....
public
property StatusTBytes: TBytes read StatusStream write Load;
//...
End;
答案 0 :(得分:1)
Intercept.OnReceive
事件本身不会触发,它会在另一个读取操作的上下文中触发。您展示的代码中没有任何内容可以执行任何读取,因此永远不会触发OnReceive
事件。
Intercept
用于处理数据(压缩,加密等),而不是用于检测何时读取数据。请改用IOHandler
方法,例如ReadBytes()
。 TIdIOHandler
有自己的内部缓冲区来处理分裂/连接数据包。这使您可以专注于更高级别的逻辑(读取30字节的消息),而Indy处理较低级别的详细信息(在循环中读取,直到30个字节可用,为后续读取缓存未使用的数据等)。
完全摆脱Intercept
,然后在需要时拨打TIdTCPClient.IOHandler.ReadBytes()
,例如:
在计时器中:
procedure TForm1.IdTCPClient1Connected(Sender: TObject);
begin
ReadTimer.Enabled := True;
end;
procedure TForm1.IdTCPClient1Disconnected(Sender: TObject);
begin
ReadTimer.Enabled := False;
end;
procedure TForm1.ReadTimerElapsed(ASender: TObject);
var
Buffer: TIdBytes;
Begin
try
if IdTCPClient1.IOHandler.InputBufferIsEmpty then
begin
IdTCPClient1.IOHandler.CheckForDataOnSource(10);
IdTCPClient1.IOHandler.CheckForDisconnect;
if IdTCPClient1.IOHandler.InputBufferIsEmpty then Exit;
end;
IdTCPClient1.IOHandler.ReadBytes(Buffer, 30);
except
IdTCPClient1.Disconnect;
Exit;
end;
If Traktor <> nil Then Traktor.StatusTBytes := Buffer;
AppendConLog(TraktorReceive, False, False, BytesToHexStr(Buffer) );
End;
或者在工作线程中:
type
TDataEvent = procedure(const Data: TIdBytes) of object;
TReadingThread = class(TThread)
private
FBuffer: TIdBytes;
FClient: TIdTCPClient;
FOnData: TDataEvent;
procedure DoData;
protected
procedure Execute; override;
public
constructor Create(AClient: TIdTCPClient; AOnData: TDataEvent); reintroduce;
end;
constructor TReadingThread.Create(AClient: TIdTCPClient; AOnData: TDataEvent);
begin
inherited Create(False);
FClient := AClient;
FOnData := AOnData;
end;
procedure TReadingThread.Execute;
begin
while not Terminated do
begin
FClient.IOHandler.ReadBytes(FBuffer, 30);
Synchronize(DoData);
end;
end;
procedure TReadingThread.DoData;
begin
if Assigned(FOnData) then FOnData(FBuffer);
end;
var
ReadThread: TReadingThread = nil;
procedure TForm1.IdTCPClient1Connected(Sender: TObject);
begin
ReadThread := TReadingThread.Create(IdTCPClient1, DataAvailable);
end;
procedure TForm1.IdTCPClient1Disconnected(Sender: TObject);
begin
if Assigned(ReadThread) then
begin
ReadThread.Terminate;
ReadThread.WaitFor;
FreeAndNil(ReadThread);
end;
end;
procedure TForm1.DataAvailable(const Data: TIdBytes);
begin
If Traktor <> nil Then Traktor.StatusTBytes := Data;
AppendConLog(TraktorReceive, False, False, BytesToHexStr(Data) );
end;
答案 1 :(得分:0)
将数据的每个部分从端口添加到全局缓冲区:ansistring 检查缓冲区是否在位置1包含所需的标头(否则在标头字节之前删除所有标头),并检查缓冲区长度&gt; = buflen。如果是这样,请处理数据并将其从缓冲区中删除。