みんなの「教えて(疑問・質問)」にみんなで「答える」Q&Aコミュニティ

こんにちはゲストさん。会員登録(無料)して質問・回答してみよう!

解決済みの質問

C# シリアル通信でデータ受信時の欠損について

Visualstudio 2013 を使用して C# で開発を行っています。

SerialPort Classを使用してデータの送受信をするプログラムを作成しているのですが、
非同期でデータを受信する際にどうしてもうまくデータを取得出来ません。

5Byteのデータは正常に取得できるのですが、
その直後にくる40Byteのデータは、真ん中あたりの10数Byteや最後の10数Byteしか取れません。


serialPort.DataReceived に登録したイベント関数の中身です。

--------------------------------------------------------------------------------------
private void _serialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
bytesRead = 0;
// Initialize a buffer to hold the received data
byte[] buffer = new byte[this.serialPort.ReadBufferSize];

try
{
bytesRead = this.serialPort.Read(buffer, 0, buffer.Length);
if (true == serialPort.IsOpen)
{
serialPort.DiscardInBuffer();//受信バッファをクリアする
}

}
catch (Exception ex)
{
DataLog.Exception(ex);
}

//派生クラス用の処理
DeviceClassEventArgs _DeviceClassEventArgs = new DeviceClassEventArgs(buffer, bytesRead);
DeviceClassEvent(this, _DeviceClassEventArgs);
}
--------------------------------------------------------------------------------------

ネットの情報を参考に、
ReceivedBytesThreshold の値を期待するデータ量に逐一変えることで
とりあえず正常に取ることが出来たのですが、これでいいのでしょうか?
期待するデータ量がわからなかった場合は使えないのかなとも思います。

データが欠損してしまう理由、
上記の対処法以外の一般的な対処法など有りましたら教えて下さい。

その他参考になるページ等ありましたら教えていただけると大変助かります。

投稿日時 - 2014-06-19 22:42:20

QNo.8645416

困ってます

質問者が選んだベストアンサー

DataReceivedイベントが発生したときでも、
シリアルポートへの受信はまだ継続している可能性があるので
不用意にバッファクリアしてはいけない。
非同期の受信処理は、何かと難しいのです。

private void _serialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
  bytesRead = 0;
  // Initialize a buffer to hold the received data
  byte[] buffer = new byte[this.serialPort.ReadBufferSize];

  try
  {
    //bytesRead = this.serialPort.Read(buffer, 0, buffer.Length);
    //if (true == serialPort.IsOpen)
    //{
    // serialPort.DiscardInBuffer();//受信バッファをクリアする
    //}

    // 受信バッファにデータがなくなるまで繰り返し読込む
    while (true)
    {
      if (0 == serialPort.BytesToRead)
      {
        break;
      }
      buffer[bytesRead] = (byte)serialPort.ReadByte();
      bytesRead++;
      System.Threading.Thread.Sleep(0);

      // シリアルポートの受信バッファには、
      // ・必要なブロックの途中から受信している。
      // ・次のブロックの先頭部分も受信されている。
      // 可能性があるので、ここで必要なブロックだけRead()できたことを確認する。
      if (必要なブロックが正常に読めたか確認する関数())
      {
        break;
      }
    }
  }
  catch (Exception ex)
  {
    DataLog.Exception(ex);
  }

  //派生クラス用の処理
  DeviceClassEventArgs _DeviceClassEventArgs = new DeviceClassEventArgs(buffer, bytesRead);
  DeviceClassEvent(this, _DeviceClassEventArgs);
}

投稿日時 - 2014-06-20 13:50:03

お礼

>不用意にバッファクリアしてはいけない。

なるほど。確かにそうですね。
なにが起こっているのかわからなかったのですがやっと理解出来ました。
ありがとうございます。

投稿日時 - 2014-06-23 09:36:02

このQ&Aは役に立ちましたか?

0人が「このQ&Aが役に立った」と投票しています

回答(4)

ANo.4

最初に _serialPort_DataReceived 関数で 5 Byte 読んで
その後 _serialPort_DataReceived 関数で 40 Byte を読んだら、
途中からしか取れなかった。 関数の中には、「通信ポートを
オープンしてるなら受信バッファをクリアする」
if (true == serialPort.IsOpen)
{
serialPort.DiscardInBuffer();//受信バッファをクリアする
}
という処理が入っているのですから当然そうなるのではないですか?

読込の関数を読んだ時点からデータの受信を開始するとした為、
それまで受信していたデータを捨ててまっさらな状態にする為
受信バッファをクリアしてるのですから

そう考えれば解決方法は簡単で
受信バッファをクリアするの部分を受信バッファにある分、
全て別のバッファに読む様に修正して

その別のバッファの中身を捨てるか、この後の本来の受信処理と
つなげて処理するかを、通信フロー等を考慮しながら決めて
通信プログラムを作成します

投稿日時 - 2014-06-20 14:09:11

お礼

>読込の関数を読んだ時点からデータの受信を開始するとした為、
それまで受信していたデータを捨ててまっさらな状態にする為
受信バッファをクリアしてるのですから

なるほど。確かにそうですね。
なにが起こっているのかわからなかったのですがやっと理解出来ました。
ありがとうございます

投稿日時 - 2014-06-23 09:37:58

ANo.2

一般的な対処法は意味を持ちません。

通信相手がどのような機器でどのようなデータをどのようなプロトコルで通信するのか、相手側のプログラムや設定を変更できるか等で全く変わってしまいます。

投稿日時 - 2014-06-20 02:08:28

ANo.1

>データが欠損してしまう理由、
>上記の対処法以外の一般的な対処法など有りましたら教えて下さい。
1.フロー制御を行う
2.ボーレートを下げる     等が一般的かと思います

場合によってはキャラクタ間にウエイト入れたり(これやると当然レスポンスは落ちます)

1を行うときは
信号線でのフロー制御を行う場合ケーブルも
きちんと制御線に対応していることが条件となります

キャラクタベースのフロー制御(XON/XOFF)はやり取りしてるデータによっては
使えない場合があります(バイナリデータやり取りする場合などは無理)

とりあえず送信側でデータが落ちてるのか受信側で
データ落ちしてるのかを確認するのが先ではないかと思います
オーバランとかアンダーランとかのステータス確認してみてください

あくまで参考までに

投稿日時 - 2014-06-20 00:07:56

あなたにオススメの質問