すでにFAQかも知れませんが、マルチスレッド、デリゲート(Delegate)、Invoke といったあたりが
よく理解できず、質問させていただきます。
これまでVB6.0で作成していたアプリをVB2008に移植しようとしています。VB2008のDataReceived
イベントが別スレッドで発生するため、スレッドセーフになるような工夫が必要なことは判りました。
MSDNの下記サンプルを見つけて睨んでいるのですが、サンプルをどう直したらよいかが判りません。
http://msdn.microsoft.com/ja-jp/library/cc720852.aspx
私の使用環境では、外部からの受信データは「連続する2Bytesのバイナリデータ」です。デリミタは
ありません。
2Byte受信したところでイベントが発生するように ReceivedBytesThresholdはデザイン画面のプロパティ
で"2"に設定しました。その他の通信条件も同様にプロパティで設定しました。
2Bytesのデータを受信したところで、その値をメインフォーム側に渡し、内容をチェックし、最終的な
データを組み立て、ラベルに表示させたいと思っています。
また、DataReceivedイベント内で受信エラーが発生した場合にはエラーメッセージが引き渡される
ことが必要です(MessageBoxで表示)。
上記のサンプルでは、外部からのデータが「デリミタを持つ文字列」が想定されています。そのため、
受信文字列とエラーメッセージを同じ"strDataReceived"という文字列で受け渡しができています。
バイトデータと文字列の両方をうまく引き継ぐにはどのような記述にすればよろしいのでしょうか。
何かヒントでもいただければありがたいです。よろしくお願いします。
私はフォームに貼るコントロールは使わないのですが・・・
参考に!
DataReceivedの中身ですが、サンプルはアスキー通信ですので
バイナリ通信の場合は・・・
Dim dev As SerialPort
Dim buff() As Byte
Dim iRet As Integer
Dim count As Integer
dev = CType(sender, SerialPort)
If e.EventType = SerialData.Chars Then
count = dev.BytesToRead
If count >= 2 Then
ReDim buff(1)
iRet = dev.Read(buff, 0, 2)
If iRet = 2 Then
RaiseEvent ReceiveData(buff)
End If
End If
End If
エラー処理は、ご自身でお考え下さい。
尚、上記コードは、別クラスにシリアル通信部分を分離して
います。
RaiseEvent ReceiveData の部分は・・・
Public Event ReceiveData(ByVal dt() As Byte)
と宣言してあります。
アプリ側では・・・
Private WithEvents devSerial As Serial2
と宣言して
Private Sub devSerial_ReceiveData(ByVal dt() As Byte) Handles devSerial.ReceiveData
Debug.WriteLine(dt(0).ToString("XX") + "," + dt(1).ToString("XX"))
End Sub
で、受信部分の処理をしています。
※ 実験動作していませんので、動作するかは解りません。
以上。あくまで参考に!
オショウさん、早速のお返事をありがとうございました。
せっかくプログラム例まで作成していただいたのですが、私には難しすぎます。
何をやっているのか理解できません。
前記のMSDNの例とはあまりに違う構造なので、困惑しています。
スミマセン、理解力がなくて・・・。
困りました。
FrameWorkでセリアル通信をする場合マルチスレッド、デリゲートの最低の
知識は必要になります。
http://www.geocities.jp/hatanero/rs232c5.html
ここでデータを受信してHEX表示するサンプルを載せて有ります。
ReceivedBytesThresholdの設定は設定して有りません。
バイナリ通信の場合はReceivedBytesThresholdの必要性は低いと思います。
入って来たデータが既定のバイト数になったら処理すれば良いのです。
エラーに関しては
Try
SerialPort1.Read(ByteRead, 0, SerialPort1.BytesToRead)
Catch ex As Exception
MessageBox.Show( ex.Message)
End Try
で良いのですが、あくまでも読み取りエラーですので注意してください。
我龍院さん、おはようございます。
お返事をありがとうございました。
我龍院さんのページにはRS-232C関連の解説が多く掲載されており勉強になります。
教えていただいたページを早速見てみました。まだよく理解できませんが、
足がかりを得た感じがします。ありがとうございました。
ところで、本題からはややずれるのですが、我龍院さんの書き込みで、
> で良いのですが、あくまでも読み取りエラーですので注意してください。
との記述ですが、これは「Read」メソッド実行時にエラーがあった場合のみという意味
なのでしょうか。私はVB6.0におけるMSCommで検出される「受信バッファオーバーフロー」とか、
「パリティエラー」等といった通信エラーが帰ってくるものと思っていたのですが。
それらのエラーはどのように検地するのでしょうか。
重ねての質問で恐縮ですが、宜しくご教示願います。
だから何故調べないのでしょうか?
SerialPortクラスのメンバ説明見れば・・・
フォームに貼ったSerialPort1のErrorReceivedを見れば
ByVal e As System.IO.Ports.SerialErrorReceivedEventArgs
この、eでシリアルのエラー見れますが・・・
※ 1分もかからないですヨ!
そこまで到達するの・・・
以上。
すみません。「検地」ではなく、「検知」でした。
.
オショウさん、コメント恐れ入ります。
そうですか。データを受信するDataReceivedイベントとは別に、ErrorReceived
イベントハンドラを記述して、そこで処理を行うということですね。
次第にわかってきました。
このスレはまだ「解決」にはしませんが、しばらく格闘してみます。
時間を下さい。また報告します。
.
オショウさん、我龍院さん。
その後我龍院さんのページを参考にして作業を行い、それなりの動作をするようになったのでご報告します。
まず、仕様と問題点を整理しておきます。
・VB2008でSerialPortコンポーネントを使ったシリアル通信の受信部のプログラミング。
・相手の機器からは「連続する2Bytesのバイナリデータ」がくる。
・2Bytesのカタマリがさらに連続してくる場合があるので、なるべく高速に処理すべく、
2Bytes受け取った時点でDataReceivedイベントを発生させたい(ReceivedBytesThreshold=2)。
・最終的には、受信したバイナリデータをメインスレッドに引き継ぎ、内容をチェックし、
データを組み立て、ラベルに表示する、というプログラムを作成したい。
・当面、DataReceivedが発生するスレッドから2Bytesデータをメインスレッドに引き継ぎ、
ラベル(下記の lblAZ, lblEL)に表示する部分を作成する。
・とりあえず、エラー処理は行っていない。
以下、作成したプログラムリスト。
Delegate Sub ReceiveDataDelegate(ByVal iData1 As Integer, ByVal iData2 As Integer)
Private Sub DataAnalyze(ByVal iData1 As Integer, ByVal iData2 As Integer)
lblAZ.Text = Convert.ToString(iData1)
lblEL.Text = Convert.ToString(iData2)
End Sub
Private Sub SerialPort1_DataReceived(ByVal sender As System.Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) _
Handles SerialPort1.DataReceived
Dim dlgData As ReceiveDataDelegate = New ReceiveDataDelegate(AddressOf DataAnalyze)
Dim ReceiveData1 As Integer = 0
Dim ReceiveData2 As Integer = 0
ReceiveData1 = SerialPort1.ReadByte
ReceiveData2 = SerialPort1.ReadByte
Me.Invoke(dlgData, ReceiveData1, ReceiveData2)
End Sub
改善点が多々あると思いますので、ご指摘下さい。よろしくお願いします。
.
そのコードで動いていれば良いとも言えるのですが、突っ込んで欲しいと言うことなので。
ただし正常に動いているように見えてもたまたまうまくいっている場合も有ります。(^^;
ReceivedBytesThresholdは設定値以上のバイト数の受信が有る場合に
DataReceivedイベントが起きます。
(ReceivedBytesThreshold=2と設定しておくと、例えば4バイトのデータを
一括で送信した場合、DataReceivedイベントは2バイト受信した時に起きる可能性も
ありますし、3バイト受信した場合、4バイト受信した場合に起きる可能性も有ります。
従ってデータが連続して来る場合そのコードでは不具合が出る可能性があります。
例えば3バイト、4バイトでDataReceivedイベントが起きた場合は、
イベントの中で2バイトしかデータを取っていないので、次の受信が来るまで、
受信しているデータを取れない事になります。
最悪の場合データが溜まってオーバーフローする可能性や、最後までデータを取らない可能性
が有ります。
基本的にはReceivedBytesThresholdの値に関係なく DataReceivedイベントでは
受信したデータを全て読み込んで下さい。
>2Bytesのカタマリがさらに連続してくる場合があるので、なるべく高速に処理すべく、
>2Bytes受け取った時点でDataReceivedイベントを発生させたい(ReceivedBytesThreshold=2)。
これは間違いです、データの長さが一定でしかも時間を置いて送信して来る場合を除いて、
ReceivedBytesThreshold=1(デフォルト)にしておいて、来たデータを全て読み込む方法が
一番高速となります。
受信側でたとえReceivedBytesThreshold=1に設定しておいて、送信側で10バイトのデータを一括で
送信した場合、1バイト毎にDataReceivedイベントが起きるわけでは無いのです。
我龍院さん、コメントをありがとうございました。
確かに、我龍院さんのコメントをいただく前(昨晩)にいろいろとやっていたところ、ご指摘のように
2バイトデータが間隔をあけてくる場合には正しく動作をするのですが、連続して4バイト、
6バイトとやってきた場合には挙動がおかしくなります。
受信バッファに対する処理が不十分なのかと思っておりましたが、いただいた説明で判りました。
もう少しロジックを検討してみます。
またご報告します。ありがとうございました。
.
我龍院さんへ。
その後ご指摘いただいた点を見直して、受信部分を修正しました。
と言っても、殆どが龍院さんのページを引き写したようなものですが・・・。
連続してデータを受信した場合も予想通りの動きをします。
アドバイスをありがとうございました。
Delegate Sub ReceiveDataDelegate(ByVal iData As Integer)
Private Sub DataAnalyze(ByVal iData As Integer)
'// 受信データの内容チェック
'// 受信データからデータを組み立てる処理
'// ラベルに表示する処理
End Sub
Private Sub SerialPort1_DataReceived(ByVal sender As System.Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) _
Handles SerialPort1.DataReceived
Dim dlgData As ReceiveDataDelegate = New ReceiveDataDelegate(AddressOf DataAnalyze)
Dim ReadBuff(SerialPort1.BytesToRead - 1) As Byte
SerialPort1.Read(ReadBuff, 0, SerialPort1.BytesToRead)
For i As Integer = 0 To ReadBuff.Length - 1
Me.Invoke(dlgData, CInt(ReadBuff(i)))
Next
End Sub
ツイート | ![]() |