投稿者 魔界の仮面弁士  (社会人) 投稿日時 2017/7/7 19:59:37
今回読み取るデータは、「65535 (2バイト)」+「1023 (2バイト)」の
計 4 バイトであるはずです。しかしながら提示頂いたコードでは、
> SerialPort1.Read(rdat, 0, 4)
> SerialPort1.Read(data, 0, size)
という 2 つの Read 処理が記述されています。

これだと、4 バイトではなく、4 + size バイトということで、
少なくとも 6 バイト分を読み取ろうとしていることになりませんか。


たとえば受信バッファ上に 5 バイトのデータ {01,23,45,67,89} があった場合、
たとえば下記のように Read すると、コメント部のような動作になるものだと
思っていたのですが……違いましたっけ?

If SerialPort1.ReadBufferSize >= 5 Then
 Dim bin(7) As Byte           'bin は {00, 00, 00, 00, 00, 00, 00, 00 } になる 
 '受信バッファから 2 バイト分を取り出し、bin(5)~bin(6) の位置に転写する 
 SerialPort1.Read(bin, 5, 2)  'bin は {00, 00, 00, 00, 00, 01, 23, 00 } になる 
 '受信バッファから 1 バイト分を取り出し、bin(3)~bin(3) の位置に転写する 
 SerialPort1.Read(bin, 3, 1)  'bin は {00, 00, 00, 45, 00, 01, 23, 00 } になる 
 'この時点で、まだ受信バッファには {67, 78} の 2 バイトが残留している 
End If




> この方法に無理があるのでしょうか。
> Dim rdat(3) As Byte
> SerialPort1.Read(rdat, 0, 4)

最初に Read メソッドで 4 バイトのデータを取り出そうとしているようですが、
読み出す前には常に、受信バッファにそれだけのデータが溜まっていることを
確認しなければなりません。(今回の場合は BytesToRead ≧ 4 かどうかということ)

受信したデータ以上の長さを読み取るとすると、また先の例外に陥りそうです。。
>>> **配列のオフセット及び長さが範囲を超えているか、カウンターがソースコレクションの
>>> インデックスから最後までの要素の数より大きい値です。**



> 10bit以上の値は送信しないので、65535(FFFF)を受信したら、
> そこから2バイト取得する形にしてみました。
FF が 3 回続くこともありそうですが、大丈夫ですか?

周知とは思いつつ念のために確認しますが、たとえば 1023(&H3FF) が
機器側から送信される場合、"FF,03" と "03,FF" のいずれの順序で
電送される仕様なのか把握されていますでしょうか。

もしも前者なら、たとえば 1023 (FF,03) と 801 (21,03) の 2 つが送信される場合、
"FF,FF,FF,03,FF,FF,23,01" が流れてくることになりますし、
もしも後者なら、たとえば 1023 (03,FF) と 801 (03,21) の 2 つが送信される場合、
"FF,FF,03,FF,FF,FF,03,21" が流れてくることになります。

ということで、どちらのパターンであれ、…,FF,FF,FF,xx,FF…という並びが
含まれる可能性がありえるのではないでしょうか。仮に最初のデータを受信し損ねた場合、
FFFF の読み取り位置がズレて「-253 (&HFF03)」と誤読してしまうかも知れません。

受信漏れが起きたときに、データが欠損する(読み落とす)というのならばともかく、
データが破損する(化ける)というのは都合が悪い気がします。

http://qiita.com/ozwk/items/18d44f38e26ee25ec6e8


> Dim rdatbin As Byte() = {rdat(3), rdat(2), rdat(1), rdat(0)}
ちなみに
 System.Net.IPAddress.HostToNetworkOrder
 System.Net.IPAddress.NetworkToHostOrder
のメソッドを使うと、Short 値や Integer 値のバイナリ順序を逆転できます。

たとえば、
 Dim shortValue As Short = 1023  '&H03FF
 Dim intValue As Integer = 1023  '&H000003FF
という値を上記のメソッドに渡すと、
shortValue は -253 (&HFF03)、intValue は -16580608 (&HFF030000) になります。


> Dim rdata As Short = CShort(BitConverter.ToInt32(rdatbin, 0))   '符号付き 2 バイト整数に変換 
rdatbin のバイナリが FF,FF,xx,xx の 4 バイトであると仮定すると
もしもそれが
FF,FF,00,00 なら、ToInt32 が返す値は 65535 になりますし、
FF,FF,03,FF なら、ToInt32 が返す値は -16515073 になりますし、
FF,FF,FF,03 なら、ToInt32 が返す値は 67108863 になりますし、
FF,FF,FF,FF なら、ToInt32 が返す値は -1 になるわけです。

CShort 型の範囲は -32768~32767 なのですから、大抵の場合オーバーフローする値ですよね。


対処法はいろいろありますが、FF,FF の 2 バイトは、
 BitConverter.ToUInt16 すれば 65535 ( = UShort.MaxValue )
 BitConverter.ToInt16 すれば -1
になる値なので、先導 2 バイトと後続 2 バイトの 2 回に分けて
読み取る方が楽かもしれません。

あるいは BitConverter にかけるのではなく、
rdatbin(n) と rdatbin(n + 1) の両方が &HFF かどうかを
確認すると言う手もあります。

4 バイト丸ごと変換するのなら、BitConverter.ToUInt32 した上で、
ビットマスクもしくはビットシフト演算で取り出すと言う手もあります。