2015-11-13 38 views
5

私は小さなPICマイクロコントローラテスタデバイスとインターフェイスするC#ベースのUIを変更しています。C#:シリアルポートを読み取る - BytesToRead

UIは、シリアルポート接続を介してマイクロコントローラに「コマンド」を送信することによってテストを開始する2つのボタンで構成されています。 250ミリ秒ごとに、UIはシリアルインターフェイスをポーリングして、PICからのテスト結果で構成される簡単なメッセージを探します。メッセージはテキストボックスに表示されます。次のように

私が継承されたコードは次のとおりです。

try 
{ 
    btr = serialPort1.BytesToRead; 
    if (btr > 0) 
     Thread.Sleep(300); 
    btr = serialPort1.BytesToRead; 
    if (btr > 0) 
    { 
     Thread.Sleep(300); 
     btr = serialPort1.BytesToRead; 

     numbytes = serialPort1.Read(stuffchar, 0, btr); 
     for (x = 0; x < (numbytes); x++) 
     { 
      cc = (stuffchar[x]); 
      stuff += Convert.ToString(Convert.ToChar((stuffchar[x]))); 
     } 

最後にシリアルポートを読む前にBytesToReadへの3つの呼び出しと2つの300ミリ秒のスリープ・コールからなる最初の数行の根拠は何でしょうか?私がコードを間違って解釈していない限り、シリアルポートからの読み込みには600ミリ秒以上かかるので、私のように思えます。

答えて

0

このコードが最初はループしていたのであれば、PICがデータを収集するのを待つ方法でした。

実際のハードウェアをテストする場合は、両方のスリープを削除することをお勧めします。

4

これは、SerialPort.Read()の動作について恐ろしいハックです。これは、実際に受信したバイト数だけを返します。通常は1または2のシリアルポートが遅く、最新のPCは非常に高速です。だからThread.Sleep()を呼び出すことによって、コードはRead()呼び出しがバイトを返すのに十分長い間、UIスレッドを遅らせることになります。プロトコルがどのようなものであれ、うまくいけばそれらのすべて。通常は常に動作します。投稿されたコードではうまくいかず、プログラマーは恣意的に2倍遅れただけでした。ああ。

もちろん、大きな悲惨なことは、UIスレッドが強制的に非常に緊張していることです。非常に目立ち、塗装してユーザーの入力に反応するのが非常に遅くなります。

これは、最初にプロトコルに注意して修復する必要があります。 PICはレスポンスに一定のバイト数を送信する必要があるため、単にカウントダウンするか、PCに応答が完全に受信されたことを検出する方法を提供する必要があります。通常、応答の最後のバイト(SerialPort.NewLine)として一意のバイトを送信するか、応答の長さをメッセージの先頭にバイト値として含めます。具体的なアドバイスをするのは難しいですが、あなたはプロトコルを全く記述していませんでした。

ハッキーコードを保持してワーカースレッドに移動できるため、UIにはあまり影響しません。 SerialPort.DataReceivedイベントから無料で1つを取得します。しかし、それはコアの問題を解決する代わりに2つの問題を生み出す傾向があります。

0

@TomWrあなたが正しいです、私がこれを読んでいるところからです。私のコメントで以下
あなたのスニペット:

try 
{ 
    // Let's check how many bytes are available on the Serial Port 
    btr = serialPort1.BytesToRead; 

    // Something? Alright then, let's wait 300 ms. 
    if (btr > 0) 
     Thread.Sleep(300); 

    // Let's check again that there are some bytes available the Serial Port 
    btr = serialPort1.BytesToRead; 

    // ... and if so wait (maybe again) for 300 ms 
    // Please note that, at that point can be all cumulated about 600ms 
    // (if we actually already waited previously) 
    if (btr > 0) 
    { 
     Thread.Sleep(300); 
     btr = serialPort1.BytesToRead; 

     numbytes = serialPort1.Read(stuffchar, 0, btr); 
     for (x = 0; x < (numbytes); x++) 
     { 
      // Seems like a useless overhead could directly use 
      // an Encoding and ReadExisting method() of the SerialPort. 
      cc = (stuffchar[x]); 
      stuff += Convert.ToString(Convert.ToChar((stuffchar[x]))); 
     } 

は私の推測では、すでにデータがデバイスによって送信されたとのことができます彼らに

をフェッチしているかどうかを確認するために、基本的には、おそらく、idstamによって上記されたように同じですこのコードを適切なSerialPortメソッドで簡単にリファクタリングすることは、実際にはシリアルポート上で利用可能なデータがあるかどうかをチェックするために、はるかに簡潔な方法があるからです。

「ポートに何バイトありますか?何かがあれば、300ミリ秒後に同じことをやり直してください。"これは悲惨なことに
で終了します"このように2回300 ms = 600ms、または1回だけ(最初にあったかどうかに応じて)、またはまったく何もしません(このUIを介して通信しているデバイスによってThread.SleepがUIをブロックして以来本当に不愉快なことがあります)。 "

まず、同じコードベースを維持しようとしていることを考えてみましょう。

ReadTimeoutプロパティを使用してタイムアウト例外をキャッチするのではなく、読みやすくするためにはそれ以上ではなく、Convert.ToChar()呼び出しを使用せずにStringを直接取得するのはなぜですか?

私は、コードがCやC++(あるいは少なくとも根本的な理由)から移植されたソフトウェアのバックグラウンドを持っている人から移植されていると感じます。

とにかく、利用可能なバイト数のチェックに戻ると、シリアルポートデータが別のスレッド/ BackgroundWorker/Taskハンドラでフラッシュされない限り、私は特にそれが2回チェックされる理由は見当たりませんコード化される。
もっと速くするには?実際には、シリアルポートに実際にデータがある場合は、追加の遅延が発生するためです。それは私にとってそれほど意味がありません。

スニペットを少し良くするもう1つの方法は、ReadExisting()を使用してポーリングすることです。

そうでない場合は、SerialPort BaseStreamを使用して非同期メソッドを検討することもできます。

あなたのコードベースの残りの部分、つまりコンテキストにアクセスすることなく、言い表すのはかなり難しいです。

目的/プロトコルの詳細については、何をすべきかについてのヒントを示します。さもなければ、私はちょうどこれが文脈の中でもう一度、コード化されていないと思われると言うことができます。

ハンスがUIの反応性について言及したことは、実際にはあなたのスニペットがUIではないスレッドで実行されていることを願っています(あなたのポストでUIがポーリングしているまだスニペットが別の労働者のためにあることを望む)。

UIスレッドの場合は、UIが実際にユーザーのやりとりに反応しないようにThread.Sleep呼び出しがあるたびにブロックされ、エンドユーザーに不満を感じることがあります。

DataReceivedイベントを購読し、ハンドラを使用して必要な/必要な処理を実行することもできます(バッファや値の比較など)。

monoはまだこのイベントのトリガーを実装していませんが、プレーンなMS .NETの実装に対して実行している場合は、マルチスレッドの手間がなくても問題ありません。要するに

:スレッド(S)、それはUIがある場合

  • が、その後介して別のスレッドを使用するUIの応答性についてのあなたのスニペットのケアと心を取って(している)されて

    • チェックスレッド、BackgroundWorker(スレッドプール)、またはタスク。UIスレッドの同期の
  • の手間を避けるために
  • ストリーム非同期メソッドは、目的が本当に二重の300ミリ秒のスレッドスリープメソッド呼び出し
  • に値するかどうかをあなたが直接、代わりに文字列を取得することができます参照してください後者のチェックで、自分でバイトを収集するのではなく、操作を実行する場合(選択されたエンコーディングがあなたのニーズを満たすことができる場合)収集すること。
+0

ストリーム非同期メソッド(例: 'port.BaseStream.ReadAsync')は別のスレッドを使用しません....それは良いことです。複数のスレッドからデータ構造へのアクセスを同期させることや、スレッド間のUIコントロールの使用を整理することを心配する必要はありません。イベントハンドラが 'await'を返すか呼び出すたびに、データ構造を一貫した状態にしておきます。 –

+0

@BenVoigt http://referencesource.microsoft.com/#mscorlib/system/io/stream.cs,e224b4bec8748849 async/awaitはTaskを使用しているため、Threadpoolです。したがって、別のスレッドで実行できますが、必ずしもそうではありません。 私が間違っていると思われる場合は、もう少し説明してください。 特にUIが関連している場合は、同期に関して合意しました。 – Ehouarn

+1

タスクオブジェクトは、デフォルトで現在のコンテキストで実行され、UIアプリケーションではデフォルトのコンテキストはUIメッセージディスパッチャーです。余分なスレッドは使用されません。これで作業をワーカースレッドに強制することができますが、それはI/O、特にシリアルI/Oに対しては愚かなことになります。ワーカースレッドは、CPUバインド操作にのみ有効です。 –

関連する問題