2012-05-07 10 views
0

私はSerialPortを使用してハードウェアとのインターフェースをとるC#マルチスレッドアプリケーションを持っています。serialport multithread unsolicitedメッセージ

プログラムは主にコマンドレスポンスシーケンスですが、内部エラーのためにハードウェアが迷惑な「RESET」メッセージを送信することがあります。このとき、ソフトウェアは特定の値を設定する一連のコマンドを送信して再初期化する必要があります。 (スレッドプールから)

つ以上のスレッドは、私はdatareceivedイベントで応答をチェックし、TryInitialize()にしてTakeSampleNowの他のスレッドはロック待ちができ

public class ALComm 
{ 
    private readonly AutoLoaderManager _manager; 
    private readonly AutoResetEvent dataArrived = new AutoResetEvent(false); 
    private SerialPort _alPort; 
    private string _alResponse; 

    .... Code to init _alPort and attach datareceived event etc 

    public void TakeSampleNow() 
    { 
     if (Monitor.TryEnter(_alPort, 1000)) //let's wait a second 
     { 
      _manager.MessageList.Enqueue("Try sampling"); 
      try 
      { 
       Send("Command1"); 
       string response = Receive(); 

       switch(response) 
       { 
        case "X": blah blah.. 
        case "Y": blah blah.. 
       } 

       Send("Command2"); 
       string response = Receive(); 

       while(response != "OK") 
       { 
        Send("Command3"); 
        string response = Receive(); 
        Send("Command2"); 
        string response = Receive(); 
       } 
      } 
      finally 
      { 
       Console.WriteLine("Releasing port"); 
       //Thread.CurrentThread.Priority = ThreadPriority.Normal; 
       Monitor.Exit(_alPort); 
      } 
     else 
     { 
      _manager.MessageList.Enqueue("Port is busy!!!"); 
     } 
    } 

    public string Receive() 
    { 
     string inString = null; 

      dataArrived.WaitOne(1000); 
      inString = _alResponse; 

     return inString; 
    } 

    private void AlPort_DataReceived(object sender, SerialDataReceivedEventArgs e) 
    { 
     _alResponse = _alPort.ReadLine(); 

     //if (_alResponse.ToUpper().Contains("RESET")) 
     //{ 
     // _alState = AlState.Reset; 
     // TryInitialize(); 
     //} 

     dataArrived.Set();    
    } 

    private void TryInitialize() 
    { 
     Monitor.Enter(_alPort);  //lock so other threads do not access samplenow during initialization 
     try 
     { 
      string response; 

      Console.WriteLine("Initializing ... The AutoLoader"); 
      _alPort.DiscardInBuffer(); 

      Send("CommandX"); 
      response = Receive(); 

      --- blah blah 

      _alResponse = ""; 
     } 
     finally 
     { 
      Monitor.Exit(_alPort); 
     } 
    } 

TakeSampleNow()を実行しようとすることができますロックを解除するには、_alResponseに "RESET"が含まれていて、メソッドから戻ってくる場合は、各応答をチェックする必要があります。それはそれをより複雑にします。

私はこれをよりうまくやり遂げることができます。私はそれが状態機械であると信じていますが、それを概念化することはできません。

+1

迷惑な応答は大きな問題です。特に「リセット」と表示されたときは、「落ちてしまいました。あなたが努力する必要があるのは、そのような応答を*決して*得ることではありません。それがデバイスのものだったので、あなたがそれを得るときあなたのマシンを再起動します。 –

+0

私もサービスを再開したいと思っています。しかし、それはブラウザによってユーザによって監視されるWindowsサービスです。 – cheedep

答えて

1

あなたはプロトコルの詳細を提供していません。コマンド/ rsponseペアが重複するかどうか、もしそうであれば応答がコマンドとどのようにマッチするかは言いません。

これは、状態エンジンで実行できるはずです。イベントのBlockingCollectionを待機する独自のスレッドで状態マシンを実行します。プロトコルを実行して受信バイトを解析してメッセージにするには、 'SerialRecv'スレッドも必要です。

私はただ1つの 'SerialEvent'クラスを使用して、イベントをSMキューに入れます。クラスには、rxバッファ、txData、解析データ、tx文字列をアセンブルするデータ、例外/ errorMessフィールドなどのイベントとメンバーを記述するための列挙型が必要です。完了した要求/応答をディスプレイまたはロガーに転送する)。私はstraightaway考えることができます

一部のイベント:EsmError、EsmLog、EsmDisplay:EsmNewRequestResponse、EsmRxData、EsmResetRx

イベントの列挙型は、いくつかの段階として、SMで使用されていない他の値、例えばを有することができます。

タイムアウトが必要な場合は、SM入力キューのtake()をタイムアウトすることでタイムアウトを生成できます。

はい、私は脱落したことがあります。

複数のスレッドがSerialEventインスタンスを「一度に」発行する場合、SMは最初のものを処理している間に新しいSerialEventを取得します。 SMは処理を待つSerialEventを保持するために別のキュー/両端キューを必要とします。 BlockingCollection /スレッドによるSMのシリアライズにより、この '保留中の'キューはスレッドセーフである必要はありません。 SMは、要求/応答が完了した後に、処理待ちのキューが存在するかどうかを調べるために、この保留キューをチェックする必要があります。

要求/応答を複数のスレッドから同期的に処理するには、要求スレッドは待機する必要があります。 SerialEventクラスのAutoResetEventはそうです。 SerialEventをシステムに送信すると、SerialEventインスタンスがキューに入れられ、AutoResetEventを待機します。インスタンスの処理が完了すると(すなわち、応答受信、エラーまたはタイムアウト)、SMはイベントを設定し、元のスレッドはSerialEventインスタンスをデータで埋めて実行されます。

次へ - SerialEventクラスは、/ CGを継続的に作成するのではなく、プールするほうが良いかもしれません。それはプールとして動作する別のBlockingCollectionを必要とします。

+0

Martinさん、コマンドの応答シーケンスは重複しません。特定の時点でハードウェアにコマンドを送信するのは1つのスレッドしかなく、\ r \ nで終わる応答が受信されます。受信したメッセージは_alResponseフィールドに格納され、コマンド送信スレッドによって読み取られます。私はあなたの反応を全般に理解していますが、私の場合にはどうすれば使用できるかを理解する時間が必要です。 – cheedep

1

シリアルポートを読み取ろうとしているスレッドが複数あることは望ましくありません。ポートを読み取るだけで何もしない単一のスレッドが必要です。いくつかのデータを取得すると、複数のサンプルスレッドで処理できるキューまたは同様のデータ構造にメッセージを配置します。このようにして、単一のリーダースレッドが確実にRESETメッセージを見つけて反応することができます。

+0

ありがとうTJD。 ReadLine()の後にDataReceivedイベントのスレッドによってトリガされるAutoResetEventを待機している特定の時間にコマンドを送信するスレッドは1つだけです。 – cheedep