Imports System.IO
Imports System.Net
Imports System.Net.Sockets
Imports System.Security.Cryptography.Xml
Imports System.Text
Imports System.Threading
Imports Microsoft.SqlServer
Imports RS_WF62_terminal_VB.RSWF62

Public Class RSWF62
    Public Structure DeviceInfo
        Public SSID As String
        Public MacAdrs As String
        Public Version As String
        Public Mode As String
        Public IPAdrs As String
    End Structure

    Public rcvClient As UdpClient
    Private LedThread As Thread
    Private ScanThread As Thread
    Public FrmLEDMsg As FrmLED
    Public FrmScanMsg As FrmScan
    Public WaitHandle As EventWaitHandle

    ' MACアドレス・IPアドレス・バージョンを保管するリスト
    Public foundList As New Dictionary(Of Integer, DeviceInfo)()

    Private ClearLst As New ClearLstDataDelegate(AddressOf ClearLstData)
    Delegate Sub ClearLstDataDelegate()
    Private Sub ClearLstData()
        ' リスト初期化
        LstDevice.Items.Clear()
    End Sub

    Private SetLst As New SetLstDataDelegate(AddressOf SetLstData)
    Delegate Sub SetLstDataDelegate(ByVal Id As Integer, ByVal str1 As String, ByVal str2 As String, ByVal str3 As String, ByVal str4 As String)
    Private Sub SetLstData(ByVal Id As Integer, ByVal str1 As String, ByVal str2 As String, ByVal str3 As String, ByVal str4 As String)
        ' データを設定
        LstDevice.Items.Add(str1)
        LstDevice.Items(Id).SubItems.Add(str2)
        LstDevice.Items(Id).SubItems.Add(str3)
        LstDevice.Items(Id).SubItems.Add(str4)
    End Sub

    Private GetLst As New GetLstDataDelegate(AddressOf GetLstData)
    Delegate Function GetLstDataDelegate() As String
    Private Function GetLstData() As String
        ' データを取得
        GetLstData = LstDevice.SelectedItems(0).SubItems(1).Text
    End Function

    Private ClearCmb As New ClearCmbDataDelegate(AddressOf ClearCmbData)
    Delegate Sub ClearCmbDataDelegate()
    Private Sub ClearCmbData()
        ' データをクリア
        CmbComPort.Items.Clear()
    End Sub

    Private SetCmb As New SetCmbDataDelegate(AddressOf SetCmbData)
    Delegate Sub SetCmbDataDelegate(ByVal str1 As String)
    Private Sub SetCmbData(ByVal str1 As String)
        ' データを設定
        CmbComPort.Items.Add(str1)
        ' 選択デバイス
        CmbComPort.SelectedIndex = 0
    End Sub

    Private EnableBtn As New EnableBtnDataDelegate(AddressOf EnableBtnData)
    Delegate Sub EnableBtnDataDelegate(ByVal btn As Button, ByVal fEnable As Boolean)
    Private Sub EnableBtnData(ByVal btn As Button, ByVal fEnable As Boolean)
        ' 無効/有効
        btn.Enabled = fEnable
    End Sub


    ' 初期設定
    Private Sub RSWF62_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        ' イベントハンドル
        WaitHandle = New EventWaitHandle(False, EventResetMode.ManualReset)
        WaitHandle.Reset()

        ' スキャン
        ReScan()
    End Sub

    ' 終了処理
    Private Sub RSWF62_FormClosing(sender As Object, e As FormClosingEventArgs) Handles MyBase.FormClosing
        If ((FrmScanMsg Is Nothing) = False) Then
            If (FrmScanMsg.IsDisposed = False) Then
                ' クローズ
                FrmScanMsg.Close()
            End If
        End If
        If ((FrmLEDMsg Is Nothing) = False) Then
            If (FrmLEDMsg.IsDisposed = False) Then
                ' クローズ
                FrmLEDMsg.Close()
            End If
        End If

        ' イベントクローズ
        WaitHandle.Close()
    End Sub

    ' 再スキャン
    Private Sub BtnReScan_Click(sender As Object, e As EventArgs) Handles BtnReScan.Click
        ' スキャン
        ReScan()
    End Sub

    ' 終了
    Private Sub BtnExit_Click(sender As Object, e As EventArgs) Handles BtnExit.Click
        Close()
    End Sub

    ' LED確認
    Private Sub BtnLED_Click(sender As Object, e As EventArgs) Handles BtnLED.Click

        If LstDevice.Items.Count > 0 Then
            ' 何も選択してないなら終了
            If LstDevice.SelectedIndices.Count = 0 Then
                MessageBox.Show("デバイスを選択していません")
                Exit Sub
            End If

            ' ボタン無効
            BtnReScan.Enabled = False
            BtnLED.Enabled = False
            BtnTCPIP.Enabled = False
            BtnComPort.Enabled = False
            BtnExit.Enabled = False

            ' LED
            Task.Run(Sub()
                         LEDProc()
                     End Sub)

        Else
            MessageBox.Show("デバイスがありません")
        End If

    End Sub

    ' LED
    Private Sub LEDProc()
        Dim port As Integer
        Dim ip As IPAddress
        Dim client As TcpClient
        Dim iii As Integer
        Dim mStream As NetworkStream
        Dim writeBuf() As Byte
        Dim resultBuf() As Byte
        Dim strIPAdrs As String

        ' IPアドレス
        strIPAdrs = LstDevice.Invoke(GetLst)
        ip = IPAddress.Parse(strIPAdrs)
        ' ポート番号
        port = 5074

        ' クライアント
        client = New TcpClient(ip.ToString(), port)
        ' ストリーム
        mStream = client.GetStream()
        mStream.ReadTimeout = 1000
        mStream.WriteTimeout = 1000

        ' LEDメッセージ
        ' スレッド開始
        LedThread = New Thread(AddressOf LEDMsgProc)
        LedThread.Start()

        Try
            ' コマンドモードに変更
            ReDim writeBuf(3 + 2)
            ' 送受信
            writeBuf(0) = &H24 ' '$'
            writeBuf(1) = &H24 ' '$'
            writeBuf(2) = &H24 ' '$'
            writeBuf(3) = &HD
            writeBuf(4) = &HA
            resultBuf = writeReadCommand(mStream, writeBuf)

            ' 3回リトライ
            For iii = 0 To 2
                ReDim writeBuf(12 + 3 + 2)
                ' 送受信
                writeBuf(0) = &H73 ' 's'
                writeBuf(1) = &H65 ' 'e'
                writeBuf(2) = &H74 ' 't'
                writeBuf(3) = &H20 ' ' '
                writeBuf(4) = &H73 ' 's'
                writeBuf(5) = &H79 ' 'y'
                writeBuf(6) = &H73 ' 's'
                writeBuf(7) = &H20 ' ' '
                writeBuf(8) = &H6C ' 'l'
                writeBuf(9) = &H65 ' 'e'
                writeBuf(10) = &H64 ' 'd'
                writeBuf(11) = &H20 ' ' '
                writeBuf(12) = &H30 ' 3つで
                writeBuf(13) = &H78 ' [0x2]
                writeBuf(14) = &H32 ' とする
                writeBuf(15) = &HD
                writeBuf(16) = &HA
                resultBuf = writeReadCommand(mStream, writeBuf)

                Thread.Sleep(1000)

                ReDim writeBuf(12 + 3 + 2)
                ' 送受信
                writeBuf(0) = &H73 ' 's'
                writeBuf(1) = &H65 ' 'e'
                writeBuf(2) = &H74 ' 't'
                writeBuf(3) = &H20 ' ' '
                writeBuf(4) = &H73 ' 's'
                writeBuf(5) = &H79 ' 'y'
                writeBuf(6) = &H73 ' 's'
                writeBuf(7) = &H20 ' ' '
                writeBuf(8) = &H6C ' 'l'
                writeBuf(9) = &H65 ' 'e'
                writeBuf(10) = &H64 ' 'd'
                writeBuf(11) = &H20 ' ' '
                writeBuf(12) = &H30 ' 3つで
                writeBuf(13) = &H78 ' [0x3]
                writeBuf(14) = &H33 ' とする
                writeBuf(15) = &HD
                writeBuf(16) = &HA
                resultBuf = writeReadCommand(mStream, writeBuf)

                Thread.Sleep(1000)
            Next iii

            ' モードを戻す
            ReDim writeBuf(4 + 2)
            ' 送受信
            writeBuf(0) = &H65 ' 'e'
            writeBuf(1) = &H78 ' 'x'
            writeBuf(2) = &H69 ' 'i'
            writeBuf(3) = &H74 ' 't'
            writeBuf(4) = &HD
            writeBuf(5) = &HA
            resultBuf = writeReadCommand(mStream, writeBuf)

            ' ストリームクローズ
            mStream.Close()
            ' クライアントクローズ
            client.Close()

            ' ボタン有効
            BtnReScan.Invoke(EnableBtn, BtnReScan, True)
            BtnLED.Invoke(EnableBtn, BtnLED, True)
            BtnTCPIP.Invoke(EnableBtn, BtnTCPIP, True)
            BtnComPort.Invoke(EnableBtn, BtnComPort, True)
            BtnExit.Invoke(EnableBtn, BtnExit, True)

            ' クローズ
            FrmLEDMsg.Close()
        Catch ex As Exception
            ' ボタン有効
            BtnReScan.Invoke(EnableBtn, BtnReScan, True)
            BtnLED.Invoke(EnableBtn, BtnLED, True)
            BtnTCPIP.Invoke(EnableBtn, BtnTCPIP, True)
            BtnComPort.Invoke(EnableBtn, BtnComPort, True)
            BtnExit.Invoke(EnableBtn, BtnExit, True)

            ' クローズ
            FrmLEDMsg.Close()
        End Try
    End Sub

    ' LEDメッセージ
    Private Sub LEDMsgProc()
        FrmLEDMsg = New FrmLED()
        ' 表示
        FrmLEDMsg.ShowDialog()
    End Sub

    Public Function writeReadCommand(ByVal mStream As NetworkStream, ByVal writeBuf As Byte()) As Byte()
        Dim TotalCount As Integer
        Dim memStream As New MemoryStream()
        Dim rcvBuf As Byte() = New Byte(256) {}
        Dim readCount As Integer
        readCount = 0
        TotalCount = 0

        ' データが空でない
        If writeBuf.Length <> 0 Then
            Try
                ' コマンド送信
                mStream.Write(writeBuf, 0, writeBuf.Length - 1)

                ' 受信したデータを出力ストリームに書き込む
                readCount = mStream.Read(rcvBuf, 0, rcvBuf.Length)
                While (readCount > 0)
                    memStream.Write(rcvBuf, TotalCount, readCount)
                    TotalCount += readCount

                    readCount = mStream.Read(rcvBuf, 0, rcvBuf.Length)
                End While
                writeReadCommand = memStream.ToArray()
                Exit Function
            Catch ex As Exception
                If TotalCount > 0 Then
                    writeReadCommand = memStream.ToArray()
                End If
                Debug.WriteLine(ex.Message)
            End Try
        End If
        writeReadCommand = memStream.ToArray()
    End Function

    Private Sub BtnTCPIP_Click(sender As Object, e As EventArgs) Handles BtnTCPIP.Click
        If LstDevice.Items.Count > 0 Then
            ' 何も選択してないなら終了
            If LstDevice.SelectedIndices.Count = 0 Then
                MessageBox.Show("デバイスを選択していません")
                Exit Sub
            End If

            Dim FrmTerminal As New Terminal()

            ' 非表示
            Visible = False

            ' TCP/IPモード
            FrmTerminal.IPAdrs = LstDevice.SelectedItems(0).SubItems(1).Text
            FrmTerminal.connectType = 0

            ' 表示
            FrmTerminal.ShowDialog(Me)
            ' 終了
            FrmTerminal.Dispose()

            ' 表示
            Visible = True
        Else
            MessageBox.Show("デバイスがありません")
        End If
    End Sub

    Private Sub BtnComPort_Click(sender As Object, e As EventArgs) Handles BtnComPort.Click
        Dim FrmTerminal As New Terminal()

        ' 非表示
        Visible = False

        ' COMモード
        FrmTerminal.PortName = CmbComPort.GetItemText(CmbComPort.SelectedItem)
        FrmTerminal.connectType = 1

        ' 表示
        FrmTerminal.ShowDialog(Me)
        ' 終了
        FrmTerminal.Dispose()

        ' 表示
        Visible = True

    End Sub

    ' スキャン準備
    Private Sub ReScan()
        ' デバイス名削除
        TxtSelectDevice.Text = String.Empty
        ' ボタン無効
        BtnReScan.Enabled = False
        BtnLED.Enabled = False
        BtnTCPIP.Enabled = False
        BtnComPort.Enabled = False
        BtnExit.Enabled = False

        ' 検索
        Task.Run(Sub()
                     DeviceScanProc()
                 End Sub)
    End Sub

    ' スキャン
    Private Sub DeviceScanProc()
        ' 検索メッセージ
        ' スレッド開始
        ScanThread = New Thread(AddressOf DeviceScanMsgProc)
        ScanThread.Start()

        ' TCO/IP検索
        SearchDevice()
        ' COM検索
        SearchCom()

        ' ボタン有効
        BtnReScan.Invoke(EnableBtn, BtnReScan, True)
        BtnLED.Invoke(EnableBtn, BtnLED, True)
        BtnTCPIP.Invoke(EnableBtn, BtnTCPIP, True)
        BtnComPort.Invoke(EnableBtn, BtnComPort, True)
        BtnExit.Invoke(EnableBtn, BtnExit, True)

        ' クローズ
        FrmScanMsg.Close()

    End Sub

    ' スキャンメッセージ
    Private Sub DeviceScanMsgProc()
        FrmScanMsg = New FrmScan()
        ' 表示
        FrmScanMsg.ShowDialog()
    End Sub

    ' TCO/IP検索
    Private Sub SearchDevice()
        Dim fCheck As Boolean = False
        Dim timeCount As Integer
        Dim strSSID As String
        Dim strMac As String
        Dim strVer As String
        Dim strMode As String
        Dim strIPAdrs As String

        Dim id As Integer
        Dim DevInfo As DeviceInfo
        Dim DeviceCount As Integer

        ' リスト初期化
        LstDevice.Invoke(ClearLst)
        WaitHandle.Reset()

        ' UDP受信用のクライアントを立ち上げる
        Task.Run(Sub()
                     udpReceive(True)
                 End Sub)

        ' イベント待ち
        WaitHandle.WaitOne()

        timeCount = 0
        While timeCount < 10

            DeviceCount = 0
            ' UDP受信でデータ取得していれば抜ける
            For Each item As KeyValuePair(Of Integer, DeviceInfo) In foundList
                fCheck = True
                id = item.Key
                DevInfo = item.Value

                ' リストに追加
                strSSID = DevInfo.SSID
                strMac = DevInfo.MacAdrs
                strVer = DevInfo.Version
                strMode = DevInfo.Mode
                strIPAdrs = DevInfo.IPAdrs

                ' 受信したデータ表示
                LstDevice.Invoke(SetLst, DeviceCount, strSSID, strIPAdrs, strMode, strVer)
                DeviceCount = DeviceCount + 1
            Next

            If (fCheck) Then
                Exit Sub
            End If
            Thread.Sleep(1000)
            timeCount = timeCount + 1
        End While
    End Sub

    ' UDPでデータを受信し、端末リストとしてIPアドレス・MACアドレスに格納する
    Public Sub udpReceive(ByVal getWiFiMode As Boolean)
        Dim DevInfo As DeviceInfo
        Dim iii As Integer
        Dim StartPos As Integer
        Dim EndPos As Integer
        Dim Verbyte() As Byte
        Dim strSSID As String
        Dim strMac As String
        Dim strVer As String
        Dim strMode As String
        Dim DeviceCount As Byte
        Dim timeCount As Byte
        Dim Status As Boolean
        Dim id As Integer
        Dim strdata1 As String
        Dim strdata2 As String
        Dim strdata3 As String
        Dim strdata4 As String
        Dim FirstIP As Integer

        FirstIP = 0
        DeviceCount = 0
        Try
            ' UDP設定
            rcvClient = New UdpClient(New IPEndPoint(IPAddress.Any, 5073))
            rcvClient.Client.ReceiveTimeout = 20000

            timeCount = 0
            While (True)
                Dim remoteEP As IPEndPoint = Nothing
                ' リクエストを受信
                Dim recvBytes As Byte() = rcvClient.Receive(remoteEP)

                ' ポート5073の場合
                If remoteEP.Port = 5073 Then
                    strMac = String.Format("{0:X2}:{1:X2}:{2:X2}:{3:X2}:{4:X2}:{5:X2}", recvBytes(0), recvBytes(1), recvBytes(2), recvBytes(3), recvBytes(4), recvBytes(5))

                    strSSID = Encoding.ASCII.GetString(recvBytes, &H6A, 32)

                    StartPos = 0
                    EndPos = 0

                    For iii = 0 To recvBytes.Length - 1
                        If recvBytes(iii) = &H56 Then ' 'V'
                            If recvBytes(iii + 1) = &H65 Then ' 'e'
                                If recvBytes(iii + 2) = &H72 Then ' 'r'
                                    StartPos = iii + 3
                                End If
                            End If
                        End If
                    Next

                    For iii = StartPos To recvBytes.Length - 1
                        If recvBytes(iii) = &H2C Then ' ','
                            EndPos = iii
                        End If
                    Next

                    ReDim Verbyte(EndPos - StartPos)
                    For iii = StartPos To EndPos - 1
                        Verbyte(iii - StartPos) = recvBytes(iii)
                    Next
                    strVer = Encoding.ASCII.GetString(Verbyte)

                    If recvBytes(6) = 0 Then
                        strMode = "インフラストラクチャ"
                    ElseIf recvBytes(6) = 1 Then
                        strMode = "アクセスポイント"
                    Else
                        strMode = "Provisioning"
                    End If

                    DevInfo.SSID = strSSID
                    DevInfo.MacAdrs = strMac
                    DevInfo.Mode = strMode
                    DevInfo.Version = strVer
                    DevInfo.IPAdrs = remoteEP.Address.ToString()

                    strdata1 = DevInfo.IPAdrs
                    ' 1つめ検索
                    StartPos = strdata1.IndexOf(".")
                    strdata2 = strdata1.Substring(StartPos + 1, strdata1.Length - StartPos - 1)
                    ' 2つめ検索
                    StartPos = strdata2.IndexOf(".")
                    strdata3 = strdata2.Substring(StartPos + 1, strdata2.Length - StartPos - 1)
                    ' 3つめ検索
                    StartPos = strdata3.IndexOf(".")
                    strdata4 = strdata3.Substring(StartPos + 1, strdata3.Length - StartPos - 1)

                    ' IPAdrsの最後をIdとする
                    id = Integer.Parse(strdata4)

                    If FirstIP = 0 Then
                        FirstIP = id
                    Else
                        ' 同じIPアドレスから受信
                        If id = FirstIP Then
                            Exit While
                        End If
                    End If



                    ' MACアドレス/接続先IP/バージョンをリストに格納
                    Status = foundList.TryAdd(id, DevInfo)
                    If Status = True Then
                        DeviceCount = DeviceCount + 1
                    End If
                End If

                timeCount = timeCount + 1
                If (timeCount > 10) Then
                    Exit While
                End If

            End While

            ' クローズ
            rcvClient.Close()
            ' イベント設定
            WaitHandle.Set()

        Catch ex1 As ObjectDisposedException
            ' 外部で破棄(=切断)されている場合何もしない
            ' イベント設定
            WaitHandle.Set()
        Catch ex2 As NullReferenceException
            'PlusDebug.WriteLine(ex2.Message)
            ' イベント設定
            WaitHandle.Set()
        Catch ex3 As SocketException
            'PlusDebug.WriteLine(ex3.Message)
            ' イベント設定
            WaitHandle.Set()
        End Try
    End Sub

    Private Sub LstDevice_SelectedIndexChanged(sender As Object, e As EventArgs) Handles LstDevice.SelectedIndexChanged
        If LstDevice.SelectedItems.Count > 0 Then
            ' 何も選択してないなら終了
            If LstDevice.SelectedItems(0).Index < 0 Then
                Exit Sub
            End If

            ' デバイス名表示
            TxtSelectDevice.Text = LstDevice.SelectedItems(0).SubItems(0).Text
        Else
            ' デバイス名削除
            TxtSelectDevice.Text = String.Empty
        End If
    End Sub

    ' COM検索
    Private Sub SearchCom()
        ' コンボボックス初期化
        CmbComPort.Invoke(ClearCmb)

        ' レジストリから検索
        ' キーを読み取り専用で開く
        Dim regkey As Microsoft.Win32.RegistryKey =
                Microsoft.Win32.Registry.LocalMachine.OpenSubKey("HARDWARE\\DEVICEMAP\\SERIALCOMM", False)
        'キーが存在しないときは Nothing が返される
        If (regkey Is Nothing) = False Then
            ' 文字列を読み込む
            Dim stringValue As String() = regkey.GetValueNames()

            For Each item In stringValue
                ' コントロール設定
                CmbComPort.Invoke(SetCmb, regkey.GetValue(item))
            Next
            ' 閉じる
            regkey.Close()
        End If
    End Sub

End Class
