2017-07-12 25 views
0

開発中のWinform GUIを使用してシリアルポート上でマイクロコントローラと通信しています。C#serialPort_DataReceivedイベントでフィードバックを待つ

私は、あらかじめ定義されたプロトコルに従って一連のコマンドを送信し、マイクロコントローラからフィードバック文字列を受信して​​います。

コマンドを送信した後で特定のフィードバックを待つ簡単な方法があるのだろうかと思います。

など。

  1. 設定した時間のための
  2. 待ちます(数分、数秒することができる)
  3. が時間内にあれば、フィードバックを表示し、次のコマンド/アクション
を発行するために進んでコマンドを送ります

フィードバックが時間内に受信されない場合、タイムアウトが発生し、エラーメッセージが表示されます。データが時間内に戻った場合は、待機方法を直ちに停止し、次のアクション・コースに進む必要があります。私はフィードバックを待っている間にUIをブロックしたくありません。

私はデータを受け取るために次のコードを使用しています。

delegate void SetTextCallback(string text); 

    private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e) 
    { 
     try 
     { 
      string receivedData = serialPort1.ReadExisting(); 
      SetText(receivedData); 
     } 
     catch (IOException exception) 
     { 
      MessageBox.Show(exception.Message, "Message", MessageBoxButtons.OK, MessageBoxIcon.Error); 
     } 
     catch (Exception exception) 
     { 
      MessageBox.Show(exception.Message, "Message", MessageBoxButtons.OK, MessageBoxIcon.Error); 
     } 
    } 

    private void SetText(string text) 
    { 
     if (this.textBoxReceive.InvokeRequired) 
     { 
      SetTextCallback d = SetText; 
      this.Invoke(d, new object[] { text }); 
      //this.Invoke(new Action(() => { this.textBoxReceive.AppendText(text); })); 

     } 
     else 
     { 
      if (text.Length > 15) 
      { 
       CheckPosition(text); //To check for a position number from the feedback string 
      } 
      this.textBoxReceive.AppendText(text); 

     } 
    } 

これは私の書き込み方法です。

private void SendCommand(int move, int trigger) 
    { 
     try 
     { 
      if (serialPort1.IsOpen) 
      { 
       string cmd = string.Empty; 
       //Format: { “move”: 0, “trigger”: 0} 
       cmd = "{ " + "\"move\": " + move + "," 
         + " \"trigger\": " + trigger 
         + " }" 
         + Environment.NewLine; 
       textBoxReceive.AppendText("Send:" + cmd + Environment.NewLine); // Display sent command 
       serialPort1.DiscardOutBuffer(); 
       serialPort1.DiscardInBuffer(); 
       serialPort1.Write(cmd); 

      } 
      else if (serialPort1.IsOpen != true) 
      { 
       MessageBox.Show(@"Lost COM Port.", "Message", MessageBoxButtons.OK, MessageBoxIcon.Error); 
      } 

     } 
     catch (IOException e) 
     { 
      MessageBox.Show(e.Message, "Message", MessageBoxButtons.OK, MessageBoxIcon.Error); 

     } 
     catch (Exception ex) 
     { 
      MessageBox.Show(ex.Message, "Message", MessageBoxButtons.OK, MessageBoxIcon.Error); 

     } 

    } 

