我已经下载并安装了comport库。我有一个模拟器通过串口RS232发送数据到Selphi程序。这是以下代码段。
procedure TMainForm.ComPortRxChar(Sender: TObject; Count: Integer);
begin
ComPort.ReadStr(CPort.Str, Count);
Memo.Text := Memo.Text + CPort.Str;
end;
至于CPort库部分,我补充说:
var
Str: String;
here is the problem.
通过的数据示例大致类似于
$HEHDT,288.45,T*1D
$HEHDT,288.46,T*18
$HEHDT,288.47,T*1A
等等。每行每秒发送一次。因此,使用上面的代码,备忘录将显示所有这些数据。
但是,如果我将代码更改为:
procedure TMainForm.ComPortRxChar(Sender: TObject; Count: Integer);
begin
ComPort.ReadStr(CPort.Str, Count);
Memo.Text := Memo.Text + CPort.Str + 'haha';
end;
这是备忘录上的内容:
$HEHDT,2haha88.45,T*haha1Dhaha
$HEHDT,2haha88.46,T*haha18haha
$HEHDT,2haha88.47,T*haha1Ahaha
8个ASCII字符后出现“haha”。这在CPort.pas中意味着什么呢? 库,在异步/同步部分下,最多只有8ASCII字符 分配给变量Str?
如何更改代码,以便整个数据字符串,无论如何 它的大小将被分配给变量Str而不是仅仅8个字节。
更新**我意识到这部分CPort库包含以下代码。任何人都可以启发我如何编辑代码?或者它是否是我采购的正确块。谢谢!
// split buffer in packets
procedure TComDataPacket.HandleBuffer;
procedure DiscardPacketToPos(Pos: Integer);
var
Str: string;
begin
FInPacket := True;
if Pos > 1 then
begin
Str := Copy(Buffer, 1, Pos - 1); // some discarded data
Buffer := Copy(Buffer, Pos, Length(Buffer) - Pos + 1);
DoDiscard(Str);
end;
end;
procedure FormPacket(CutSize: Integer);
var
Str: string;
begin
Str := Copy(Buffer, 1, CutSize);
Buffer := Copy(Buffer, CutSize + 1, Length(Buffer) - CutSize);
CheckIncludeStrings(Str);
DoPacket(Str);
end;
procedure StartPacket;
var
Found: Integer;
begin
// check for custom start condition
Found := -1;
DoCustomStart(Buffer, Found);
if Found > 0 then
DiscardPacketToPos(Found);
if Found = -1 then
begin
if Length(FStartString) > 0 then // start string valid
begin
Found := Pos(Upper(FStartString), Upper(Buffer));
if Found > 0 then
DiscardPacketToPos(Found);
end
else
FInPacket := True;
end;
end;
procedure EndPacket;
var
Found, CutSize, Len: Integer;
begin
// check for custom stop condition
Found := -1;
DoCustomStop(Buffer, Found);
if Found > 0 then
begin
// custom stop condition detected
CutSize := Found;
FInPacket := False;
end
else
if Found = -1 then
begin
Len := Length(Buffer);
if (FSize > 0) and (Len >= FSize) then
begin
// size stop condition detected
FInPacket := False;
CutSize := FSize;
end
else
begin
Len := Length(FStartString);
Found := Pos(Upper(FStopString),
Upper(Copy(Buffer, Len + 1, Length(Buffer) - Len)));
if Found > 0 then
begin
// stop string stop condition detected
CutSize := Found + Length(FStopString) + Len - 1;
FInPacket := False;
end;
end;
end;
if not FInPacket then
FormPacket(CutSize); // create packet
end;
答案 0 :(得分:2)
你应该做自己的缓冲。这使您可以更灵活地界定消息的界限。例如,如果使用回车符(#13)分隔消息,则可以执行以下操作:
var Buf: string;
procedure DoSomething(const Str: String);
begin
Memo.Text := Memo.Text + Str;
end;
procedure TMainForm.ComPortRxChar(Sender: TObject; Count: Integer);
var I: Integer;
begin
ComPort.ReadStr(CPort.Str, Count);
Buf := Buf + CPort.Str;
for i := 1 to Length(Buf) do
if Buf[i] = #13 then
begin
DoSomething(Copy(Buf, 1, i));
Delete(Buf, 1, i+1);
Break;
end;
end;
答案 1 :(得分:2)
完整错误检查的 minimalistic 解决方案将是:
更新:
(假设收到的字符串以CRLF组合结束,并且数据包长度不是常数。这是NMEA 0183数据包)
var
finalBuf: AnsiString;
// Example packet: $HEHDT,10.17,T*28 + CRLF
// This is a NMEA 0183 protocol (Marine and GPS standard)
// Subset for reading the heading.
// HDT Heading – True
// 1 2 3
// | | |
//$--HDT,x.x,T*hh
//1) Heading Degrees, true
//2) T = True
//3) Checksum
// HE stands for: Heading – North Seeking Gyro
{- Checking packet and checksum, calculating heading }
Function ParseAndCheckNMEA_HDT(const parseS: AnsiString;
var heading: Double): Integer;
// Example packet: $HEHDT,10.17,T*28 + CRLF
var
i, p, err: Integer;
xorSum: Byte;
xorStr: AnsiString;
headingStr: AnsiString;
begin
Result := 0; // Assume ok
if (Pos('$HEHDT', parseS) = 1) then // Start header ok ?
begin
p := Pos('*', parseS);
if (p <> 0) and (Length(parseS) >= p + 2) then
// Assumes a checksum in packet
begin
xorSum := Ord(parseS[2]);
for i := 3 to p - 1 do // Calculate checksum
xorSum := xorSum xor Ord(parseS[i]);
xorStr := IntToHex(xorSum, 2);
if (UpperCase(xorStr) = Copy(parseS, p + 1, 2)) then // Checksum ok ?
begin
// Validate heading
headingStr := Copy(parseS, 8, p - 10);
Val(headingStr, heading, err);
if (err <> 0) then
Result := 4; // Not a valid float
end
else
Result := 3; // Wrong checksum
end
else
Result := 2; // No checksum
end
else
Result := 1; // Wrong header
end;
procedure TMainForm.ComPortRxChar(Sender: TObject; Count: Integer);
var
i,err: Integer;
strBuf: AnsiString;
heading: Double;
begin
ComPort.ReadStr(CPort.Str, Count);
strBuf := CPort.str;
for i := 1 to Length(strBuf) do
case strBuf[i] of
'$' :
finalBuf := '$'; // Start of package
#10 :
begin
if (finalBuf <> '') and (finalBuf[1] = '$') then // Simple validate check
begin
SetLength( finalBuf, Length(finalBuf) - 1); // Strips CR
err := ParseAndCheckNMEA_HDT(finalBuf, heading);
if (err = 0) then
Memo.Lines.Add(finalBuf); // Add validated string
//else
// Memo.Lines.Add('Error:' + IntToStr(err));
end;
finalBuf := '';
end;
else
finalBuf := finalBuf + strBuf[i];
end;
end;
知道起始字符和数据包结尾,这应该是相当安全的。 标记包结束的#13和#10(CR LF)字符被剥离,并检查包的有效校验和,并计算得到的标题值。 然后将经过验证的字符串添加到备忘录中。
更新2
要回答直接问题,为什么接收方法可以在数据字符串的中间添加“haha”字符串:
comport例程按照自己的节奏传递一个或多个数据。您无法控制何时获取数据或将有多少个字符。在我的回答中使用该方案,数据被缓冲,直到交付完整的包。通过TComPort的数据包支持,可以做到这一点。
从您的评论中可以看出,有几种NMEA 0183传感器类型连接到串行端口。 (给出其他长度的包,但都以$字符开头)。
用以下函数替换ParseAndCheckNMEA_HDT以在这种情况下验证字符串:
Function ParseAndCheckNMEA(const parseS: AnsiString): Integer;
// Example packet: $HEHDT,10.17,T*28 + CRLF
var
i, p: Integer;
xorSum: Byte;
xorStr: AnsiString;
begin
Result := 0; // Assume ok
if (Pos('$', parseS) = 1) then // Start header ok ?
begin
p := Pos('*', parseS);
if (p <> 0) and (Length(parseS) >= p + 2) then
// Assumes a checksum in packet
begin
xorSum := Ord(parseS[2]);
for i := 3 to p - 1 do // Calculate checksum
xorSum := xorSum xor Ord(parseS[i]);
xorStr := IntToHex(xorSum, 2);
if (UpperCase(xorStr) <> Copy(parseS, p + 1, 2)) then // Checksum ok ?
Result := 3; // Wrong checksum
end
else
Result := 2; // No checksum
end
else
Result := 1; // Wrong header
end;
答案 2 :(得分:0)
如果您使用的是Dejan Crnila的ComPort库,您应该查看TComDataPacket
组件,尤其是OnPacket
和OnCustomStop
个事件。
<强>更新强> 这是一个代码示例。假设起始字符串始终相同,结束字符串以',T *'开头,后跟两个字符(可能是CRC代码)。如果该假设是错误的,则必须调整停止逻辑。
procedure TForm136.ComDataPacketCustomStop(Sender: TObject; const Str: string;
var Pos: Integer);
begin
Pos := System.Pos(',T*', Str);
if (Pos > 0) then begin
Inc(Pos, 4);
if Pos <= Length(Str) then Exit;
end;
Pos := -1;
end;
procedure TForm136.FormCreate(Sender: TObject);
begin
ComDataPacket.ComPort := ComPort;
ComDataPacket.StartString := '$HEHDT,';
ComDataPacket.OnCustomStop := ComDataPacketCustomStop;
end;