web-dev-qa-db-ja.com

C#のシリアルポートからの読み取り

Readline()を使用しようとしましたが、データがドロップされます。Read()を使用してみましたが、次々に複数のパケットを取得する可能性があり、知る方法がないため、エラープルーフメソッドを使用する方法がわかりません。別のパケットが着信することを確認します。パケット間のBytesToReadは0であるため、使用できません。バッファにデータを読み取るときは、タイマーを使用するか、スレッドをスリープ状態にしてすべてのパケットが到着できるようにしますか?

迷っています。次に何を試すべきかわからない。

シリアルポートから出てくる文字列が\ nまたは\ rまたは\ r\nで終わるという保証はありません。ユーザーがPRINTを押したときにスケールから来るすべてのパケットを読み取るための絶対確実な方法が必要です。

誰かが私が好きなアイデアでここに答えました-すべてのパケットを一定時間待っていましたが、彼らは答えを消しました。再投稿できる可能性はありますか?

12
sarsnake

DataRecieved クラスの SerialPort イベントを聞いてみましたか?

public class MySerialReader : IDisposable
{
    private SerialPort serialPort;
    private Queue<byte> recievedData = new Queue<byte>();

    public MySerialReader()
    {
        serialPort = new SerialPort();
        serialPort.Open();

        serialPort.DataReceived += serialPort_DataReceived;
    }

    void serialPort_DataReceived(object s, SerialDataReceivedEventArgs e)
    {
        byte[] data = new byte[serialPort.BytesToRead];
        serialPort.Read(data, 0, data.Length);

        data.ToList().ForEach(b => recievedData.Enqueue(b));

        processData();
    }

    void processData()
    {
        // Determine if we have a "packet" in the queue
        if (recievedData.Count > 50)
        {
            var packet = Enumerable.Range(0, 50).Select(i => recievedData.Dequeue());
        }
    }

    public void Dispose()
    {
        if (serialPort != null)
            serialPort.Dispose();
    }
22
Samuel

少し前に同じプロセスを経ました。

「パケット」を読み取る唯一の方法は、パケットの開始と終了がストリームのどこにあるかという概念を持つことです。

から msdn

SerialPortクラスはデータをバッファリングし、BaseStreamプロパティに含まれるストリームはバッファリングしないため、2つは、読み取ることができるバイト数について競合する可能性があります。 BytesToReadプロパティは、読み取るバイトがあることを示すことができますが、これらのバイトは、SerialPortクラスにバッファリングされているため、BaseStreamプロパティに含まれるストリームにアクセスできない場合があります。

バックラウンドスレッド(BackgroundWorkerを使用できます)を使用して、データをバッファーに読み込みました。 SerialPort.Newlineプロパティを使用して終了文字を確実に設定できない場合(たとえば、変動するため)、ブロッキングSerialPort.Readline()を使用できないため、独自のパケット検出システムを実装する必要があります。方法。

SerialPort.Read()(またはSerialPort.ReadExisting()メソッドを使用した文字列)を使用してバイトバッファに読み込み、データで有効なパケットを検出したときにイベントを生成することができます。 Read()(およびReadExisting()を想定)はSerialPortのバッファーを空にするため、データを別の場所に保持する必要があることに注意してください。

SerialPort.ReadTimeoutを設定すると、TimeoutExceptionを処理でき、デバイスが送信していない状態を簡単に処理できます。これは、固定バイト数またはその他の終了していないスキームを使用している場合に、パケット検出をリセットするための良い方法です。 (部分的なパケットが必要ない場合は、タイムアウト時にSerialPort.DiscardInBuffer()を使用します)。

幸運を

10
Byron Ross

バイトはいつでも入ってくる可能性があるため、着信データのバッファリングは重要です。だからあなたはすべきです

  1. 着信データをバッファリングする
  2. バッファをスキャンして完全なデータを見つけます
  3. 使用済みデータをバッファから削除します

まだシリアルポートに問題があるのではないかと思います。もしそうなら、私はC#でシリアルポートプログラミング言語を開発しました、そしてそれは誰もが遭遇するほとんどすべての問題を解決すると信じています。

ぜひご覧になってみてください。例えば;次のようにシリアルポートからの着信データをバッファリングし、文字列操作を簡単に行うことができます。

state Init
  // define a global variable
  our $BUFFER = "";
  jump(Receive);
end state

state Receive
  recv();
  $len = length($DATA_PACKET);
  if("$len > 0") {
    $BUFFER += $DATA_PACKET;
    call(Parser);
  }
end state

state Parser
  // check if buffer matchs regular expression pattern
  if(match($BUFFER, "(?<WILLDELETE>.*?<STX>(?<DATA>.*?)<ETX>(?<CHECKSUM>[0-9A-F]{2}))")) {
    // Received complete data
    $lenData = length($WILLDELETE);
    $BUFFER = remove($BUFFER, 0, $lenData);

    // Do operations with the other parsed fields. $DATA and $CHECKSUM in this example.
  }
end state

プロジェクトはsourceforgeで無料で入手できます。ご不明な点がございましたら、お気軽にお問い合わせください。

プロジェクトのホームページ

ダウンロードリンク

3
albay