私は遅延のために苦労しているクリックボタン方式を持っています(///開始待ち)。

private void buttonDet_Click(object sender, EventArgs e) 
    { 

     ResetPositionMark(); 
     serialPort1.DiscardInBuffer(); 
     //Position 1 at Initial Stage 
     textBoxStatus.AppendText("Position 1: Init/Home." + Environment.NewLine); 
     SendCommand(0,1); //Trigger LED1 
     textBoxStatus.AppendText("LED1 triggered." + Environment.NewLine); 
     Thread.Sleep(200); 
     ///Camera Capture 

     //============== Position 2 ============== 
     SendCommand(2,0); //Move to Position 2 
     textBoxStatus.AppendText("Moving to Position 2..." + Environment.NewLine); 
     **///Start waiting** 
     if (timeout) 
     { 
      textBoxStatus.AppendText("Position 2 Timeout reached." + Environment.NewLine); 
     } 
     else 
     { 
      textBoxStatus.AppendText("Data received in time." + Environment.NewLine); 
      textBoxStatus.AppendText("Position 2 OK." + Environment.NewLine); 
      SendCommand(0, 2); //Trigger LED2 once the motor reaches the position 2 
      textBoxStatus.AppendText("LED2 triggered." + Environment.NewLine); 
     } 


     ///Camera Capture 

     //============== Position 3 ============== 
     SendCommand(3,0); //Move to Position 3 
     textBoxStatus.AppendText("Moving to Position 3..." + Environment.NewLine); 
     **///Start waiting** 
     if (timeout) 
     { 
      textBoxStatus.AppendText("Position 3 Timeout reached." + Environment.NewLine); 
     } 
     else 
     { 
      textBoxStatus.AppendText("Data received in time." + Environment.NewLine); 
      textBoxStatus.AppendText("Position 3 OK." + Environment.NewLine); 
      SendCommand(0, 3); //Trigger LED3 once the motor reaches the position 2 
      textBoxStatus.AppendText("LED3 triggered." + Environment.NewLine); 
     } 

     ///Camera Capture 


     SendCommand(1, 0); //Move back to Home position (position 1) 
     textBoxStatus.AppendText("Moving Home to Position 1..." + Environment.NewLine); 
     **///Start waiting** 
     if (timeout) 
     { 
      textBoxStatus.AppendText("Back to Home Timeout!" + Environment.NewLine); 
     } 
     else 
     { 
      textBoxStatus.AppendText("Data received in time." + Environment.NewLine); 
      textBoxStatus.AppendText("Home now." + Environment.NewLine); 
     } 
    } 

私はスレッドとManualResetEventに精通しておりません、など

コードサンプルで好ましくは、データを待つために最善の方法を確認するために助けてください。

ありがとうございます。ここでのパラメータは、ほとんどのために話している

  Task.Factory.StartNew(() => { 
     }, _canecellationTokenSource.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default); 

:によって行われることが 、アイデアはあなたがコマンドの送信を開始するときに別のスレッドを作成することであるhttps://1drv.ms/u/s!AnSTW4R3pQ5uitAnyiAKTscGPHpxYw

:ここ

答えて

0

はシンプルなソリューションです自分自身:

  1. このスレッド/タスクで実行したい機能は
  2. ですか? 210
  3. キャンセルトークン。コマンドを送信する必要がなくなったときにスレッドを終了できるようにします。
  4. "LongRunning"オプションは、これが長期実行タスクであることを示します。あなたはそれについてもっと読むことができますhere
    1. デフォルトスケジューラに渡す。

あなたが何をする必要があるか次は、AutoResetEventのインスタンスを作成することです。それはあなたが読むことができる詳細でどのように動作するMSDN。しかし、一言で言えば、オープンとクローズの2つの状態を持つスイッチです。デフォルトでは、コンストラクター内のfalseパラメーターはそれをクローズします。 シリアルポート(DataReceived)からのイベントハンドラで、AutoResetEventを "開く"ようにします。だから、これを行う:今

dataReceivedEvent.Set(); 

を、あなたはAutoResetEventは、「開く」されるのを待つコマンドを発行すると、あなたは、このように、あなたが待って喜んでいる時間を指定する場合:

var succeeded = dataReceivedEvent.WaitOne(TimeSpan.FromSeconds(3)); 

これは、3秒以内にAutoResetEventが開かれていない場合、待機を停止し、失敗を報告することを意味します。基本的には、指定された時間枠で開かれていない場合はfalseを返し、そうであればtrueを返します。 「自動」リセットイベントであるため、待機した後に自動的に「閉じる」ので、手動でリセットする必要はありません。

残りはあなたがすでに持っているものです。 invokeを使用してUIと対話し、コマンドを読み書きします。

public class Communicator 
{ 
    CancellationTokenSource _canecellationTokenSource = new CancellationTokenSource(); 
    List<Command> _scenario = new List<Command>(6) 
    { 
     Command.Trigger(1), 
     Command.MoveTo(2), 
     Command.Trigger(2), 
     Command.MoveTo(3), 
     Command.Trigger(3), 
     Command.MoveTo(1) 
    }; 

    public void Start(ListBox feedbackControl) 
    { 
     Task.Factory.StartNew(() => { 
      var dataReceivedEvent = new AutoResetEvent(false); 
      var ct = _canecellationTokenSource.Token; 
      var controller = new DummyMicrocontroller(); 
      DataReceivedEventHandler onDataReceived = (cmd) => { dataReceivedEvent.Set(); }; 
      controller.DataReceived += onDataReceived; 
      foreach (var cmd in _scenario) 
      { 
       if (ct.IsCancellationRequested) 
       { 
        AddItemSafe(feedbackControl, $"Operation cancelled..."); 
        break; 
       } 

       AddItemSafe(feedbackControl, cmd.GetMessage(Command.MessageType.Info)); 
       controller.Send(cmd); 
       var succeeded = dataReceivedEvent.WaitOne(TimeSpan.FromSeconds(3)); 
       var messageType = succeeded ? Command.MessageType.Success : Command.MessageType.Error; 
       AddItemSafe(feedbackControl, cmd.GetMessage(messageType)); 
      } 

      AddItemSafe(feedbackControl, $"Finished executing scenario."); 

      controller.DataReceived -= onDataReceived; 

     }, _canecellationTokenSource.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default); 

    } 

    public void Stop(ListBox feedbackControl) 
    { 
     AddItemSafe(feedbackControl, $"Attempting to cancel..."); 
     _canecellationTokenSource.Cancel(); 
    } 

    private void AddItemSafe(ListBox feedbackControl, object item) 
    { 
     if (feedbackControl.InvokeRequired) 
     { 
      feedbackControl.Invoke((MethodInvoker)delegate { AddItemSafe(feedbackControl, item); }); 
     } 
     else 
     { 
      feedbackControl.Items.Add(item); 
     } 
    } 
} 

UIはそれ自身のスレッドにとどまり、影響を受けません。 私はマイクロコントローラを用意していないので、ダミーシミュレータを書く必要がありました:)

+0

ありがとう、BerserkerDotNet!これらのマルチスレッドの用語やコンセプトに慣れていないので、どのメソッドが何をしているのか、どのメソッドを呼び出すのか、どのように私のメソッドを変更するのかについて、初心者のレベルまで単純化してください。 – TLT

+0

私は私の答えをより説明するように更新しました。私が提供したソリューションを使用するためのコードを更新するのは難しいことではありません。 DummyMicrocontrollerをあなたの実際のシリアルポート駆動コントローラで置き換えて、手元にあるコマンドの配列を持っていることを考えてください。そうすれば、コードを複製したり、別のコマンドを発行したりする必要がなくなります。 – BerserkerDotNet

+0

私に辛抱強くご指摘いただきありがとうございます。私はあなたからたくさんのことを学んでいます。それは大きく役立ちます。別の質問:ここでのタイムアウトは3秒に固定されています。コマンドを送信する必要がある場合、30分後にPCB/UCレポートが完了した後、私に報告してください。各コマンドによってuCには異なる処理が行われるため、異なる処理には時間がかかります。 – TLT

関連する問題