none
SERIALコントロールのDataReceivedイベントについて RRS feed

  • 質問

  • はじめて投稿します。

     

    シリアル通信プログラムを作成しているのですが、コマンドを送信後、受信データをすぐ処理したいのですが、うまくいきません。

    送信後、強制的にApplication.DoEvents()でイベントを発生させると意図した処理ができます。

     

    なぜ、Application.DoEvents() がないとDataReceivedの処理をしないのでしょうか?

     


    Delegate Sub AddMessageDelegate(ByVal sStr As String)


    Dim sREVDATA As String

     

    Private Sub btnConnect_Click(ByVal sender As System.Object, _
                                 ByVal e As System.EventArgs) Handles btnConnect.Click
        _Serial.Open()


        _Serial.Write("#AT" & vbCrLf) 

        System.Threading.Thread.Sleep(200)

     

        _Serial.Write("#RCAP" & vbCrLf) 

        System.Threading.Thread.Sleep(200)
        ’Application.DoEvents()

    ’ここでsREVDATAを処理

     

        _Serial.Write("#RPGSB" & vbCrLf) 

        System.Threading.Thread.Sleep(200)
        ’Application.DoEvents()

    ’ここでsREVDATAを処理


    End Sub

     

    Private Sub _Serial_DataReceived(ByVal sender As System.Object, _
                                     ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) _
                                     Handles _Serial.DataReceived
    Dim addmsg As New AddMessageDelegate(AddressOf AddMessage)

        Try
            sREVDATA = _Serial.ReadLine
        Catch ex As Exception
            sREVDATA = ex.Message
        End Try

        txtMessage.Invoke(addmsg, sREVDATA)

    End Sub

     

    Private Sub AddMessage(ByVal sStr As String)
        txtMessage.Text = txtMessage.Text + sStr + vbCrLf

    End Sub

    2008年5月19日 8:53

回答

すべての返信

  • 同じスレッドで動作しているからです。

    つまり、イベントは発生していますがキューに溜まっています。

    ボタンのイベントハンドラの途中で溜まったメッセージはハンドラを抜けた後に処理されることになります。

     

    DoEventsはその仕様を無視して溜まっているメッセージをその時点で処理させるステートメントです。

    DoEventsはあなたが期待しているイベントだけでなく溜まっているものすべてが実行されます。

    したがって、結果論ではなく十分にその副作用を理解して使用する必要があります。

    #普通なら同期で実行される処理が同時並行で実行されるとか。

    #例で言うとクリックを2回してハンドラが並行して実行されるとか、また前提の共有変数値が書き変わり条件が崩れるとか。

    #DoEventsのあとに変数を書き換える処理があり、他のイベントハンドラで書き換わったであろう値を期待した処理があっても

    #書き換えのステップより先にイベントハンドラ内の処理が実行されることだってあるということ。

     

    というわけで、期待した動きになると書かれていますが、それは「たまたま」です。

    たまたまというのは、イベントがWriteの結果を受けて発生するという仕様のためと思われます。

     

    今回の場合、後続の処理がイベントなのでどうあがいても非同期です。

    オブジェクトの仕様を変更できないので、Aの後のBという状態を自身で作ることになるでしょう。

    つまり、きっかけのコマンドを記憶してそれに対する後処理が完了したかどうかを監視するすることになると思います。

     

     

    というか、非同期とわかっていて同期実行したい理由は何でしょうか?

    イベントは発生理由とタイミングが明確なだけで、時間軸の概念は無くまた時間軸に依存性を持たせてはいけません。

    #もしそうなら関数の戻り値になっているはず。

     

    2008年5月19日 12:07
  • まどかさま、ご回答有難うございます。

     

    今回、3種のボタンがあり、同じコマンドを送り、返信内容が同じでもその後の処理の内容が異なります。

     

    今回のようにWriteの結果により、必ずイベントが発生するのであれば、

     

    _Serial.Write("#RCAP" & vbCrLf) 

    System.Threading.Thread.Sleep(200)

    sREVDATA = _Serial.ReadLine

     

    としても問題ないのでしょうか?

    2008年5月20日 0:42
  •  おんかな さんからの引用

    _Serial.Write("#RCAP" & vbCrLf) 

    System.Threading.Thread.Sleep(200)

    sREVDATA = _Serial.ReadLine

     

    としても問題ないのでしょうか?

     

    それで問題ないはずです。

     

    また、SerialPort.ReadLineメソッドの中でタイムアウト処理が実行されますので、おそらくThread.Sleepメソッドは不要です。

    タイムアウトは、SerialPort.ReadTimeoutプロパティで変更できます。

    2008年5月20日 6:12
  • 大嘘ついていました。。。m(_ _)m

    DataReceivedは別スレッドで発生すると明記されていましたね。。。

     

    が、逆になおさら非同期であることが確定したので、

    ボタンクリック内でDataRecievedが発生するタイミングをユーザー側が決めてしまうのは不可能です。

     

    元々書かれているように十分に余裕をもった時間の間待ち続けるか、更新完了フラグ等が立つまでループ

    といった処理しか想像できませんね。

     

    DataRecievedイベントが関係ないのなら続けて書けばいいと思います。

    2008年5月20日 7:19
  • まどかさん、西脇さんありがとうございました。

     

    続けて書いて処理してみます。

    2008年5月20日 10:59