none
SerialPortコンポーネントを使用してACK待ちするには RRS feed

  • 質問

  • SerialPortコンポーネントを使用した機器制御ソフトの作成を検討しています。
    制御コマンドを発行後に必ず機器側からのACKを待つ必要があるのですが、
    SerialPortコンポーネントを使用した場合はどのようにするのが一般的なのでしょうか?

    データの受信は別スレッドで行われるということでしたので、コマンド発行後に
    System.Threading.MonitorでWaitし,データ受信したらPulseというような
    やり方で良いのでしょうか?

    何か定石のような手法がありましたら教えていただきたいのです。
    どうぞよろしくお願いいたします。

    2011年2月28日 7:14

回答

  • 新たに監視スレッドを立ち上げる必要は無いと思います。

    SerialPortの受信イベントは別スレッドで発生されるものですが、実装する場合は、通常のイベント処理と同じような実装になると思います。

    ※余談ですが、画面系の操作は、現在バインドされているスレッドからのみ有効なので、Invokeメソッドでメインスレッドのdelegateをコールする必要があります。

    問題は、DataReceivedイベントがデータが届くたびにバンバン発生することです。

    そのため、現在受信したデータ長を、チェックして、規定のデータ長であれば、受信するという方式をとる必要があると思います。

     

    あと、「Monitorでwaitし、受信したら、Pulse」と書かれてますが、

    この通りに実装すると、メインスレッドが待ち状態になり、固まってしまうのでは?

     

    なので、DataReceivedイベントのタイミングではなく、

    決められたTimerの時間間隔でSerialPortのデータを読み取る、でもいいと思います。

    この方法ですと、「命令を送ってから、500msec以内にACKを受け取る」といった仕様があったとしたら、

    ①命令を送信する

    ②タイマーを開始する

    ③500msec後にタイマイベントが発生するので、シリアルポートのデータを取得

    ④受信データのチェックをしてNGなら通信エラー

    とかどうでしょう?

     

    • 編集済み Keidome 2011年2月28日 11:08 誤字
    • 回答としてマーク 山本春海 2011年3月11日 8:16
    2011年2月28日 11:05

すべての返信

  • 新たに監視スレッドを立ち上げる必要は無いと思います。

    SerialPortの受信イベントは別スレッドで発生されるものですが、実装する場合は、通常のイベント処理と同じような実装になると思います。

    ※余談ですが、画面系の操作は、現在バインドされているスレッドからのみ有効なので、Invokeメソッドでメインスレッドのdelegateをコールする必要があります。

    問題は、DataReceivedイベントがデータが届くたびにバンバン発生することです。

    そのため、現在受信したデータ長を、チェックして、規定のデータ長であれば、受信するという方式をとる必要があると思います。

     

    あと、「Monitorでwaitし、受信したら、Pulse」と書かれてますが、

    この通りに実装すると、メインスレッドが待ち状態になり、固まってしまうのでは?

     

    なので、DataReceivedイベントのタイミングではなく、

    決められたTimerの時間間隔でSerialPortのデータを読み取る、でもいいと思います。

    この方法ですと、「命令を送ってから、500msec以内にACKを受け取る」といった仕様があったとしたら、

    ①命令を送信する

    ②タイマーを開始する

    ③500msec後にタイマイベントが発生するので、シリアルポートのデータを取得

    ④受信データのチェックをしてNGなら通信エラー

    とかどうでしょう?

     

    • 編集済み Keidome 2011年2月28日 11:08 誤字
    • 回答としてマーク 山本春海 2011年3月11日 8:16
    2011年2月28日 11:05
  • Keidome様

    コメントありがとうございます。

    おっしゃる通り、MonitorでWaitしてしまうとメインスレッドが待ち状態になってしまいますね。もしこれをやるならばコマンド発行は別スレッドから行う必要がありますね。

    お教えいただいたタイマーを使用する方法で検討してみます。ただし、今回の制御対象機器では明確なACKの応答時間の仕様がありませんでした。またタイマーの間隔を無駄に長くしてもレスポンスが悪くなるだけと思いますので、X msec毎にタイマーでACKをチェックし、Y回失敗したなら通信エラー、というような方法を考えてみます。

    2011年3月2日 7:26
  • シリアルポート関係は調べてもなかなか詳しく書いたところが無くて苦労しますよね。。。

     

    今更C++/CLIの話で適用できるかはわかりませんが、

    Data_Recievedイベント中にBeginInvoke()で別スレッドを呼び出し、

    そこで処理をするようにするのが良いかと思います。

    ___________________________

    private:

         System::Void deligate DLGTHoge(String^ hogestring);

         System::Void Hoge(String^ hogestring){

               // 実際のデータをこちゃこちゃいじる処理

          //serialPort->ReadExisting();は必ずしも送信されたデータを全て受信するとは限らないので、

              //(メソッド呼び出し時にバッファに存在するデータのみ全て取得するため)

              //グローバルな変数に受信データを+=したりするといいかもしれません。

         }

    private: System::Void Data_Recieved(){

         String^ StrRecieveString;

         DLGThoge^ DRHOGE = gcnew(this, &namespace::class::method);

         StrRecieveString = this->serialPort->ReadExisting(); //とりあえず全部取得

         this->BeginInvoke(DRHOGE, StrRecieveString);//データの扱いは他のスレッドに任せる。

    }

    ※適当に手打ちしたので、コピペして動くかは知りません。

    ______________________________

    こんな感じで処理したほうがタイマー待ちを行うよりスマートだと思います。

    ちなみにC++/CLIだとInvoke()を使用して立ち上げると、Application::Exit();

    がうまく働きません。なのでBeginInvoke()ですが、他の言語ではどうか知りません。

     

    これならば、データの受信が無いときはデータ受信待ち処理を

    メインスレッド内でやる必要が無いので効率がよくなると思いますが、

    いかがでしょうか?



    2011年5月25日 4:11