Serial Over IPによるSD330コントローラーのLAN化 ― 2017年05月05日 19:08
はじめにお読みください。→当サイトのリンクと免責事項
現在SDD330コントローラはUSBにてパソコンにつないでいます。
SD330を遠隔で操作しようとするとSD330をコントロールしているPCにリモートアクセスして。そのPCのデスクトップより制御するようになります。
コントロールPCをリモートデスクトップから操作します。
これでも良いのですがコントロールPCを単なるシリアル通信のトンネルにして遠隔のPCから直接操作できるような仕組みを考えました。
コントロールPCにUSBインターフェースでSD330コントローラは繋がっています。
そこでコントロールPCでTCP/IPに変換し送信、遠隔のPCでTCP/IPからシリアル通信に変換しあたかもSD330コントローラが直接つながっているようにします。
コントロールPCのUSBインターフェースはUSB仮想COMポートとしていますが、遠隔のPCではLAN仮想COMポートになります。いわゆる
しかしこの方法には欠点があります。遅延の問題です。SD330コントローラからのデータは一旦コントロールPCのバッファに入ります。次にデータは送られて遠隔PCのバッファに入ります。SD330コントローラからは921.6kbpsで送出されます。PC間はLANですのでそれなりに早いです。遠隔PCの仮想COMポートはSD330コントローラに合わせ921.6kbpsです。
Windows の.NETのプログラムの作り方にも拠りますがどれくらいのレスポンスになるのでしょうか。
この遅延を嫌うならSD330コントローラの出口をUSBでなくシリアル出力にしてそこにメディアサーバー(Serial-TCP/IP)を付けます。そのままLANになります。
921.6kbpsをサポートしている製品で手ごろな価格なのがLantronix製デバイスサーバです。同社のXport-XEは3.3Vのシリアル通信ですので300~920Kbpsをサポートしています。多くのRS232Cタイプはラインドライバーの関係でほとんどが最高115k bpsです。これらの機器はMCUで処理しますのでWindows より格段に速くなります。また1:多が可能なので1台のPCから複数のシリアル機器を制御できます。
とりあえずVB.NETで簡単に作ってみます。
今回のソフトは、コントロールPCはサーバーとして先に起動し遠隔PC(クライアント)からの接続要求を待ちます。接続が確立したらTCP/IPの受信ループに入ります。TCP/IPが受信するとそのままシリアルにデータを送信します。
シリアルの方は受信イベントが発生の都度SD330コントローラから受信しそのままTCP/IPに送信します。
遠隔PCの方はクライアントなのでプログラム起動と同時に接続要求を出します。接続が確立したらTCP/IPの受信ループに入ります。TCP/IPが受信するとそのままシリアルにデータを送信します。
シリアルの方は受信イベントが発生の都度SD330コントローラから受信しそのままTCP/IPに送信します。
つまり全く同じ構成です。これは1:1の通信を想定しているからです。
コントロールPCは接続されているSerial機器と送受信しますが、遠隔PCの方はこのソフトでリモートPCからのデータをCOM4につなぎます。COM4は専有されますので他のソフトから読み書きできません。そこで2つのCOMポートが繋がっているようなドライバーを用意します。今回はcom0comというドライバーを使いました。
このcom0comドライバは 2つの仮想のCOMポートをクロスケーブルで接続したように見せかけ、
片方の仮想COMポートからもう一方の仮想COMポートへ通信する事ができます。
片方の仮想COMポートからもう一方の仮想COMポートへ通信する事ができます。
COM4に送られたデータはCOM3にでます。COM3からのデータはCOM4に出てきます。そこでアナライザなどのコントロールソフトはCOM3を使用すればCOM4を経由してTCP/IP経由でリモートPCに行きSerial機器を制御できます。com0comについてはこちらを参考にしてください。
実際の動作での結果ですがRigExpert社のAA-54で試験したところコントロールPCからデータを受信し描画するまで100データで11秒でした。本プログラムを介し遠隔PCから同条件で行ったところ13秒かかりました。
AA-54は38.4kbpsで元々遅い感じですので全く気にならない遅延でした。
SD330コントローラの場合HF全域を測定しその8000データを受信し描画するまでの時間を計ったところどちらも6秒で遅延はありませんでした。Serialは921.6kBPSです。
バッファ制御など考えずに簡単に作った割には結構実用になると思います。
サーバー側のルーチンです。デザイン画面はForm1にLogboxという名前のListBoxが一つあるだけです。
またSerialPortコントロールを張り付けています。(SerialPort1)
Port番号とCOM番号はコード中に組み込んでいます。
Formは必要ないのですがデバックを兼ねています。
このルーチンはSD330用で1:1通信で尚且つ並列動作は無い、いわゆる半2重通信が前提です。
SerialもTCPもBinaryでRead、Writeしています。データの最大長は8kByteです。
サーバーはクライアントからの接続待ちでブロッキングしていて接続が完了すれば受信待ちになります。クライアントから切断要求がくれば接続待ちに戻ります。終了はありません。フォーム画面の✖で閉じない限り無限ループです。内部にてエラーが発生するとクライアントにシャットダウンを送信してFormを閉じて終了します。
言語はVB.NET (MS Visual Studio Express 2013 for Windows DESKTOP)です。
-------------------------------------------------------------------------------------------------------------------
Option Strict On
Imports System.IO
Imports System.Net
Imports System.Net.Sockets
Imports System.Text
Imports System.Threading
'-------------------------------------------------------------------------------------------------------------------
Class Form1
Inherits System.Windows.Forms.Form
Dim myStream As NetworkStream ' myStream は 受信のほか送信でも使うためグローバル
Dim myS_Socket As Socket ' シャットダウンを送信するためグローバル
'-------------------------------------------------------------------------------------------------------------------
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim myThreads As Thread
myThreads = New Thread(AddressOf serverSTART) '接続待ち受信スレッド
myThreads.IsBackground = True ' バックグランドスレッド MAINスレッド終了と共に終了
myThreads.Start() ' スレッド スタート
' シリアルポートのオープン
SerialPort1.PortName = "COM3"
' シリアルポートの通信速度指定
SerialPort1.BaudRate = 921600
' シリアルポートのパリティ指定
SerialPort1.Parity = IO.Ports.Parity.None
' シリアルポートのビット数指定
SerialPort1.DataBits = 8
' シリアルポートのストップビット指定
SerialPort1.StopBits = IO.Ports.StopBits.One
SerialPort1.ReadBufferSize = 10000
' シリアルポートのオープン
SerialPort1.Open()
SerialPort1.ReadTimeout = 3000
LOGOUTPUT("COM3は開かれました")
End Sub
'-------------------------------------------------------------------------------------------------------------------
Private Sub Form1_FormClosed(sender As Object, e As FormClosedEventArgs) Handles Me.FormClosed
SerialPort1.Close()
LOGOUTPUT("COM3は閉じられました")
End Sub
'-------------------------------------------------------------------------------------------------------------------
Private Sub serverSTART() '接続&受信待ちスレッド
Dim mySocket As Socket
' Dim myS_Socket As Socket ' シャットダウンを送信するためグローバル
Dim myAddress As IPAddress
Dim myPort As Integer
Dim myEndPoint As EndPoint
' Dim myStream As NetworkStream ' myStream は 受信のほか送信でも使うためグローバル
Dim resBytes As Byte() = New Byte(10000) {}
Dim resSize As Integer = 0
Dim i_send As Integer
Try
mySocket = New Socket(AddressFamily.InterNetwork, _
SocketType.Stream, ProtocolType.Tcp)
LOGOUTPUT("ソケット作成")
myPort = 45678
myAddress = IPAddress.Any
myEndPoint = New IPEndPoint(myAddress, myPort)
mySocket.Bind(myEndPoint)
LOGOUTPUT("バインド")
mySocket.Listen(10)
LOGOUTPUT("リッスン開始")
LOGOUTPUT("")
Do '外側のDOループは接続待ちループ
myS_Socket = mySocket.Accept() 'ブロッキング モードで Accept 接続の試行 待ち
LOGOUTPUT("クライアント接続")
myStream = New NetworkStream(myS_Socket)
Try
Do '内側のDOループは受信待ちループ
'受信
resSize = myStream.Read(resBytes, 0, resBytes.Length) 'ブロッキング モードで Read 待ち
If resSize = 0 Then 'クライアントが切断
LOGOUTPUT("クライアントからシャットダウン要求")
Exit Do
End If
'************************ COM OUTPT **********************
' TCPからCOMにデータを送信
i_send = 0
Do Until i_send = resSize
SerialPort1.Write(resBytes, i_send, 1)
i_send = i_send + 1
Loop
If resSize < 11 Then '10BYTE以下なら電文をLOGに、それ以上はLENGSを
Dim i_c As Integer = 0
Do Until i_c = resSize
LOGOUTPUT("COM------->送信>> " & resBytes(i_c) & "=" & Chr(resBytes(i_c)))
i_c = i_c + 1
Loop
Else
LOGOUTPUT("COM------->送信LNG>> " & resSize.ToString)
End If
'******************************************************************
If resSize < 11 Then '10BYTE以下なら電文をLOGに、それ以上はLENGSを
Dim i_c As Integer = 0
Do Until i_c = resSize
LOGOUTPUT("受信>> " & resBytes(i_c) & "=" & Chr(resBytes(i_c)))
i_c = i_c + 1
Loop
Else
LOGOUTPUT("受信LNG>> " & resSize.ToString)
End If
Loop
'終了処理
myS_Socket.Shutdown(SocketShutdown.Send)
LOGOUTPUT("クライアントへシャットダウン送信")
myS_Socket.Close()
LOGOUTPUT("Socket_Close 受信終了 次の接続を待つ")
Catch ex As Exception
LOGOUTPUT(ErrorCodeofSocket(ex).ToString & ex.Message)
myS_Socket.Shutdown(SocketShutdown.Send)
LOGOUTPUT("クライアントへシャットダウン送信")
If Not IsNothing(myS_Socket) Then
myS_Socket.Close()
End If
LOGOUTPUT("Socket_Error 受信停止 次の接続を待つ")
End Try
Loop
Catch ex As Exception
LOGOUTPUT("* 接続失敗再起動" & ex.Message)
myS_Socket.Shutdown(SocketShutdown.Send)
LOGOUTPUT("クライアントへシャットダウン送信")
If Not IsNothing(myS_Socket) Then
myS_Socket.Close()
LOGOUTPUT("Socket.Close")
End If
If Not IsNothing(mySocket) Then
mySocket.Close()
LOGOUTPUT("Socket.Close")
End If
Form_Close()
End Try
End Sub
'-------------------------------------------------------------------------------------------------------------------
Private Sub Form_Close()
Dim Cancel_Event_Args As New System.ComponentModel.CancelEventArgs()
Application.Exit(Cancel_Event_Args) 'アプリケーション終了
If Cancel_Event_Args.Cancel Then 'アプリケーションの終了がキャンセルされたか調べる
LOGOUTPUT("アプリケーション終了がキャンセルされました")
End If
End Sub
'-------------------------------------------------------------------------------------------------------------------
Private Function ErrorCodeofSocket(ByVal ex As Exception) As Integer 'ソケットエラーコード解析
If IsNothing(ex) Then
Return -1
ElseIf TypeOf ex Is SocketException Then
Return CType(ex, SocketException).ErrorCode
Else
Return ErrorCodeofSocket(ex.InnerException)
End If
End Function
'Windows ソケットのエラー コード、値、および意味はこちら
'-------------------------------------------------------------------------------------------------------------------
Private Delegate Sub D_Logupdate(ByVal str As String) '別スレッドからLOG書き込み
'-------------------------------------------------------------------------------------------------------------------
Private Sub LOGOUTPUT(ByVal strr As String)
If Me.InvokeRequired Then '別スレッドか判定
Me.Invoke(New D_Logupdate(AddressOf Logupdate), strr) '別スレッドならデリゲート
Else
Logupdate(strr)
End If
End Sub
'-------------------------------------------------------------------------------------------------------------------
Private Sub Logupdate(ByVal str As String) ' LOG書き出しルーチン
SyncLock logBox
logBox.Items.Add(str)
logBox.TopIndex = logBox.Items.Count - 1
logBox.Refresh()
End SyncLock
End Sub
'-------------------------------------------------------------------------------------------------------------------
Private Sub SerialPort1_DataReceived(sender As Object, e As Ports.SerialDataReceivedEventArgs) Handles SerialPort1.DataReceived
' LOGOUTPUT("EVENT_DataReceived")
System.Console.Beep(440, 300) 'COMデータ受信しTCPに送信
Dim leng As Integer = 0
Dim dat(10000) As Byte
Try
leng = SerialPort1.Read(dat, 0, dat.GetLength(0))
TCP_DATA_SEND(dat, leng) 'クライアントにデータを送信する
If leng < 11 Then '10BYTE以下なら電文をLOGに、それ以上はLENGS
Dim i_c As Integer = 0
Do Until i_c = leng
LOGOUTPUT("COM<-------受信>> " & dat(i_c) & "=" & Chr(dat(i_c)))
i_c = i_c + 1
Loop
Else
LOGOUTPUT("COM<-------受信LNG>> " & leng.ToString)
End If
Catch ex As TimeoutException
LOGOUTPUT("COM3ERR* " & ex.Message)
SerialPort1.Close()
myS_Socket.Shutdown(SocketShutdown.Send)
LOGOUTPUT("クライアントへシャットダウン送信")
Form_Close()
End Try
'************************************************************************
End Sub
'-------------------------------------------------------------------------------------------------------------------
Private Sub TCP_DATA_SEND(buffer() As Byte, size As Integer) 'クライアントにデータを送信する
Try
myStream.Write(buffer, 0, size)
If size < 11 Then '10BYTE以下なら電文をLOGに、それ以上はLENGSを
Dim i_c As Integer = 0
Do Until i_c = size
LOGOUTPUT("送信>> " & buffer(i_c) & "=" & Chr(buffer(i_c)))
i_c = i_c + 1
Loop
Else
LOGOUTPUT("送信LNG>> " & size.ToString)
End If
Catch ex As Exception
LOGOUTPUT(ErrorCodeofSocket(ex).ToString & ex.Message)
LOGOUTPUT("TCP送信エラー ")
myS_Socket.Shutdown(SocketShutdown.Send)
LOGOUTPUT("クライアントへシャットダウン送信")
Form_Close() '
End Try
End Sub
End Class
Imports System.IO
Imports System.Net
Imports System.Net.Sockets
Imports System.Text
Imports System.Threading
'-------------------------------------------------------------------------------------------------------------------
Class Form1
Inherits System.Windows.Forms.Form
Dim myStream As NetworkStream ' myStream は 受信のほか送信でも使うためグローバル
Dim myS_Socket As Socket ' シャットダウンを送信するためグローバル
'-------------------------------------------------------------------------------------------------------------------
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim myThreads As Thread
myThreads = New Thread(AddressOf serverSTART) '接続待ち受信スレッド
myThreads.IsBackground = True ' バックグランドスレッド MAINスレッド終了と共に終了
myThreads.Start() ' スレッド スタート
' シリアルポートのオープン
SerialPort1.PortName = "COM3"
' シリアルポートの通信速度指定
SerialPort1.BaudRate = 921600
' シリアルポートのパリティ指定
SerialPort1.Parity = IO.Ports.Parity.None
' シリアルポートのビット数指定
SerialPort1.DataBits = 8
' シリアルポートのストップビット指定
SerialPort1.StopBits = IO.Ports.StopBits.One
SerialPort1.ReadBufferSize = 10000
' シリアルポートのオープン
SerialPort1.Open()
SerialPort1.ReadTimeout = 3000
LOGOUTPUT("COM3は開かれました")
End Sub
'-------------------------------------------------------------------------------------------------------------------
Private Sub Form1_FormClosed(sender As Object, e As FormClosedEventArgs) Handles Me.FormClosed
SerialPort1.Close()
LOGOUTPUT("COM3は閉じられました")
End Sub
'-------------------------------------------------------------------------------------------------------------------
Private Sub serverSTART() '接続&受信待ちスレッド
Dim mySocket As Socket
' Dim myS_Socket As Socket ' シャットダウンを送信するためグローバル
Dim myAddress As IPAddress
Dim myPort As Integer
Dim myEndPoint As EndPoint
' Dim myStream As NetworkStream ' myStream は 受信のほか送信でも使うためグローバル
Dim resBytes As Byte() = New Byte(10000) {}
Dim resSize As Integer = 0
Dim i_send As Integer
Try
mySocket = New Socket(AddressFamily.InterNetwork, _
SocketType.Stream, ProtocolType.Tcp)
LOGOUTPUT("ソケット作成")
myPort = 45678
myAddress = IPAddress.Any
myEndPoint = New IPEndPoint(myAddress, myPort)
mySocket.Bind(myEndPoint)
LOGOUTPUT("バインド")
mySocket.Listen(10)
LOGOUTPUT("リッスン開始")
LOGOUTPUT("")
Do '外側のDOループは接続待ちループ
myS_Socket = mySocket.Accept() 'ブロッキング モードで Accept 接続の試行 待ち
LOGOUTPUT("クライアント接続")
myStream = New NetworkStream(myS_Socket)
Try
Do '内側のDOループは受信待ちループ
'受信
resSize = myStream.Read(resBytes, 0, resBytes.Length) 'ブロッキング モードで Read 待ち
If resSize = 0 Then 'クライアントが切断
LOGOUTPUT("クライアントからシャットダウン要求")
Exit Do
End If
'************************ COM OUTPT **********************
' TCPからCOMにデータを送信
i_send = 0
Do Until i_send = resSize
SerialPort1.Write(resBytes, i_send, 1)
i_send = i_send + 1
Loop
If resSize < 11 Then '10BYTE以下なら電文をLOGに、それ以上はLENGSを
Dim i_c As Integer = 0
Do Until i_c = resSize
LOGOUTPUT("COM------->送信>> " & resBytes(i_c) & "=" & Chr(resBytes(i_c)))
i_c = i_c + 1
Loop
Else
LOGOUTPUT("COM------->送信LNG>> " & resSize.ToString)
End If
'******************************************************************
If resSize < 11 Then '10BYTE以下なら電文をLOGに、それ以上はLENGSを
Dim i_c As Integer = 0
Do Until i_c = resSize
LOGOUTPUT("受信>> " & resBytes(i_c) & "=" & Chr(resBytes(i_c)))
i_c = i_c + 1
Loop
Else
LOGOUTPUT("受信LNG>> " & resSize.ToString)
End If
Loop
'終了処理
myS_Socket.Shutdown(SocketShutdown.Send)
LOGOUTPUT("クライアントへシャットダウン送信")
myS_Socket.Close()
LOGOUTPUT("Socket_Close 受信終了 次の接続を待つ")
Catch ex As Exception
LOGOUTPUT(ErrorCodeofSocket(ex).ToString & ex.Message)
myS_Socket.Shutdown(SocketShutdown.Send)
LOGOUTPUT("クライアントへシャットダウン送信")
If Not IsNothing(myS_Socket) Then
myS_Socket.Close()
End If
LOGOUTPUT("Socket_Error 受信停止 次の接続を待つ")
End Try
Loop
Catch ex As Exception
LOGOUTPUT("* 接続失敗再起動" & ex.Message)
myS_Socket.Shutdown(SocketShutdown.Send)
LOGOUTPUT("クライアントへシャットダウン送信")
If Not IsNothing(myS_Socket) Then
myS_Socket.Close()
LOGOUTPUT("Socket.Close")
End If
If Not IsNothing(mySocket) Then
mySocket.Close()
LOGOUTPUT("Socket.Close")
End If
Form_Close()
End Try
End Sub
'-------------------------------------------------------------------------------------------------------------------
Private Sub Form_Close()
Dim Cancel_Event_Args As New System.ComponentModel.CancelEventArgs()
Application.Exit(Cancel_Event_Args) 'アプリケーション終了
If Cancel_Event_Args.Cancel Then 'アプリケーションの終了がキャンセルされたか調べる
LOGOUTPUT("アプリケーション終了がキャンセルされました")
End If
End Sub
'-------------------------------------------------------------------------------------------------------------------
Private Function ErrorCodeofSocket(ByVal ex As Exception) As Integer 'ソケットエラーコード解析
If IsNothing(ex) Then
Return -1
ElseIf TypeOf ex Is SocketException Then
Return CType(ex, SocketException).ErrorCode
Else
Return ErrorCodeofSocket(ex.InnerException)
End If
End Function
'Windows ソケットのエラー コード、値、および意味はこちら
'-------------------------------------------------------------------------------------------------------------------
Private Delegate Sub D_Logupdate(ByVal str As String) '別スレッドからLOG書き込み
'-------------------------------------------------------------------------------------------------------------------
Private Sub LOGOUTPUT(ByVal strr As String)
If Me.InvokeRequired Then '別スレッドか判定
Me.Invoke(New D_Logupdate(AddressOf Logupdate), strr) '別スレッドならデリゲート
Else
Logupdate(strr)
End If
End Sub
'-------------------------------------------------------------------------------------------------------------------
Private Sub Logupdate(ByVal str As String) ' LOG書き出しルーチン
SyncLock logBox
logBox.Items.Add(str)
logBox.TopIndex = logBox.Items.Count - 1
logBox.Refresh()
End SyncLock
End Sub
'-------------------------------------------------------------------------------------------------------------------
Private Sub SerialPort1_DataReceived(sender As Object, e As Ports.SerialDataReceivedEventArgs) Handles SerialPort1.DataReceived
' LOGOUTPUT("EVENT_DataReceived")
System.Console.Beep(440, 300) 'COMデータ受信しTCPに送信
Dim leng As Integer = 0
Dim dat(10000) As Byte
Try
leng = SerialPort1.Read(dat, 0, dat.GetLength(0))
TCP_DATA_SEND(dat, leng) 'クライアントにデータを送信する
If leng < 11 Then '10BYTE以下なら電文をLOGに、それ以上はLENGS
Dim i_c As Integer = 0
Do Until i_c = leng
LOGOUTPUT("COM<-------受信>> " & dat(i_c) & "=" & Chr(dat(i_c)))
i_c = i_c + 1
Loop
Else
LOGOUTPUT("COM<-------受信LNG>> " & leng.ToString)
End If
Catch ex As TimeoutException
LOGOUTPUT("COM3ERR* " & ex.Message)
SerialPort1.Close()
myS_Socket.Shutdown(SocketShutdown.Send)
LOGOUTPUT("クライアントへシャットダウン送信")
Form_Close()
End Try
'************************************************************************
End Sub
'-------------------------------------------------------------------------------------------------------------------
Private Sub TCP_DATA_SEND(buffer() As Byte, size As Integer) 'クライアントにデータを送信する
Try
myStream.Write(buffer, 0, size)
If size < 11 Then '10BYTE以下なら電文をLOGに、それ以上はLENGSを
Dim i_c As Integer = 0
Do Until i_c = size
LOGOUTPUT("送信>> " & buffer(i_c) & "=" & Chr(buffer(i_c)))
i_c = i_c + 1
Loop
Else
LOGOUTPUT("送信LNG>> " & size.ToString)
End If
Catch ex As Exception
LOGOUTPUT(ErrorCodeofSocket(ex).ToString & ex.Message)
LOGOUTPUT("TCP送信エラー ")
myS_Socket.Shutdown(SocketShutdown.Send)
LOGOUTPUT("クライアントへシャットダウン送信")
Form_Close() '
End Try
End Sub
End Class
-------------------------------------------------------------------------------------------------------------------
続いてクライアント側のルーティンです。こちらもデザイン画面はForm1にLogboxという名前のListBoxが一つあるだけです。またSerialPortコントロールを張り付けています。(SerialPort1)
IP_ADDRESSとPort番号、COM番号はコード中に組み込んでいます。
クライアントが起動するとサーバーに接続要求をします。接続後は交信します。終了はFormの✖によってサーバーに切断要求を送り、返事のシャットダウンが来るとプログラムを終了します。サーバーの電文中にシャットダウンがあればFormを閉じ終了します。
通常は再度プログラムを起動すれば接続から始まります。
-------------------------------------------------------------------------------------------------------------------
Option Strict On
Imports System.IO
Imports System.Net
Imports System.Net.Sockets
Imports System.Text
Imports System.Threading
'-------------------------------------------------------------------------------------------------------------------
Class Form1
Inherits System.Windows.Forms.Form
Dim mySocket As Socket 'セッションエンドの中でも使用のためグローバル
Dim myStream As NetworkStream 'データの送信のためグローバル
'-------------------------------------------------------------------------------------------------------------------
Private Sub Form1_FormClosed(sender As Object, e As FormClosedEventArgs) Handles Me.FormClosed
SerialPort1.Close()
TCP_CLOSE()
SeesionEND()
End Sub
'-------------------------------------------------------------------------------------------------------------------
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
' シリアルポートのオープン
SerialPort1.PortName = "COM4"
' シリアルポートの通信速度指定
SerialPort1.BaudRate = 921600
' シリアルポートのパリティ指定
SerialPort1.Parity = IO.Ports.Parity.None
' シリアルポートのビット数指定
SerialPort1.DataBits = 8
' シリアルポートのストップビット指定
SerialPort1.StopBits = IO.Ports.StopBits.One
SerialPort1.ReadBufferSize = 10000
' シリアルポートのオープン
SerialPort1.Open()
SerialPort1.ReadTimeout = 3000
LOGOUTPUT("COM4は開かれCOM3と繋がりました")
TCP_OPEN() ' サーバーと接続 受信スレッド起動
End Sub
'-------------------------------------------------------------------------------------------------------------------
Private Sub TCP_OPEN()
Dim myThread As Thread
Dim myAddress As IPAddress
Dim myPort As Integer
Dim myEndPoint As EndPoint
Try
mySocket = New Socket(AddressFamily.InterNetwork, _
SocketType.Stream, ProtocolType.Tcp)
LOGOUTPUT("ソケット作成")
myAddress = IPAddress.Parse("192.168.1.249")
myPort = 45678
myEndPoint = New IPEndPoint(myAddress, myPort)
mySocket.Connect(myEndPoint)
LOGOUTPUT("接続")
myStream = New NetworkStream(mySocket)
'別スレッド
myThread = New Thread(AddressOf ClientSTART)
myThread.IsBackground = True ' バックグランドスレッド MAINスレッドの終了と共に終了
myThread.Start() ' スレッドスタート
LOGOUTPUT("受信スレッド起動")
Catch ex As Exception
If Not IsNothing(mySocket) Then
mySocket.Close()
End If
LOGOUTPUT("* " & ex.Message)
LOGOUTPUT("* 再起動してください")
SeesionEND()
End Try
End Sub
'-------------------------------------------------------------------------------------------------------------------
Private Sub TCP_CLOSE()
mySocket.Shutdown(SocketShutdown.Send) '送信の終了
LOGOUTPUT("サーバーへシャットダウン送信")
mySocket.Close()
End Sub
'-------------------------------------------------------------------------------------------------------------------
Sub ClientSTART() '受信
Dim resBytes As Byte() = New Byte(10000) {}
Dim resSize As Integer = 0
Try
Do
resSize = myStream.Read(resBytes, 0, resBytes.Length) 'ブロッキング 'データ受信
If resSize = 0 Then 'サーバー切断
LOGOUTPUT("サーバーからシャットダウン要求")
Exit Do
End If
'***************************************** COM DATA SEND
' TCPからCOMにデータを送信
SerialPort1.Write(resBytes, 0, resSize)
'**************************************************************
If resSize < 11 Then '10BYTE以下なら電文をLOGに、それ以上はLENGSを
Dim i_c As Integer = 0
Do Until i_c = resSize
LOGOUTPUT("受信>> " & resBytes(i_c))
i_c = i_c + 1
Loop
Else
LOGOUTPUT("受信LNG>> " & resSize.ToString)
End If
Loop
LOGOUTPUT("受信スレッド終了")
SeesionEND()
Catch ex As Exception
LOGOUTPUT(ErrorCodeofSocket(ex).ToString & ex.Message)
SeesionEND()
End Try
End Sub
'-------------------------------------------------------------------------------------------------------------------
Function ErrorCodeofSocket(ByVal ex As Exception) As Integer 'ソケットエラーコード解析
If IsNothing(ex) Then
Return -1
ElseIf TypeOf ex Is SocketException Then
Return CType(ex, SocketException).ErrorCode
Else
Return ErrorCodeofSocket(ex.InnerException)
End If
End Function
'Windows ソケットのエラー コード、値、および意味はこちら
'-------------------------------------------------------------------------------------------------------------------
Sub SeesionEND() 'セッション終了
If Not IsNothing(mySocket) Then
mySocket.Close()
LOGOUTPUT("ソケットCLOSE")
End If
LOGOUTPUT("")
End Sub
'-------------------------------------------------------------------------------------------------------------------
Private Delegate Sub D_Logupdate(ByVal str As String) '別スレッドからLOG書き込み
'-------------------------------------------------------------------------------------------------------------------
Sub LOGOUTPUT(ByVal strr As String)
If Me.InvokeRequired Then '別スレッドか判定
Me.Invoke(New D_Logupdate(AddressOf Logupdate), strr) '別スレッドならデリゲート
Else
Logupdate(strr)
End If
End Sub
'-------------------------------------------------------------------------------------------------------------------
Private Sub Logupdate(ByVal str As String) ' LOG書き出しルーチン
SyncLock logBox
logBox.Items.Add(str)
logBox.TopIndex = logBox.Items.Count - 1
logBox.Refresh()
End SyncLock
End Sub
'-------------------------------------------------------------------------------------------------------------------
Private Sub SerialPort1_DataReceived(sender As Object, e As Ports.SerialDataReceivedEventArgs) Handles SerialPort1.DataReceived
System.Console.Beep(440, 300) 'COMデータ受信しTCPに送信
Dim leng As Integer = 0
Dim dat(10000) As Byte
Try
leng = SerialPort1.Read(dat, 0, dat.GetLength(0))
Catch ex As TimeoutException
LOGOUTPUT("COM4ERR* " & ex.Message)
SeesionEND()
End Try
'*********************************************************TCP DATA SEND
Try
'クライアントにデータを送信する
myStream.Write(dat, 0, leng)
If leng < 11 Then '10BYTE以下なら電文をLOGに、それ以上はLENGSを
Dim i_c As Integer = 0
Do Until i_c = leng
LOGOUTPUT("送信>> " & dat(i_c))
i_c = i_c + 1
Loop
Else
LOGOUTPUT("送信LNG>> " & leng.ToString)
End If
Catch ex As Exception
'エラー処理
LOGOUTPUT("* " & ex.Message)
SeesionEND()
End Try
End Sub
End Class
Imports System.IO
Imports System.Net
Imports System.Net.Sockets
Imports System.Text
Imports System.Threading
'-------------------------------------------------------------------------------------------------------------------
Class Form1
Inherits System.Windows.Forms.Form
Dim mySocket As Socket 'セッションエンドの中でも使用のためグローバル
Dim myStream As NetworkStream 'データの送信のためグローバル
'-------------------------------------------------------------------------------------------------------------------
Private Sub Form1_FormClosed(sender As Object, e As FormClosedEventArgs) Handles Me.FormClosed
SerialPort1.Close()
TCP_CLOSE()
SeesionEND()
End Sub
'-------------------------------------------------------------------------------------------------------------------
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
' シリアルポートのオープン
SerialPort1.PortName = "COM4"
' シリアルポートの通信速度指定
SerialPort1.BaudRate = 921600
' シリアルポートのパリティ指定
SerialPort1.Parity = IO.Ports.Parity.None
' シリアルポートのビット数指定
SerialPort1.DataBits = 8
' シリアルポートのストップビット指定
SerialPort1.StopBits = IO.Ports.StopBits.One
SerialPort1.ReadBufferSize = 10000
' シリアルポートのオープン
SerialPort1.Open()
SerialPort1.ReadTimeout = 3000
LOGOUTPUT("COM4は開かれCOM3と繋がりました")
TCP_OPEN() ' サーバーと接続 受信スレッド起動
End Sub
'-------------------------------------------------------------------------------------------------------------------
Private Sub TCP_OPEN()
Dim myThread As Thread
Dim myAddress As IPAddress
Dim myPort As Integer
Dim myEndPoint As EndPoint
Try
mySocket = New Socket(AddressFamily.InterNetwork, _
SocketType.Stream, ProtocolType.Tcp)
LOGOUTPUT("ソケット作成")
myAddress = IPAddress.Parse("192.168.1.249")
myPort = 45678
myEndPoint = New IPEndPoint(myAddress, myPort)
mySocket.Connect(myEndPoint)
LOGOUTPUT("接続")
myStream = New NetworkStream(mySocket)
'別スレッド
myThread = New Thread(AddressOf ClientSTART)
myThread.IsBackground = True ' バックグランドスレッド MAINスレッドの終了と共に終了
myThread.Start() ' スレッドスタート
LOGOUTPUT("受信スレッド起動")
Catch ex As Exception
If Not IsNothing(mySocket) Then
mySocket.Close()
End If
LOGOUTPUT("* " & ex.Message)
LOGOUTPUT("* 再起動してください")
SeesionEND()
End Try
End Sub
'-------------------------------------------------------------------------------------------------------------------
Private Sub TCP_CLOSE()
mySocket.Shutdown(SocketShutdown.Send) '送信の終了
LOGOUTPUT("サーバーへシャットダウン送信")
mySocket.Close()
End Sub
'-------------------------------------------------------------------------------------------------------------------
Sub ClientSTART() '受信
Dim resBytes As Byte() = New Byte(10000) {}
Dim resSize As Integer = 0
Try
Do
resSize = myStream.Read(resBytes, 0, resBytes.Length) 'ブロッキング 'データ受信
If resSize = 0 Then 'サーバー切断
LOGOUTPUT("サーバーからシャットダウン要求")
Exit Do
End If
'***************************************** COM DATA SEND
' TCPからCOMにデータを送信
SerialPort1.Write(resBytes, 0, resSize)
'**************************************************************
If resSize < 11 Then '10BYTE以下なら電文をLOGに、それ以上はLENGSを
Dim i_c As Integer = 0
Do Until i_c = resSize
LOGOUTPUT("受信>> " & resBytes(i_c))
i_c = i_c + 1
Loop
Else
LOGOUTPUT("受信LNG>> " & resSize.ToString)
End If
Loop
LOGOUTPUT("受信スレッド終了")
SeesionEND()
Catch ex As Exception
LOGOUTPUT(ErrorCodeofSocket(ex).ToString & ex.Message)
SeesionEND()
End Try
End Sub
'-------------------------------------------------------------------------------------------------------------------
Function ErrorCodeofSocket(ByVal ex As Exception) As Integer 'ソケットエラーコード解析
If IsNothing(ex) Then
Return -1
ElseIf TypeOf ex Is SocketException Then
Return CType(ex, SocketException).ErrorCode
Else
Return ErrorCodeofSocket(ex.InnerException)
End If
End Function
'Windows ソケットのエラー コード、値、および意味はこちら
'-------------------------------------------------------------------------------------------------------------------
Sub SeesionEND() 'セッション終了
If Not IsNothing(mySocket) Then
mySocket.Close()
LOGOUTPUT("ソケットCLOSE")
End If
LOGOUTPUT("")
End Sub
'-------------------------------------------------------------------------------------------------------------------
Private Delegate Sub D_Logupdate(ByVal str As String) '別スレッドからLOG書き込み
'-------------------------------------------------------------------------------------------------------------------
Sub LOGOUTPUT(ByVal strr As String)
If Me.InvokeRequired Then '別スレッドか判定
Me.Invoke(New D_Logupdate(AddressOf Logupdate), strr) '別スレッドならデリゲート
Else
Logupdate(strr)
End If
End Sub
'-------------------------------------------------------------------------------------------------------------------
Private Sub Logupdate(ByVal str As String) ' LOG書き出しルーチン
SyncLock logBox
logBox.Items.Add(str)
logBox.TopIndex = logBox.Items.Count - 1
logBox.Refresh()
End SyncLock
End Sub
'-------------------------------------------------------------------------------------------------------------------
Private Sub SerialPort1_DataReceived(sender As Object, e As Ports.SerialDataReceivedEventArgs) Handles SerialPort1.DataReceived
System.Console.Beep(440, 300) 'COMデータ受信しTCPに送信
Dim leng As Integer = 0
Dim dat(10000) As Byte
Try
leng = SerialPort1.Read(dat, 0, dat.GetLength(0))
Catch ex As TimeoutException
LOGOUTPUT("COM4ERR* " & ex.Message)
SeesionEND()
End Try
'*********************************************************TCP DATA SEND
Try
'クライアントにデータを送信する
myStream.Write(dat, 0, leng)
If leng < 11 Then '10BYTE以下なら電文をLOGに、それ以上はLENGSを
Dim i_c As Integer = 0
Do Until i_c = leng
LOGOUTPUT("送信>> " & dat(i_c))
i_c = i_c + 1
Loop
Else
LOGOUTPUT("送信LNG>> " & leng.ToString)
End If
Catch ex As Exception
'エラー処理
LOGOUTPUT("* " & ex.Message)
SeesionEND()
End Try
End Sub
End Class
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
サーバールーチンですが当初TCPで受信しCOMにデータを送る時、クライアントと同じようにしていました。
resSize = myStream.Read(resBytes, 0, resBytes.Length) 'データ受信
SerialPort1.Write(resBytes, 0, resSize) 'データ送信
SerialPort1.Write(resBytes, 0, resSize) 'データ送信
シリアルが低速の場合は何の問題もありませんでしたが921.6KPSにしたら通信できない場合がでてきました。試行錯誤の末、次のように変更して事なきを得ました。サーバーのCOMへの通信はコマンドの送出で最長4BYTEです。1BYTEづつ送ってもレスポンスには影響ありません。
resSize = myStream.Read(resBytes, 0, resBytes.Length) 'データ受信
i_send = 0
Do Until i_send = resSize
SerialPort1.Write(resBytes, i_send, 1) '1BYTEづつ送信
i_send = i_send + 1
Loop
Do Until i_send = resSize
SerialPort1.Write(resBytes, i_send, 1) '1BYTEづつ送信
i_send = i_send + 1
Loop
原因は分かりません。相手(Serial機器)のあることですので誰もがこのようにしてうまくいくか分かりません。





