2016-05-28 4 views
0

シリアルポート経由でマイクロコントローラと通信するアプリケーションがあります。バックグラウンドワーカーでコントローラーのステータスを定期的に確認し、ユーザーがコマンドを送信して応答を受け取ることで、ユーザーインターフェイスを介して非同期にコントローラーと対話できるようにします。SerialPortの操作を同期させる方法は?

UIやバックグラウンドワーカーは、シリアル通信静的クラスを使用します。

static class SerialCommunication 
{ 
    static SerialPort serialPort; 
    static int readWriteTimeout = 1000; // [ms] 
    static int waitForTransmissionTimeout = 2; // [s] 
    static string rxData = "", endOfString = "" + char.MinValue; 
    static bool WaitingForSerialData = false; // Set in SerialWrite(), cleared in SerialRead() 

    [...] 
    public static string SerialRead() 
    { 
     try 
     { 
      rxData = serialPort.ReadTo(endOfString); 
     } 
     catch (TimeoutException) 
     { 
      WaitingForSerialData = false; 
      throw new Exception(Properties.Resources.serial_read_timeout); 
     } 
     WaitingForSerialData = false; 
     return rxData; 
    } 

    public static void SerialWrite(string text) 
    { 
     DateTime start = DateTime.Now; 
     while (WaitingForSerialData) // Avoids the situation in which a command executed on a thread receives the response for the command executed from a different thread 
     { 
      if (!WaitingForSerialData) 
      { 
       try 
       { 
        WaitingForSerialData = true; // All commands wait for confirmation/data, so it is normal to set this boolean value for every serial transmission 
        serialPort.Write(text); 
       } 
       catch (TimeoutException) 
       { 
        throw new Exception(Properties.Resources.serial_write_timeout); 
       } 
      } 
      else 
      { 
       System.Threading.Thread.Sleep(100); 
       if ((DateTime.Now - start).Seconds >= waitForTransmissionTimeout) 
       { 
        throw new Exception("Timeout"); 
       } 
      } 
     } 
    } 
} 

シリアルポートは、アプリケーションの起動時に初期化されます。 SerialWriteおよびSerialReadは、UIまたはバックグラウンドワーカーで連続して呼び出されます。

コマンドが別のスレッドで実行された別のコマンドからの応答を受け取ることを避けたいです。現在、コマンドを送信する前に(SerialReadを終了するために)コマンドを受け取るためにSerialWriteで待機する実装を実装しましたが、SerialWriteが実行された場合にUIをブロックすることができます(最大waitForTransmissionTimeout秒間)そこ。

SerialPortの操作を同期するにはどうすればよいですか?

+1

だから、_all_コマンドはSerialWriteが続いています応答を受信するSerialReadによって?もしそうなら、なぜ単一のSendCommandなどで読み書き操作を組み合わせないのですか? – Evk

+0

@Evkしかし、SendCommandがバックグラウンドワーカースレッドなどで実行され、UIの実行の直後(実行が終了する前)に何が起こるのでしょうか? –

+1

シンプルなロックで全体の操作(write + following read)を保護する必要があります。次に、バックグラウンドワーカーが実行を終了する前にUIがコマンドを送信すると、完了するまで待機します。もちろん、UIスレッドからその操作を実行しないでください。バックグラウンドスレッドから実行し、完了したら結果をUIスレッドに返します(ユーザーに結果を表示する)。そのようなことをUIスレッドで直接実行しないでください。 – Evk

答えて

1

私は、全体の状況を知らないが、それはあなたがちょうどこのように、あなたの書き込みが読み取りに続いて直列化するために、単純なロックを使用することができます私には思える:

static class SerialCommunication 
{ 
    static SerialPort serialPort; 
    static string endOfString = "" + char.MinValue; 
    static readonly object _commandLock = new object(); 

    public static string SendCommand(string text) { 
     lock (_commandLock) { 
      SerialWrite(text); 
      return SerialRead(); 
     } 
    } 

    private static string SerialRead() { 
     try { 
      return serialPort.ReadTo(endOfString); 
     } 
     catch (TimeoutException) { 
      throw new Exception(Properties.Resources.serial_read_timeout); 
     }    
    } 

    private static void SerialWrite(string text) { 
     try { 
      serialPort.Write(text); 
     } 
     catch (TimeoutException) { 
      throw new Exception(Properties.Resources.serial_write_timeout); 
     } 
    } 
} 
+0

ありがとう! UIに関しては、シリアル・オペレーションのためにバックグラウンド・ワーカーを使用すべきですか、よりコンパクトなコードを作成するソリューションがありますか? –

+0

あなたのバージョンのC#コンパイラがそれをサポートしている場合は、バックグラウンドスレッドを使用するかTask.Runを使用するか、async \ awaitを使用できます。これについては、https://stephenhaunts.com/2014/10/14/using-async-and-await-to-update-the-ui-thread/など、さまざまな場所で読むことができます。 – Evk

+0

もう一度ありがとう! –

関連する問題