Imports System.Net
Imports System.Net.Sockets
Imports System.Threading

Public Class DevNetworkCommunication
    Inherits Transmitter

    Public m_Serial As Socket

    Public m_SerialComfig As UDPComfig
    Public m_Control As Control

    '接收数据队列
    Public m_ReceiveQueue As List(Of Cmdconfig) = New List(Of Cmdconfig)
    '特殊接收处理标志
    Public m_SpecialReceiveFlag As Integer
    Public recvBufferli As List(Of Byte)



    ''' <summary>
    ''' 透传数据包头
    ''' </summary>
    Public _fixedHead As Byte() = {&HAA, &H55}


    ''' <summary>
    ''' 透传数据固定ID
    ''' </summary>
    Public _fixeSystemId As Byte() = {&H54, &H33, &H53, &H41}


    ''' <summary>
    ''' 透传数据房间信息
    ''' </summary>
    Public _fixeRoomInfo As Byte() = {&HFF, &HFF, &HFF, &HFF}
    ''' <summary>
    ''' UDP通讯接收线程
    ''' </summary>
    Private _receiveThread As Thread

    Sub New(gControl As Control)

        m_SerialComfig = New UDPComfig
        m_ReceiveQueue = New List(Of Cmdconfig)
        m_ReceiveQueue.Clear()
        m_Control = gControl

        recvBufferli = New List(Of Byte)
        m_SpecialReceiveFlag = -1
        CommunicationTypeindex = 2
    End Sub

    Public Overrides Sub SendData(data As Cmdconfig)
        data.CmdSendTime = DateTime.Now
        If IsNothing(m_Serial) OrElse Not m_Serial.IsBound Then
            RuningLog.OutputLogsToTheControl(m_Control, New RuningLogConfig($"发送器未连接!", Color.Red), 1)
            Return
        End If

        '清空发送缓存
        m_Serial.SendTo(data.CmdSendData, m_SerialComfig.TargetEndPoint)

    End Sub

    Public Overrides Sub CloseTransmitter()
        If Not IsNothing(m_Serial) Then
            m_Serial.Shutdown(SocketShutdown.Both)
            m_Serial.Close()
            m_Serial = Nothing
        End If
        If Not IsNothing(_receiveThread) AndAlso _receiveThread.IsAlive Then
            _receiveThread.Abort()
        End If
    End Sub

    Public Overrides Sub ClearSendData()
        If IsNothing(m_ReceiveQueue) Then Return
        m_ReceiveQueue.Clear()
    End Sub
    Public Overrides Sub ClearReadData()


    End Sub

    Public Overrides Function ReceiveData(g_comfig As Cmdconfig) As Cmdconfig

    End Function
    ''' <summary>
    ''' UDP接收数据
    ''' </summary>
    Private Sub UdpRecviveData()

        Dim recvLength As Integer
        Dim recvBuffer(1024) As Byte

        While m_Serial.IsBound
            Try
                recvLength = m_Serial.ReceiveFrom(recvBuffer, m_SerialComfig.TargetEndPoint)
                '_countRXData += recvLength
                Dim buf As Byte() = New Byte(recvLength - 1) {}
                Array.Copy(recvBuffer, buf, recvLength)
                RuningLog.OutputLogsToTheControl(m_Control, New RuningLogConfig($"RX:{DataProcessing.ByteToString2(buf)}", Color.Blue), 1)
            Catch ex As Exception
                Console.WriteLine($"Udp Recv Error:{ex.Message}")
                Return
            End Try
            ReceivingDataProcessing(recvBuffer, recvLength)
        End While
        ' If Not IsNothing(m_Transmitter) AndAlso m_Transmitter.IsTransmitter Then m_Transmitter.CloseTransmitter()
    End Sub

    ''' <summary>
    ''' 接收数据处理
    ''' </summary>
    ''' <param name="recvBuffer"></param>
    ''' <param name="recvLength"></param>
    Private Sub ReceivingDataProcessing(recvBuffer() As Byte, recvLength As Integer)
        Dim startIndex As Integer            '定义一个开始的计数值
        Dim packetLength As Integer              '定义包的长度
        While startIndex < recvLength               '判断计数值是否小于总包长    
            If recvBuffer(startIndex) = _fixedHead(0) AndAlso recvBuffer(startIndex + 1) = _fixedHead(1) Then         ' 判断包头是否等于AA55
                If recvBuffer(startIndex + 4) = _fixeSystemId(0) AndAlso                    '判断第4,5,6,7位是否等于固定值
                   recvBuffer(startIndex + 5) = _fixeSystemId(1) AndAlso
                   recvBuffer(startIndex + 6) = _fixeSystemId(2) AndAlso
                   recvBuffer(startIndex + 7) = _fixeSystemId(3) Then
                    packetLength = recvBuffer(startIndex + 2) + recvBuffer(startIndex + 3) * 256     '赋值包长
                    If packetLength + startIndex <= recvLength Then                  '判断包长 + 计数值是否小于等于总包长
                        Dim packetData(packetLength - 1) As Byte                     '定义一个新数据包数组   
                        Array.Copy(recvBuffer, startIndex, packetData, 0, packetLength)      '复制数组 +5个重载
                        If CheckCRC16Sucess(packetData, packetData.Length) Then          '判断CRC16校验是否通过  
                            '显示数据
                            DealData(packetData, packetLength)             '分部处理数据（显示文字提示）
                            startIndex += packetData.Length                 '计数值更新
                        Else
                            RuningLog.OutputLogsToTheControl(m_Control, New RuningLogConfig($"CRC16校验失败！", Color.Red), 1)
                            Exit While
                        End If
                    Else
                        RuningLog.OutputLogsToTheControl(m_Control, New RuningLogConfig($"数据包长校验失败！", Color.Red), 1)
                        Exit While
                    End If
                Else
                    RuningLog.OutputLogsToTheControl(m_Control, New RuningLogConfig($"数据校验失败！", Color.Red), 1)
                    Exit While
                End If
            Else
                RuningLog.OutputLogsToTheControl(m_Control, New RuningLogConfig($"非RCU主机发送的数据！", Color.Red), 1)
                Exit While
            End If
        End While
    End Sub

    ''' <summary>
    ''' 检验CRC16成功
    ''' </summary>
    ''' <param name="dataBuff"></param>
    ''' <param name="length"></param>
    ''' <returns></returns>
    Private Function CheckCRC16Sucess(dataBuff() As Byte, length As Integer) As Boolean
        Dim crcBytes() As Byte = GetCRC16CheckSum(dataBuff, length - 2)
        If crcBytes(0) <> dataBuff(length - 2) OrElse crcBytes(1) <> dataBuff(length - 1) Then Return False
        Return True
    End Function



    ''' <summary>
    ''' 处理数据
    ''' </summary>
    ''' <param name="dataBuff"></param>
    ''' <param name="length"></param>
    ''' <returns></returns>
    Private Function DealData(dataBuff() As Byte, length As Integer) As Boolean
        Select Case dataBuff(8)
            Case &H70
                '获取队列第5个元素
                Dim len As Integer = m_ReceiveQueue.Count
                Dim item As Cmdconfig
                Dim buf(length - 17 - 1) As Byte
                Array.Copy(dataBuff, 15, buf, 0, buf.Length)

                For i = 0 To len - 1
                    If (len - 1 - i) < m_ReceiveQueue.Count Then
                        item = m_ReceiveQueue(len - 1 - i)
                        If dataBuff(15) = 2 Then
                            item.OtherProtocol.UDP_TRANSPARENT_MODE_COMMAND_REPLY_STATUS = 1
                            item.OtherProtocol.UDP_TRANSPARENT_MODE_COMMAND_REPLY_RESULT = dataBuff(16)
                        End If
                        If dataBuff(15) = 1 Then
                            item.OtherProtocol.UDP_QUERY_HOST_CURRENT_MODE_COMMAND_REPLY_STATUS = 1
                            item.OtherProtocol.UDP_QUERY_HOST_CURRENT_MODE_COMMAND_REPLY_RESULT = dataBuff(16)
                        End If

                        'item.Protocol.ParseReceiveData(item, buf)
                    End If
                Next
            Case &H71
                Dim len As Integer = m_ReceiveQueue.Count
                Dim item As Cmdconfig
                Dim buf(length - 17 - 1 - 2) As Byte
                If 17 + buf.Length < dataBuff.Length Then
                    Array.Copy(dataBuff, 17, buf, 0, buf.Length)
                    For i = 0 To len - 1
                        If (len - 1 - i) < m_ReceiveQueue.Count Then
                            item = m_ReceiveQueue(len - 1 - i)
                            'If dataBuff(15) = 2 Then
                            '    item.OtherProtocol.UDP_TRANSPARENT_MODE_COMMAND_REPLY_STATUS = 1
                            '    item.OtherProtocol.UDP_TRANSPARENT_MODE_COMMAND_REPLY_RESULT = dataBuff(16)
                            'End If
                            'If dataBuff(15) = 1 Then
                            '    item.OtherProtocol.UDP_QUERY_HOST_CURRENT_MODE_COMMAND_REPLY_STATUS = 1
                            '    item.OtherProtocol.UDP_QUERY_HOST_CURRENT_MODE_COMMAND_REPLY_RESULT = dataBuff(16)
                            'End If

                            item.Protocol.ParseReceiveData2(item, buf)
                        End If
                    Next
                End If

        End Select
        Return True
    End Function

    ''' <summary>
    ''' 获取CRC16校验和
    ''' </summary>
    ''' <param name="dataBuff">数据</param>
    ''' <param name="length">数据长度</param>
    ''' <returns></returns>
    Private Function GetCRC16CheckSum(dataBuff() As Byte, length As Integer) As Byte()
        Dim crc16 As UInteger
        Dim crcBytes() As Byte

        crc16 = &HFFFF
        For i = 0 To length - 1
            crc16 = crc16 And &HFFFF
            crc16 = crc16 Xor dataBuff(i)
            For bit = 0 To 7
                crc16 = IIf((crc16 And 1) = 0, crc16 >> 1, (crc16 >> 1) Xor &HA001)
            Next
        Next
        crc16 = crc16 And &HFFFF
        crcBytes = BitConverter.GetBytes(UShort.Parse(crc16))
        Return crcBytes
    End Function

    Public Overrides Function IsTransmitter() As Boolean
        If IsNothing(m_Serial) OrElse m_Serial.Connected = False Then  Return False
        Return True
    End Function

    Public Overrides Function OpenTransmitter() As Boolean
        If Not IsNothing(m_Serial) Then
            m_Serial.Shutdown(SocketShutdown.Both)
            m_Serial.Close()
        End If
        If Not IsNothing(_receiveThread) AndAlso _receiveThread.IsAlive Then
            _receiveThread.Abort()
        End If

        m_Serial = New Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp)
        ''加保护机制
        m_Serial.Bind(m_SerialComfig.LocalEndPoint)
        m_Serial.Connect(m_SerialComfig.TargetEndPoint)
        _receiveThread = New Thread(New ThreadStart(AddressOf UdpRecviveData))
        _receiveThread.Start()
    End Function

    Public Overrides Function GetTransmitterStatus() As Boolean
        If IsNothing(m_Serial) Then Return False
        Return True
    End Function
    ''' <summary>
    ''' 本地ip/本地端口/目标ip/目标端口
    ''' </summary>
    ''' <param name="params"></param>
    ''' <returns></returns>
    Public Overrides Function SetTransmitterParameter(ParamArray params() As Object) As Boolean
        If IsNothing(m_SerialComfig) Then Return False
        Dim ipAddr As IPAddress
        Dim port As Integer
        If params.Length > 0 Then
            '判断是否为ip
            If IPAddress.TryParse(params(0), ipAddr) Then
                m_SerialComfig.LocalIP = ipAddr
            End If
            If params.Length > 1 Then
                '判断是否为端口
                If Integer.TryParse(params(1), port) Then
                    m_SerialComfig.LocalPort = port
                End If
            End If
            If params.Length > 2 Then
                '判断是否为ip
                If IPAddress.TryParse(params(2), ipAddr) Then
                    m_SerialComfig.TargetIP = ipAddr
                End If
                If params.Length > 3 Then
                    '判断是否为端口
                    If Integer.TryParse(params(3), port) Then
                        m_SerialComfig.TargetPort = port
                    End If
                End If
            End If
        End If

        '判断本地ip和 端口是否有效
        If IsNothing(m_SerialComfig.LocalIP) OrElse IsNothing(m_SerialComfig.LocalPort) Then
            MsgBox("本地ip或端口无效")
        Else
            '判断目标ip和 端口是否有效
            m_SerialComfig.LocalEndPoint = New IPEndPoint(m_SerialComfig.LocalIP, m_SerialComfig.LocalPort)
        End If
        '判断目标ip和 端口是否有效
        If IsNothing(m_SerialComfig.TargetIP) OrElse IsNothing(m_SerialComfig.TargetPort) Then
            MsgBox("目标ip或端口无效")
        Else
            m_SerialComfig.TargetEndPoint = New IPEndPoint(m_SerialComfig.TargetIP, m_SerialComfig.TargetPort)
        End If




    End Function
    '判断是否为端口
    Private Function IsPort(port As String) As Boolean
        Return Integer.TryParse(port, port)
    End Function
    '判断是否为ip
    Private Function IsIP(ip As String) As Boolean
        Dim ipAddr As IPAddress
        Return IPAddress.TryParse(ip, ipAddr)
    End Function

    Public Overrides Function SendData(data() As Byte) As Boolean
        'data.CmdSendTime = DateTime.Now
        If IsNothing(m_Serial) Then
            RuningLog.OutputLogsToTheControl(m_Control, New RuningLogConfig($"发送器未连接!", Color.Red), 1)
            Return False
        End If

        '清空发送缓存
        m_Serial.SendTo(data, m_SerialComfig.TargetEndPoint)
        Return True
    End Function
End Class
Public Class UDPComfig
    '本地ip
    Public LocalIP As IPAddress
    '本地端口
    Public LocalPort As Integer
    '目标ip
    Public TargetIP As IPAddress
    '目标端口
    Public TargetPort As Integer
    '发送网络端点
    Public LocalEndPoint As IPEndPoint
    '接收网络端点
    Public TargetEndPoint As IPEndPoint

    '获取 串口名称列表
    Sub New()
        LocalIP = GetLocalIP.Item(0)

    End Sub

    '获取本地IP列表
    Public Shared Function GetLocalIP() As List(Of IPAddress)
        Dim address() As IPAddress
        Dim destAddress As New List(Of IPAddress)
        address = Dns.GetHostEntry(Dns.GetHostName()).AddressList
        For Each pAddress As IPAddress In address
            If pAddress.AddressFamily = AddressFamily.InterNetwork Then
                destAddress.Add(pAddress)
            End If
        Next
        Return destAddress
    End Function

End Class