2012-05-11 16 views
1

私はまだTComPortコンポーネントに問題がありますが、今回はコンポーネントそのものがその背後にあるロジックではありません。私はデバイスの魔女がシリアルポート経由でいくつかのASCII文字列を送信する私はそれらの文字列をpraseする必要がありますコンピュータは非常に速く反応するので、文字列の残りの部分だけをキャプチャする覚えているときにそれを解析することは不可能になります。デルファイでのシリアルポートの同期

私は10才以上のシリアルアクティビティがないかどうかを確認してから、バッファに保存している文字列を作成します。しかし、この方法は専門家ではありません私は聞くことができるアイドルイベントの魔女はありません...私の問題のための最善の解決策を待っています。ありがとう。

+2

は、これは既知の問題です。より多くの環境でそれを持っていた。データ・イベント・トリガーは実際に続くデータよりも速い。読書の前に少し待っているのに加えて、他に何かがあるかどうかはわかりません。 – pritaeas

+0

これはかなり正常です。文字をバッファに格納し、タイマーをリセットするだけです。その後、タイマーを使用して実際のアクティビティをトリガーします。タイマーはかなりタイトになる可能性があります。あなたは "状態マシン"で終わり、イベントのためのトリガーを持っています。 – mj2008

+0

Djean CrnilaのTComport(いくつかのコンポーネントは「TComport」と呼ばれています)を使用するとします。この問題は、データイベントのトリガに関連している可能性がありますが、シリアルデバイスのボーレートが、コンポート制御のレートとは異なるレートに設定されている可能性もあります。デバイスが数msごとにパケットを送信する場合、パケットが適切な長さであれば、データパケット全体を簡単にキャプチャして解析できます。デバイスはパケット制御文字の終わりを送信しますか?それはより簡単になります。あなたがデバイスに関する詳細情報を提供し、パケットなどを受信した場合、誰かがヘルプを提供できる可能性があります。 – SteveJG

答えて

3

は、シリアル・ポート・コンポーネントの数を使用した後、私は、CreateFile('\\?\COM1',GENERIC_READ or GENERIC_WRITE,0,nil,OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,0)を使用してTHandleStreamインスタンスに、そのハンドルを渡し、そこから読み込むための専用のスレッドを開始することにより、今まで最高の結果を持っています。私は、スレッドがイベントハンドラを書くよりももう少し多くの作業を行うことを知っていますが、それでもシリアルポートを使用することから生じる同期の問題を処理する最良の方法です。

3

OnRXCharイベントの典型的なハンドラ:

procedure XXX.RXChar(Sender: TObject; Count: Integer); 
begin 
    ComPort.ReadStr(s, Count); 
    Accumulator := Accumulator + s; 
    if not AccumContainsPacketStart then 
    Accumulator := '' 
    else if AccumContainsPacketEndAfterStart then begin 
    ExtractFullStringFromAccum; 
    ParseIt; 
    end; 
end; 
+0

文字列には#0などの文字が含まれているため、readstrメソッドの使用はdefenetlyですthe.question – opc0de

+1

パスカル文字列では重要ではありません(OS関数の出力まで) – MBo

+0

@ opc0de、パスカルで#0文字は他の文字と同じです。バイトで作業したい場合は、AnsiChars/AnsiStringsをバイト/ TByteにスワップします。これは、D2009とユニコード以来の好ましい方法です。 –

2

注意。 ほとんどのCOMポートコンポーネントは、所有者に報告するときにヒントを得ません。通常、ポートからバイトを収集する責任を負うスレッドには、1つ以上のバイトが処理可能な状態であることがOSによって通知されます。この情報はあなたのレベルまで単純に表示されます。だからあなたは、メッセージが転送されることを期待すると、あなたはOSがあなたに与えているものを得る。

すべての受信文字をグローバルバッファにバッファリングする必要があります。メッセージ文字列の最後の文字を取得したら、メッセージを処理します。

ここでは、メッセージ開始が特殊文字で識別され、メッセージの末尾が別の文字で識別される例を示します。

あなたのメッセージが別の方法で構築されている場合は、コードをどのように適応させるかを理解できます。

var 
    finalBuf: AnsiString; 

{- Checking message } 
Function ParseAndCheckMessage(const parseS: AnsiString) : Integer; 
begin 
    Result := 0; // Assume ok 
    {- Make tests to confirm a valid message } 
    ... 
end; 


procedure TMainForm.ComPortRxChar(Sender: TObject; Count: Integer); 
var 
    i,err: Integer; 
    strBuf: AnsiString; 
begin 
    ComPort.ReadStr(strBuf, Count); 
    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 := ParseAndCheckMessage(finalBuf); 
       if (err = 0) then 
       {- Handle validated string } 
       else 
       {- Handle error } 
      end; 
      finalBuf := ''; 
     end; 
    else 
     finalBuf := finalBuf + strBuf[i]; 
    end; 
end; 
1

プロトコルに開始/終了マーカーがある場合は、TComDataPacketを使用して、利用可能なときにフルパケットを提供できます。

+0

Delphi 7でTComDataPacketを使用するとアクセス違反が発生することはありませんが、パケットが大きい場合は – opc0de

+0

がOKです。ウォーレンがあなたの他の質問でそれを話すのを見ました。しかし、Delphi XE2で成功したばかりです。 –

+0

サイズが1024バイトの固定バッファを定義しているようです。コンストラクタでそれを増やして、それがより大きなパケットに役立つかどうか確認してください。 –

-2

ある程度の文字の場合、データが完全に送信されることを確認するために、ReadStrの前に数ミリ秒の遅延を使用できます。文字の4量の例:

procedure TForm1.ComPort1RxChar(Sender: TObject; Count: Integer); 
var 
    Str: String; 
    tegangan : real; 
begin 
    sleep(100); //delay for 100ms 
    ComPort1.ReadStr(Str, 4); 

...

関連する問題