[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。
前回のSerialPortコントロールを使い方その3では、外部装置から「垂れ流し」的にデータが向こうからやってくる場合 を想定していました。
メイン処理側ではタイマイベントにより一定の時間ごとに受信があったかどうかをみて処理を行っていましたが、この方法はあまりスマートではありません。 そこで、データを受信した時のみ処理が出来る様に、メイン側にイベントとして通知する方法を考えてみました。
イベントと言えば以下の様にテキストボックスの KeyPress イベントなどがあります。 関数宣言の第2引数の KeyPressEventArgs を通して入力されたキーデータが通知されます。
今回はデータ受信イベントなので、イベント関数の第2引数の KeyPressEventArgs を受信用のものに変え 受信データを通知する様にします。
先ずはイベント発生時のアーギュメントの宣言ですが、以下の様なクラスです。 イベントアーギュメントの元クラスの EventArgs を継承し SerialEventArgs と宣言します。 追加するプロパティとして文字列の受信データのやり取りを宣言します。
データ受信イベント・通知アーギュメント用クラス
Public Class SerialEventArgs Inherits EventArgs Private mstrReceivedData As String ' 受信データを取得 Public Property ReceivedData() As String Get Return mstrReceivedData End Get Set(ByVal value As String) mstrReceivedData = value End Set End Property End Class
この通知アーギュメントクラスを利用して、「その3」のシリアル通信クラスを変更します。 変更内容は以下の通りです。
- Event を使ってイベントを定義を行う。
- 通知アーギュメントクラスを生成し、受信データをその中に設定する。
- データの終了コードを受信した時に、RaiseEvent を使ってイベントを発生する。
SerialPortコントロールの使い方その4(通信用クラス)
Imports System.IO.Ports ''' ----------------------------------------------------------------------- '''''' シリアル通信クラス・垂れ流し受信イベント付き ''' ''' ----------------------------------------------------------------------- Public Class ClsSerialRcv '''''' シリアルポートクラス ''' Private SerialPort As SerialPort '''''' 読込データ格納先 ''' '''データ受信イベントで蓄えられる Private CmdBuf() As Byte '''''' STX受信フラグ(装置の送信が開始された証拠) ''' Private fSTX As Boolean = False '''''' 最終受信文字列(受信データからSTX,ETXを除いたもの) ''' Private strLastRxData As String = "" '''''' 送受信文字列の開始コード(STX:0x02) ''' Private Const CMD_STX As Byte = &H2 '''''' 送受信文字列の終了コード(ETX:0x03) ''' Private Const CMD_ETX As Byte = &H3 '''''' データ受信イベント定義 ''' Public Event ReceivedData(ByVal sender As Object, ByVal e As SerialEventArgs) ''' ----------------------------------------------------------------------- '''''' コンストラクタ ''' ''' <param name="SerialPort">シリアルポートコントロール</param> ''' ----------------------------------------------------------------------- Sub New(ByVal SerialPort As SerialPort) 'シリアルポートの退避 Me.SerialPort = SerialPort '受信イベントのハンドラ設定 AddHandler Me.SerialPort.DataReceived, AddressOf DataReceivedHandler End Sub ''' ----------------------------------------------------------------------- '''''' 最終受信データプロパティ ''' '''受信データ文字列 ''' ----------------------------------------------------------------------- ReadOnly Property LastRxData As String Get Return Me.strLastRxData End Get End Property ''' ----------------------------------------------------------------------- '''''' ポートオープン ''' '''True:正常終了, False:エラー ''' ----------------------------------------------------------------------- Function Open() As Boolean '戻り値初期化 Open = False Try 'ポートチェック If Me.SerialPort.IsOpen = False Then '未オープンならば、オープンする 'ポート設定(ここは通信相手に合わせる) Me.SerialPort.PortName = "COM7" Me.SerialPort.BaudRate = 9600 Me.SerialPort.DataBits = 8 Me.SerialPort.Parity = Parity.None Me.SerialPort.StopBits = StopBits.One 'オープン Me.SerialPort.Open() End If '結果を返す Open = Me.SerialPort.IsOpen Catch ex As Exception 'エラー処理 End Try End Function ''' ----------------------------------------------------------------------- '''''' ポートクローズ ''' '''True:正常終了, False:エラー ''' ----------------------------------------------------------------------- Function Close() As Boolean '戻り値初期化 Close = False Try 'ポートチェック If Me.SerialPort.IsOpen = True Then 'オープン済みならば、クローズする Me.SerialPort.Close() End If '結果を返す Close = Not (Me.SerialPort.IsOpen) Catch ex As Exception 'エラー処理 End Try End Function ''' ----------------------------------------------------------------------- '''''' データ受信ハンドラ ''' '''''' [STX]から始まるデータをコマンドバッファに格納し[ETX]受信時に文字列に変換 ''' ''' ----------------------------------------------------------------------- Private Sub DataReceivedHandler(sender As Object, e As SerialDataReceivedEventArgs) Try Dim SP As SerialPort = CType(sender, SerialPort) 'バッファのバイト数チェック Dim DataLen As Integer = SP.BytesToRead If DataLen = 0 Then Exit Sub End If 'データ長チェック If DataLen > 4096 Then DataLen = 4096 End If '読込バッファの確保 Dim Buf(DataLen - 1) As Byte '読込 SP.Read(Buf, 0, DataLen) ' '受信バッファを先頭からチェックする For i As Integer = 0 To Buf.Length - 1 Select Case Buf(i) Case CMD_STX If Me.fSTX = True Then '再度のSTXはコマンドバッファをクリア Me.CmdBuf = Nothing End If 'STXフラグON Me.fSTX = True Case CMD_ETX If Me.fSTX = True Then 'STXフラグONの場合 'コマンドバッファを文字列変換 Me.strLastRxData = _ System.Text.Encoding.GetEncoding(932).GetString(Me.CmdBuf) '*** データ受信イベントを発生 *** Dim se As New SerialEventArgs se.ReceivedData = Me.strLastRxData '受信データの値をセット RaiseEvent ReceivedData(Me, se) 'STXフラグOFF Me.fSTX = False End If 'コマンドバッファクリア Me.CmdBuf = Nothing Case Else '[STX][ETX]以外 If Me.fSTX = True Then 'STXフラグONの場合 Dim intIdx As Integer = 0 If Me.CmdBuf Is Nothing Then '格納バッファの領域確保 Me.CmdBuf = Array.CreateInstance(GetType(Byte), 1) '先頭指定 intIdx = 0 Else '後ろに追加する Dim Length As Integer = Me.CmdBuf.Length ReDim Preserve Me.CmdBuf(Length) '最後尾指定 intIdx = Length End If '1文字コピー Me.CmdBuf(intIdx) = Buf(i) End If End Select Next Catch ex As Exception 'エラー処理 End Try End Sub End Class
このクラスを使用した例を以下のソースに示します。
SerialPortコントロールの使い方その4(通信用クラスの使用例)
Public Class frmSerialRcvEvent 'シリアル通信クラス Private WithEvents mclsSerialRcv As ClsSerialRcvEvent 'Invokeメソッドで使用するデリゲート宣言 Delegate Sub DisplayTextDelegate(ByVal strDisp As String) '''''' フォームクローズイベント ''' Private Sub frmSerialRcv_FormClosed(sender As Object, e As FormClosedEventArgs) Handles Me.FormClosed 'シリアル通信のクローズ mclsSerialRcv.Close() End Sub '''''' フォームロードイベント ''' Private Sub frmSerialRcv_Load(sender As Object, e As EventArgs) Handles Me.Load 'シリアル通信クラスの生成 mclsSerialRcv = New ClsSerialRcvEvent(Me.SerialPort1) 'シリアル通信のオープン If mclsSerialRcv.Open() = False Then 'オープンエラー(必要があれば処理する) End If End Sub '''''' リッチテキストBOXに文字列表示関数 ''' ''' <param name="strDisp">表示文字列</param> Private Sub DisplayText(ByVal strDisp As String) 'リッチテキストBOXに文字列を追加 Me.RichTextBox1.Text &= strDisp & vbCrLf End Sub '''''' データ受信のイベント処理(この処理は別スレッドでコールされる!!) ''' Private Sub mclsSerialRcv_ReceivedData(sender As Object, e As SerialEventArgs) Handles mclsSerialRcv.ReceivedData Try '以下の処理はスレッドが異なると怒られます。 'Me.RichTextBox1.Text &= e.ReceivedData & vbCrLf 'データ表示:デリゲート生成 Dim dlg As New DisplayTextDelegate(AddressOf DisplayText) 'デリゲート関数をコールする Me.Invoke(dlg, New Object() {e.ReceivedData}) Catch ex As Exception MsgBox(ex.Message) End Try End Sub End Class
このフォームは、シリアルコントロール及び、受信データ表示用のリッチテキストボックスを画面に張り付けてあります。 シリアル通信クラスを WithEvents で宣言し、データ受信イベントの関数宣言が出来る様にします。 その関数 mclsSerialRcv_ReceivedData 中で、受信データの処理を行います。
このイベント処理は注意が必要で、自分のフォーム内のコントロールには直接アクセスが出来ません。
この mclsSerialRcv_ReceivedData は元をたどれば、シリアルコントロールのイベント DataReceived の中から呼ばれるので 別スレッドになるからです。
そこで「SerialPortコントロールの使用方法:[SerialPort,Invoke]」と同様にデリゲート宣言を行って Invoke 関数でそれをコールする必要があります。
このプログラムを実行すると、以下の様に、「RS232Cテストツール」から連続で2個のデータを送信しても、 それぞれのデータが表示されます。
- <STX>DATA=2002<ETX><STX>DATA=3333<ETX>
USB ⇒ RS232C 変換ケーブル
シリアル通信に関連してですが、 最近のPC、特にノートPCの場合ではRS232Cのコネクタがついていることは殆んどありません。そのため、以下の様なUSBを使ったRS232Cの変換ケーブルが使えそうです。
⇒iBUFFALO USBシリアルケーブル(USBtypeA to D-sub9ピン)1.0m ブラックスケルトン BSUSRC0610BS
⇒サンワサプライ USB-RS232Cコンバータ USB-CVRS9
⇒Plugable USB‐9ピンRS232シリアルアダプター (Prolific社製 PL2303HX Rev Dチップセット採用)
関連する記事
⇒クラスにイベントを実装する方法について(Event, RaiseEvent, WithEvents)⇒SerialPortコントロールの使用方法:[SerialPort,Invoke]
⇒SerialPortコントロールの使い方その2
⇒SerialPortコントロールの使い方その3(外部装置からの垂れ流しデータ受信)
⇒SerialPortコントロールの使い方その5(データ受信時及び、信号変化時にイベントを発生させる)
⇒SerialPortコントロールの使い方その6(ハンドシェィクによるデータ送受信)
コメント