5

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

等々。各回線は1秒ごとに送信されます。したがって、上記のコードを使用すると、メモにはこれらすべてのデータがそのまま表示されます。

ただし、コードを次のように変更すると、次のようになります。

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文字の後に表示されます。つまり、CPort.pasライブラリでは、非同期/同期部分で、最大8ASCII文字のみが変数Strに割り当てられますか?

サイズに関係なく、データ文字列全体が8バイトだけではなく、変数Strに割り当てられるようにコードを変更するにはどうすればよいですか。

更新**次のコードを含む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;
4

3 に答える 3

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;
于 2012-04-04T08:48:47.533 に答える
2

完全なエラー チェックを備えた最小限のソリューションは次のようになります。

アップデート :

(受信した文字列が 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

直接の質問に答えるには、受信メソッドがデータ文字列の途中に「ハハ」文字列を追加できる理由:

コンポート ルーチンは、独自のペースで 1 つまたは複数の文字のデータを配信します。データを取得する時期や文字数を制御することはできません。私の回答のスキームを使用すると、完全なパッケージが配信されるまでデータがバッファリングされます。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;
于 2012-04-05T07:07:12.223 に答える
0

Dejan Crnila の ComPort ライブラリを使用している場合は、TComDataPacketコンポーネント、特にそのコンポーネントOnPacketOnCustomStopイベントを確認する必要があります。

更新: コード例を次に示します。開始文字列が常に同じで、終了文字列が ',T*' で始まり、その後に 2 つの文字 (おそらく 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;
于 2012-04-04T08:31:38.023 に答える