2009-06-10 10 views
3

シリアルポート(たとえばCOM1)からデータを常に読み取る必要のあるシリアルポートコードがあります。しかし、これは非常にCPU集約的で、ユーザーがウィンドウを動かしたり、シリアル・ラインを介して受信されたバイトなどの多くのデータがウィンドウに表示されていると、通信が乱れてしまいます。次のコードを考慮System.IO.Ports.SerialPortとマルチスレッド

void port_DataReceived(object sender, SerialDataReceivedEventArgs e) 
{ 

byte[] buffer = new byte[port.ReadBufferSize]; 

var count = 0; 

try 
{ 
    count = port.Read(buffer, 0, buffer.Length); 
} 
catch (Exception ex) 
{ 
    Console.Write(ex.ToString()); 
} 

if (count == 0) 
    return; 

//Pass the data to the IDataCollector, if response != null an entire frame has been received 


var response = collector.Collect(buffer.GetSubByteArray(0, count)); 

if (response != null) 
{ 
    this.OnDataReceived(response); 
} 

コードは、データのストリームが 一定であり、データ(フレーム/パケット)について分析されなければならないように収集することが必要です。

port = new SerialPort(); 

    //Port configuration code here... 

    this.collector = dataCollector; 

    //Event handlers 
    port.DataReceived += new SerialDataReceivedEventHandler(port_DataReceived); 
    port.Open(); 

ウィンドウに追加されたユーザーとの対話と何が存在しない場合は、 これは、すぐに通信が本当にめちゃくちゃます相互作用があると正常に動作しますが。

Dispatcher.BeginInvoke(new Action(() => 
{ 
    var builder = new StringBuilder(); 
    foreach (var r in data) 
    { 
     builder.AppendFormat("0x{0:X} ", r); 
    } 


    builder.Append("\n\n"); 

    txtHexDump.AppendText(builder.ToString()); 

    txtHexDump.ScrollToEnd(); 


}),System.Windows.Threading.DispatcherPriority.ContextIdle); 
}); 

をしかし、たとえ簡単な呼び出しが原因の問題をlog4netのために: タイムアウトは、例えば

....などが発生、これがすべてを台無しに。

はSERIALPORT通信 を最適化するために、任意のベストプラクティスはありますか誰かが私が間違ってやっているものを私に伝えることができます...

更新:場合

上記の多くのローミングサービスをしませんでした。私は非常に単純な(そして愚かな)少し例を作った:

class Program 
{ 
    static void Main(string[] args) 
    { 
     var server = new BackgroundWorker(); 
     server.DoWork += new DoWorkEventHandler(server_DoWork); 
     server.RunWorkerAsync(); 

     var port = new SerialPort(); 
     port.PortName = "COM2"; 
     port.Open(); 
     string input = ""; 

     Console.WriteLine("Client on COM2: {0}", Thread.CurrentThread.ManagedThreadId); 
     while (input != "/quit") 
     { 
      input = Console.ReadLine(); 
      if (input != "/quit") 
      { 
       var data = ASCIIEncoding.ASCII.GetBytes(input); 
       port.Write(data, 0, data.Length); 
      } 
     } 

     port.Close(); 
     port.Dispose(); 
    } 

    static void server_DoWork(object sender, DoWorkEventArgs e) 
    { 
     Console.WriteLine("Listening on COM1: {0}", Thread.CurrentThread.ManagedThreadId); 
     var port = new SerialPort(); 
     port.PortName = "COM1"; 
     port.Open(); 

     port.ReceivedBytesThreshold = 15; 
     port.DataReceived += new SerialDataReceivedEventHandler(port_DataReceived); 
    } 

    static void port_DataReceived(object sender, SerialDataReceivedEventArgs e) 
    { 
     var port = (SerialPort)sender; 
     int count = 0; 
     byte[] buffer = new byte[port.ReadBufferSize]; 
     count = ((SerialPort)sender).Read(buffer, 0, buffer.Length); 

     string echo = ASCIIEncoding.ASCII.GetString(buffer,0,count); 
     Console.WriteLine("-->{1} {0}", echo, Thread.CurrentThread.ManagedThreadId); 
    } 
} 

