Public Class BlvBus
    Private RichTextBox1 As RichTextBox
    Private g_DevType As DEV_TYPE = DEV_TYPE.NANO_485

    Sub New(ByRef RichTextBox As RichTextBox)
        RichTextBox1 = RichTextBox
    End Sub

    Public Enum FMT
        ADD_FM
        SEND_TYPE
        DEV_TYPE
        ADD_TO
        LEN
        CHECK
        CMD
        PARAM
    End Enum

    Public Enum CMD
        LOADER_CMD_SEARCH = &HC0
        LOADER_CMD_JUMP_TO_BOOT = &HC1
    End Enum

    Public Enum DEV_TYPE
        NANO_485
        PB_485
        BLV_C1
    End Enum

    Const LOADER_CMD_OFFSET = &H10
    Const LOADER_PKT_LEN = 512

    Private COLOR_ERROR As Color = Color.Red
    Private COLOR_NORMAL As Color = Color.Blue
    Private COLOR_MESSAGE As Color = Color.Black

    Private g_SendBuffer(LOADER_PKT_LEN - 1) As Byte
    Private g_RecvBuffer(LOADER_PKT_LEN - 1) As Byte
    Private g_RecvLen As UInt16 = 0
    Private g_SendSn As Byte = 1
    Private g_RichTextBoxLastTick As UInt32

    Public Function SearchDeviceCmd(ByVal devType As Byte, ByVal port As System.IO.Ports.SerialPort, ByVal address As Byte, ByRef devId As String, ByRef bootFlag As String, ByRef devModel As String, ByVal groupFlag As Boolean, ByVal val As DEV_TYPE, Optional ByVal maxRetry As Integer = 3, Optional ByVal timeout As Integer = 100) As Boolean
        g_DevType = val
        Array.Clear(g_SendBuffer, 0, g_SendBuffer.Length)
        Array.Clear(g_RecvBuffer, 0, g_RecvBuffer.Length)
        FillSendBuf(devType, g_SendBuffer, CMD.LOADER_CMD_SEARCH, 0, address, groupFlag)

        If WriteToDevice(devType, port, g_SendBuffer, g_SendBuffer(FMT.LEN), g_RecvBuffer, g_RecvLen, groupFlag, maxRetry, timeout) Then
            devId = g_RecvBuffer(FMT.PARAM).ToString("X02") & " " & g_RecvBuffer(FMT.PARAM + 1).ToString("X02") & " " & g_RecvBuffer(FMT.PARAM + 2).ToString("X02") & " " & g_RecvBuffer(FMT.PARAM + 3).ToString("X02")
            bootFlag = "APP"
            devModel = System.Text.Encoding.ASCII.GetString(g_RecvBuffer, FMT.PARAM + 4, g_RecvLen - FMT.PARAM - 4)
            Return True
        End If
        Return False
    End Function

    Public Function JumpBootCmd(ByVal devType As Byte, ByVal port As System.IO.Ports.SerialPort, ByVal address As Byte, ByVal groupFlag As Boolean, ByVal val As DEV_TYPE, ByVal maxRetry As Integer, ByVal timeout As Integer) As Boolean
        g_DevType = val
        Array.Clear(g_SendBuffer, 0, g_SendBuffer.Length)
        Array.Clear(g_RecvBuffer, 0, g_RecvBuffer.Length)
        FillSendBuf(devType, g_SendBuffer, CMD.LOADER_CMD_JUMP_TO_BOOT, 0, address, groupFlag)

        Return WriteToDevice(devType, port, g_SendBuffer, g_SendBuffer(FMT.LEN), g_RecvBuffer, g_RecvLen, groupFlag, maxRetry, timeout)
    End Function

    'Public Sub GroupJumpBootCmd(ByVal port As System.IO.Ports.SerialPort, ByVal groupFlag As Boolean， ByVal val As DEV_TYPE, Optional ByVal maxRetry As Integer = 3, Optional ByVal timeout As Integer = 100)
    '    Dim address As Byte
    '    g_DevType = val
    '    Array.Clear(g_SendBuffer, 0, g_SendBuffer.Length)
    '    Array.Clear(g_RecvBuffer, 0, g_RecvBuffer.Length)
    '    FillSendBuf(g_SendBuffer, CMD.LOADER_CMD_JUMP_TO_BOOT, 0, address, groupFlag)

    '    WriteToDevice(port, g_SendBuffer, g_SendBuffer(FMT.LEN), g_RecvBuffer, g_RecvLen, groupFlag, maxRetry, timeout)
    'End Sub

    Private Function WriteToDevice(ByVal devType As Byte, ByVal port As System.IO.Ports.SerialPort, ByVal sendBuf() As Byte, ByVal sendLen As UInt16, ByRef recvBuf() As Byte, ByRef recvLen As UInt16, ByVal groupFlag As Boolean, Optional ByVal maxRetry As Integer = 3, Optional ByVal timeout As Integer = 100) As Boolean
        Dim retryCount As Byte = 0
        Dim result As Boolean = False
        Dim retFlag As Boolean = False
        Dim timeTick As UInt32
        Dim rLen As Integer = 0
        Dim readIndex As UInt16 = 0

        Do
            RichTextBox_Show("TX", sendBuf, sendLen, COLOR_MESSAGE)
            retFlag = UartSendBuf(port, sendBuf, sendLen)                 '发送数据
            WaitSendComp(port)                                          '等待发送出去

            If g_DevType = DEV_TYPE.PB_485 Then
                Delay(0.8 * sendLen)
            End If

            If retFlag Then
                If groupFlag = False Then                           '对于 groupFlag = false 应该要做延时，不能一直不停的发
                    retFlag = False
                    timeTick = My.Computer.Clock.TickCount
                    Do
                        Try
                            rLen = port.BytesToRead
                        Catch ex As Exception
                            rLen = 0
                        End Try

                        If rLen > 0 Then                                    '等待接收数据
                            timeTick = My.Computer.Clock.TickCount
                            port.Read(recvBuf, readIndex, rLen)
                            readIndex += rLen
                            If readIndex > FMT.LEN AndAlso readIndex >= recvBuf(FMT.LEN) Then
                                retFlag = True
                                Exit Do
                            End If
                        End If
                    Loop Until (My.Computer.Clock.TickCount - timeTick) > timeout

                    If retFlag Then
                        RichTextBox_Show("RX", recvBuf, readIndex, COLOR_MESSAGE)
                        If recvBuf(FMT.ADD_FM) = sendBuf(FMT.ADD_TO) AndAlso
                              recvBuf(FMT.ADD_TO) = sendBuf(FMT.ADD_FM) AndAlso
                              recvBuf(FMT.DEV_TYPE) = devType AndAlso
                              recvBuf(FMT.LEN) = readIndex AndAlso
                              CalcCheckSum(recvBuf, readIndex) = 0 AndAlso
                              recvBuf(FMT.CMD) = sendBuf(FMT.CMD) + LOADER_CMD_OFFSET Then
                            recvLen = readIndex
                            result = True
                        End If
                    Else
                        If readIndex > 0 Then
                            RichTextBox_Show("RX", recvBuf, readIndex, COLOR_ERROR)
                        Else
                            RichTextBox_Show("没有接收到数据", COLOR_ERROR)
                        End If
                    End If
                Else
                    Delay(20)
                End If
            End If

            'Application.DoEvents()

            If result = True Then Exit Do
            retryCount += 1
        Loop Until retryCount >= maxRetry

        Return result
    End Function

    Private Sub FillSendBuf(ByVal devType As Byte, ByRef sendBuf() As Byte, cmd As Byte, paramLen As UInt16, ByVal address As Byte, ByVal groupFlag As Boolean)

        sendBuf(FMT.ADD_FM) = &H0
        sendBuf(FMT.SEND_TYPE) = g_SendSn And &HF
        sendBuf(FMT.DEV_TYPE) = devType
        If groupFlag Then
            sendBuf(FMT.SEND_TYPE) = sendBuf(FMT.SEND_TYPE) Or &H80
            sendBuf(FMT.ADD_TO) = &H0       '针对所有
        Else
            sendBuf(FMT.ADD_TO) = address
        End If
        sendBuf(FMT.LEN) = FMT.PARAM + paramLen
        sendBuf(FMT.CHECK) = 0
        sendBuf(FMT.CMD) = cmd
        sendBuf(FMT.CHECK) = CalcCheckSum(sendBuf, sendBuf(FMT.LEN))

        g_SendSn = g_SendSn + 1
        If g_SendSn > &HF Then
            g_SendSn = 0
        End If
    End Sub

    Private Sub WaitSendComp(ByVal port As System.IO.Ports.SerialPort)
        Do
            Delay(1)
        Loop Until port.BytesToWrite = 0
    End Sub

    Private Function UartSendBuf(ByVal port As System.IO.Ports.SerialPort, ByVal buf() As Byte, ByVal len As UInt16) As Boolean
        Try
            port.DiscardOutBuffer()
            port.DiscardInBuffer()
        Catch ex As Exception
            Return False
        End Try

        Try
            port.Write(buf, 0, len)
        Catch ex As Exception
            Return False
        End Try

        Return True
    End Function

    Private Sub Delay(ByVal time As Integer)
        Dim timeEnd As Integer = My.Computer.Clock.TickCount + time
        Do
            Application.DoEvents()
        Loop Until My.Computer.Clock.TickCount > timeEnd
    End Sub

    Private Function CalcCheckSum(ByVal buf() As Byte, ByVal len As UInt16) As Byte
        Dim sum As Int16 = 0
        Dim i As UInt16 = 0
        Dim retByte As Byte

        For i = 0 To len - 1
            sum += buf(i)
            sum = sum And &HFF
        Next

        retByte = (Not sum) And &HFF

        Return retByte
    End Function

    Private Sub RichTextBox_AddTick()
        Dim str As String = ""

        str = "["
        str += My.Computer.Clock.TickCount.ToString.PadLeft(10, " ")
        str += "] ["
        str += (My.Computer.Clock.TickCount - g_RichTextBoxLastTick).ToString.PadLeft(5, " ")
        str += "]"
        RichTextBox1.AppendText(str)
        g_RichTextBoxLastTick = My.Computer.Clock.TickCount
    End Sub

    Private Sub RichTextBox_Show(ByVal str As String, ByVal cor As Color)
        Dim tempSelStart As Integer
        Dim tempSelLenght As Integer

        RichTextBox_AddTick()

        tempSelStart = RichTextBox1.TextLength
        RichTextBox1.AppendText(str)
        tempSelLenght = RichTextBox1.TextLength - tempSelStart
        RichTextBox1.Select(tempSelStart, tempSelLenght)        '选中文本
        RichTextBox1.SelectionColor = cor                       '当前选中的文本显示颜色
        RichTextBox1.AppendText(vbNewLine)
        RichTextBox1.ScrollToCaret()      '将内容滚动的作用
    End Sub

    Private Sub RichTextBox_Show(ByVal str1 As String, ByVal buf() As Byte, ByVal len As UInt16, ByVal cor As Color)
        Dim tempSelStart As Integer
        Dim tempSelLenght As Integer
        Dim i As UInt16
        Dim str As String = ""

        RichTextBox_AddTick()

        tempSelStart = RichTextBox1.TextLength
        RichTextBox1.AppendText(str1)
        RichTextBox1.AppendText("--")
        For i = 0 To len - 1
            str += Hex(buf(i)).PadLeft(2, "0")
            str += " "
        Next
        RichTextBox1.AppendText(str)
        tempSelLenght = RichTextBox1.TextLength - tempSelStart
        RichTextBox1.Select(tempSelStart, tempSelLenght)        '选中文本
        RichTextBox1.SelectionColor = cor                       '当前选中的文本显示颜色
        RichTextBox1.AppendText(vbNewLine)
        RichTextBox1.ScrollToCaret()      '将内容滚动的作用
    End Sub


End Class