結果は次のようになります。

がCOM1に聞く:6 クライアントCOM2上:10 これは私が送っていくつかのサンプルデータであり、 ---> 6これは私がメインスレッド上で発生

だからポートからデータを読み取る送るいくつかのサンプルデータ....

が、これは私の問題を引き起こしているものの一部である可能性がありますがありますか?

+0

コードの最後のセクションはどこですか? 'データ'変数はどこから来ますか? –

+0

上記のコードは、IDataCollectorがイベントを発生させてフル・フレームのデータを収集したことを示すときに呼び出されます。それはWPF Windowsにあります。しかし、IDataTransportServerまたはIDataCollector実装のコードにlog.Warn( ".....")ステートメントを追加しても、SerialCommunicationは不正になります... 「データ」は、シリアルから読み込んだものです buffer.GetSubByteArray(0、count) – TimothyP

+0

ちょうどどのコードであっても、シリアル通信が間違ってしまう... – TimothyP

答えて

4

最後の結論として、イベントがメインスレッドで実行されるということは、Windowsアプリケーションでは当てはまらない可能性があります。これをコンソールでテストしないでください。

同調これに適切な方法である:最小4096

  • の前を許容ほど高いReceivedBytesThresholdを設定(およびそれを行う通常OKですが

    • は、十分な大きさのバッファを設定します Open())

    • Receivedイベントではできるだけ少なくする必要がある場合は、 データをQueueまたはMemoryStreamに渡します。 me

  • 2

    port.BytesToReadはこのように、ゼロより大きいあるまでは、データを読み取るためにport_DataReceived手順を書き換える必要があります。

    private void port_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e) 
    { 
         var port = (SerialPort)sender; 
         while (port.BytesToRead > 0) 
         { 
          int byte_count = port.BytesToRead; 
          byte[] buffer = new byte[byte_count]; 
    
          int read_count = port.Read(buffer, 0, byte_count); 
    
          // PROCESS DATA HERE 
    
         } 
    } 
    

    また、私はちょうど手順port_DataReceivedにキューリスト内のデータを挿入することをお勧めいたします、そしてデータを実行することになり別のスレッドで処理します。

    +0

    私がそれを行うと、ある時点で永遠に待ちます。 – TimothyP

    +0

    port_DataReceived ()関数またはport_DataReceived()が呼び出されませんか? 私は元の投稿に書いていない重要なことはほとんどありません: - port_DataReceivedはコールバック関数であり、それ自身のスレッドで実行されます。 COMポートに書き込まれたデータがある場合に呼び出されます。 新しいデータがCOMポートに書き込まれ、以前の呼び出し、つまり関数内でデータを処理していない場合にport_DataReceived関数への呼び出しが失敗する可能性があります。つまり、port.BytesToReadプロパティを確認することが重要です機能を残す。 –

    1

    古典的な解決策はFIFOバッファを持つことです。 FIFOのサイズが、多くの入力があり、プロセッサブロックが占有されているクリティカルなケースを処理するのに十分な大きさであることを確認します。

    --->|Reader|-->FIFO-->|Processor|--->FIFO2--->|Displayer| 
    
    11

    は、私は誰もがこれをキャッチしていない驚いています:

    は、あなたも2バッファシステムを持つことができます。 SerialPortクラスは、DataReceivedイベントを使用するときに独自のスレッドを使用します。これは、たとえば、サブスクライバが任意のフォーム要素にアクセスしている場合、InvokeメソッドまたはBeginInvokeメソッドのいずれかでサブスクライバを実行する必要があることを意味します。さもなければ、あなたはクロススレッド操作で終わる。古いバージョンの.netでは、予期せぬ動作(PCのCPUコアに依存する)とそれ以降のバージョンでは、例外が発生するはずです。

    +0

    +1、私はこれが質問に答えるかどうかはわかりませんが、これは私に過去に少し噛まれた重要な情報です。 – Brad

    関連する問題