Imports System.IO.Ports
Imports System.Threading
Imports _485_BurningTool.BLV_Bootloader

Public Class SerialController
    Inherits Transmitter

    Public m_Serial As SerialPort

    Public m_SerialComfig As SerialComfig
    Public m_Control As Control
    Private Timer1 As System.Windows.Forms.Timer
    '接收数据队列
    Public m_ReceiveQueue As List(Of Cmdconfig) = New List(Of Cmdconfig)
    '特殊接收处理标志
    Public m_SpecialReceiveFlag As Integer

    Sub New(gControl As Control)

        CommunicationTypeindex = 1
        m_SerialComfig = New SerialComfig
        m_ReceiveQueue = New List(Of Cmdconfig)
        m_ReceiveQueue.Clear()
        m_Control = gControl
        'm_Serial = New SerialPort
        'CreateSerial(m_SerialComfig)
        '创建定时器绑定定时器函数
        'Timer1 = New System.Windows.Forms.Timer
        'Timer1.Interval = 1000
        'AddHandler Timer1.Tick, AddressOf TimerTick
        'Timer1.Start()
        recvBufferli = New List(Of Byte)
        m_SpecialReceiveFlag = -1
    End Sub
    '定时器函数 定时查看接收队列是否接收超时
    Public Sub TimerTick()
        Dim len As Integer = m_ReceiveQueue.Count
        For i = 0 To len - 1
            '从尾部倒序查看节点是否超时
            Dim item As Cmdconfig = m_ReceiveQueue(len - 1 - i)
            If (Now - item.CmdSendTime).TotalSeconds > item.CmdReceiveTimeout Then
                m_ReceiveQueue.RemoveAt(len - 1 - i)
            End If
        Next
    End Sub

    '创建串口连接
    Public Function CreateSerial(g_SerialComfig As SerialComfig) As Boolean

        Try
            If IsNothing(m_Serial) Then
                m_Serial = New SerialPort(g_SerialComfig.PortName, g_SerialComfig.BaudRate, g_SerialComfig.Parity, g_SerialComfig.DataBits, g_SerialComfig.StopBits)
                AddHandler m_Serial.DataReceived, AddressOf DataReceivedHandler
                m_Serial.Open()
                Return True
            Else
                Dim m_SerialIsOpen As Boolean = False
                If m_Serial.IsOpen Then
                    isread = True
                    While isListen
                        Application.DoEvents()
                        Thread.Sleep(10)
                    End While
                    Thread.Sleep(10)
                    m_Serial.Close()
                    m_SerialIsOpen = True
                End If
                m_Serial.PortName = g_SerialComfig.PortName
                m_Serial.BaudRate = g_SerialComfig.BaudRate
                m_Serial.Parity = g_SerialComfig.Parity
                m_Serial.DataBits = g_SerialComfig.DataBits
                m_Serial.StopBits = g_SerialComfig.StopBits
                m_Serial.Handshake = g_SerialComfig.Handshake
                If m_SerialIsOpen Then
                    m_Serial.Open()
                End If
            End If


        Catch ex As Exception
            Return False
        End Try
        Return True
    End Function


    Public Overrides Sub ClearReadData()
        If IsNothing(m_Serial) Then
            Return
        Else
            If m_Serial.IsOpen Then
                '清空接收缓存
                m_Serial.DiscardInBuffer()
            End If
        End If
        If IsNothing(recvBufferli) OrElse recvBufferli.Count = 0 Then
            Return
        Else
            recvBufferli.Clear()
        End If
    End Sub


    '设置串口参数

    ''' 接收数据偏移量
    Private _recvOffset, _recvstar As Integer
    ''' <summary>
    ''' 接收数据缓存包
    ''' </summary>
    Private _recvBuffer(4095) As Byte
    Public recvBufferli As List(Of Byte)
    Public recvBufferliindex As Integer = 0
    '获取 串口数据
    Private Sub DataReceivedHandler(sender As Object, e As SerialDataReceivedEventArgs)
        If isread Then Return

        isListen = True
        Static bytes As Integer = 0
        Dim sp As SerialPort = DirectCast(sender, SerialPort)

        Try
            Select Case m_SpecialReceiveFlag
                Case WorkflowType.SearchEquipment
                    Do
                        bytes = sp.BytesToRead
                        If bytes <= 0 Then
                            isListen = False
                            Exit Sub
                        End If
                        'If bytes + _recvOffset >= 4096 Then

                        '    'ShowPortReceData(_recvBuffer)

                        '    sp.Read(_recvBuffer, _recvOffset, 4096 - _recvOffset)

                        '    _recvOffset = 0
                        'Else
                        '    sp.Read(_recvBuffer, _recvOffset, bytes)
                        '    _recvOffset += bytes
                        'End If

                        Dim buf(bytes - 1) As Byte
                        sp.Read(buf, 0, bytes)
                        recvBufferli.AddRange(buf)
                        Thread.Sleep(1)
                    Loop While sp.BytesToRead > 0
                    If recvBufferli.Count > 0 Then
                        'RuningLog.OutputLogsToTheControl(m_Control, New RuningLogConfig($"RX:{DataProcessing.ByteToString2(recvBufferli.ToArray)}", Color.Black), 1)
                        Dim item As Cmdconfig
                        item = m_ReceiveQueue(0)
                        '判断超时
                        If item.CmdSendTime.AddMilliseconds(item.CmdReceiveTimeout) < Now Then
                            recvBufferli.Clear()
                            recvBufferliindex = 0
                            Console.WriteLine("超时清空-----------------------------")
                        Else
                            '截取搜索回复

                            recvBufferliindex = ExtractSearchReply(recvBufferli.ToArray, item)

                        End If
                    End If
                Case Else
                    Do
                        bytes = sp.BytesToRead
                        If bytes <= 0 Then
                            isListen = False
                            Exit Sub
                        End If
                        'If bytes + _recvOffset >= 4096 Then

                        '    'ShowPortReceData(_recvBuffer)

                        '    sp.Read(_recvBuffer, _recvOffset, 4096 - _recvOffset)

                        '    _recvOffset = 0
                        'Else
                        '    sp.Read(_recvBuffer, _recvOffset, bytes)
                        '    _recvOffset += bytes
                        'End If

                        Dim buf(bytes - 1) As Byte
                        sp.Read(buf, 0, bytes)
                        '清空可读缓存
                        'sp.DiscardInBuffer()

                        recvBufferli.AddRange(buf)


                        Thread.Sleep(7)
                    Loop While sp.BytesToRead > 0

                    If recvBufferli.Count > 0 Then



                        Dim buf(recvBufferli.Count - 1) As Byte
                        Array.Copy(recvBufferli.ToArray, 0, buf, 0, buf.Length)
                        recvBufferli.Clear()
                        ' Array.Copy(_recvBuffer, _recvstar, buf, 0, buf.Length)
                        'Console.WriteLine(DataProcessing.ByteToString2(buf))
                        '_recvstar = _recvOffset

                        '枚举队列
                        '获取队列第5个元素
                        Dim len As Integer = m_ReceiveQueue.Count
                        Dim item As Cmdconfig
                        Dim timep As TimeSpan
                        ' RuningLog.OutputLogsToTheControl(m_Control, New RuningLogConfig($"RX:{DataProcessing.ByteToString2(buf)}", Color.Blue), 1)
                        mCommunicationFlowAddQueue(m_Control, New RuningLogConfig($"RX:{DataProcessing.ByteToString2(buf)}", Color.Blue), 1) '
                        ' RuningLog.OutputLogsToTheControl(m_Control, New RuningLogConfig($"RX:{DataProcessing.ByteToString2(buff)}", Color.Blue), 1)
                        'Console.WriteLine($"RX():{len} ")
                        For i = 0 To len - 1
                            ' Console.WriteLine($"RX:{len - 1 - i}< {m_ReceiveQueue.Count }")
                            If (len - 1 - i) < m_ReceiveQueue.Count Then
                                item = m_ReceiveQueue(len - 1 - i)


                                'timep = (Now - item.CmdSendTime)
                                ''判断本节点接收时间是否超时
                                'If item.CmdReceiveStatus OrElse timep.TotalSeconds > item.CmdReceiveTimeout Then
                                '    Console.WriteLine($"item.CmdReceiveStatus:{item.CmdReceiveStatus}, timep.TotalSeconds > item.CmdReceiveTimeout :{ timep.TotalSeconds}>{item.CmdReceiveTimeout}")
                                '    item.setCmdReceiveStatus(True)
                                '    Continue For
                                'End If
                                '按照对应协议解析 并设置到本节点中
                                item.Protocol.ParseReceiveData(item, buf)
                            End If
                        Next
                    End If
            End Select
            isListen = False
        Catch ex As Exception
            isListen = False
            'AppendTipText($"串口接收数据失败,原因:{ex.Message}", Color.Red)
            RuningLog.OutputLogsToTheControl(m_Control, New RuningLogConfig($"串口接收数据失败,原因:{ex.Message}", Color.Red), 1)
        End Try


    End Sub
    Delegate Sub delmCommunicationFlowAddQueue(c As Control, rl As RuningLogConfig, lev As Integer)
    Public Sub mCommunicationFlowAddQueue(c As Control, rl As RuningLogConfig, Optional lev As Integer = 0)
        If c.InvokeRequired Then
            Dim d As New delmCommunicationFlowAddQueue(AddressOf mCommunicationFlowAddQueue)
            c.Invoke(d, c, rl, lev)
        Else
            RuningLog.OutputLogsToTheControl(c, rl, lev)
        End If
    End Sub


    Public Function ExtractSearchReply(bufarry As Byte(), item As Cmdconfig) As Integer

        If bufarry.Length - recvBufferliindex < 12 Then
            RuningLog.OutputLogsToTheControl(m_Control, New RuningLogConfig($"接收数据长度异常！", Color.Red), 1)
            Return recvBufferliindex
        End If
        Dim len As Integer
        Dim result As Integer = recvBufferliindex
        'Dim buff(44) As Bytei+
        '遍历找到sn\
        ' Console.WriteLine($"ExtractSearchReply:{recvBufferliindex} {vbCrLf}RX:{DataProcessing.ByteToString2(bufarry)}")
        AddQueue(m_Control, New RuningLogConfig($"RX1:", Color.Blue), 1)
        For i = recvBufferliindex To bufarry.Length - 1
            If (bufarry(i) And &HF) = (item.CmdSendData(1) And &HF) Then '
                If i + 6 < bufarry.Length Then
                    If bufarry(i + 2) = item.CmdSendData(0) AndAlso bufarry(i + 6) = item.CmdSendData(7) Then
                        len = bufarry(i + 3) + bufarry(i + 4) * 256
                        Dim buff(len - 1) As Byte
                        If i > 0 Then

                            If i + len - 2 < bufarry.Length Then
                                Try
                                    Array.Copy(bufarry, i - 1, buff, 0, len)
                                    i = i + len - 2

                                    item.Protocol.ParseReceiveData(item, buff)
                                    result = i + 1
                                    AddQueue(m_Control, New RuningLogConfig($"RX2:", Color.Blue), 1) '{DataProcessing.ByteToString2(buff)}
                                    ' RuningLog.OutputLogsToTheControl(m_Control, New RuningLogConfig($"RX:{DataProcessing.ByteToString2(buff)}", Color.Blue), 1)
                                Catch ex As Exception
                                    MsgBox(ex.Message)
                                End Try

                            End If

                        Else

                        End If
                    End If
                End If

            End If
        Next
        Console.WriteLine($"ExtractSearchReply------end:{result}")
        Return result
    End Function

    '设置波特率
    Public Sub AnalyzeData()

    End Sub

    '判断串口是否有数据未接收
    Public Function IsSerialData() As Boolean
        '  If IsNothing(m_Serial) Then Return False
        Return m_Serial.BytesToRead > 0
    End Function

    Public Function SetBaudRate(baudRate As Integer, Optional devnumber As Integer = 1) As Boolean
        If IsNothing(m_Serial) Then Return False
        Dim ret As Boolean = m_Serial.IsOpen
        'Dim buff As Byte() = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25}
        If m_Serial.IsOpen Then
            'Thread.Sleep(300 * devnumber)
            isread = True
            While isListen
                Application.DoEvents()
                Thread.Sleep(10)
            End While
            Thread.Sleep(10)
            'm_Serial.Write(buff, 0, buff.Length)
            m_Serial.Close()

        End If

        m_Serial.BaudRate = baudRate

        If ret Then
                isread = False
            m_Serial.Open()
            'm_Serial.Write(buff, 0, buff.Length)
        End If

            Return True
    End Function
    '打开串口
    Public Function OpenSerial() As Boolean
        If IsNothing(m_Serial) Then Return False
        If Not m_Serial.IsOpen Then
            isread = False
            m_Serial.Open()
        End If
    End Function
    '关闭串口连接
    Public Sub CloseSerial()
        If IsNothing(m_Serial) Then Return
        If m_Serial.IsOpen Then
            isread = True
            While isListen
                Application.DoEvents()
                Thread.Sleep(10)
            End While
            Thread.Sleep(10)
            m_Serial.Close()

        End If
        Timer1.Stop()
    End Sub
    '获取串口接收数据
    Public Overrides Function ReceiveData(g_comfig As Cmdconfig) As Cmdconfig


    End Function

    '发送数据
    Public Overrides Sub SendData(data As Cmdconfig)
        data.CmdSendTime = DateTime.Now
        If IsNothing(m_Serial) OrElse Not m_Serial.IsOpen Then
            RuningLog.OutputLogsToTheControl(m_Control, New RuningLogConfig($"发送器未连接!", Color.Red), 1)
            Return
        End If
        'Dim Rl As New RuningLogConfig($"TX:", Color.DarkGreen)
        'Rl.strdata = data.CmdSendData
        ' RuningLog.OutputLogsToTheControl(m_Control, Rl, 1)
        'AddQueue(m_Control, New RuningLogConfig($"TX:", Color.DarkGreen), 1)

        '清空发送缓存
        'm_Serial.DiscardOutBuffer()

        Try
            m_Serial.Write(data.CmdSendData, 0, data.CmdSendData.Length)
            'Dim buff As Byte() = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25}
            'm_Serial.Write(buff, 0, buff.Length)
        Catch ex As Exception
            Console.WriteLine(ex.Message)
        End Try


    End Sub

    Public Overrides Function OpenTransmitter() As Boolean
        If IsNothing(m_Serial) Then
            Return CreateSerial(m_SerialComfig)
        End If
        If Not m_Serial.IsOpen Then
            Try
                isread = False
                m_Serial.Open()
            Catch ex As Exception
                Return False
            End Try

        End If
        Return True
    End Function
    Public isread As Boolean = False
    Public isListen As Boolean = False
    Public Overrides Sub CloseTransmitter()
        If IsNothing(m_Serial) OrElse Not m_Serial.IsOpen Then Return
        isread = True
        While isListen
            Application.DoEvents()
            Thread.Sleep(10)
        End While

        Thread.Sleep(10)
        m_Serial.Close()
        m_Serial = Nothing
    End Sub

    Public Overrides Function IsTransmitter() As Boolean
        If IsNothing(m_Serial) Then
            Return False
        Else
            Return True
        End If
    End Function

    Public Overrides Function GetTransmitterStatus() As Boolean
        If IsNothing(m_Serial) Then Return False
        Return m_Serial.IsOpen
    End Function

    '
    Public Overrides Function SetTransmitterParameter(ParamArray params() As Object) As Boolean
        If IsNothing(m_SerialComfig) Then Return False
        'If m_Serial.IsOpen Then m_Serial.Close()
        If params.Length > 0 Then
            If TypeOf params(0) Is String Then
                m_SerialComfig.PortName = params(0)
            End If
        End If
        If params.Length > 1 Then
            If TypeOf params(1) Is Integer Then
                m_SerialComfig.BaudRate = params(1)
            End If
        End If
        If params.Length > 2 Then
            If TypeOf params(2) Is Integer Then
                m_SerialComfig.DataBits = params(2)
            End If
        End If
        If params.Length > 3 Then
            If TypeOf params(3) Is Parity Then
                m_SerialComfig.Parity = params(3)
            End If
        End If
        If params.Length > 4 Then
            If TypeOf params(4) Is StopBits Then
                m_SerialComfig.StopBits = params(4)
            End If
        End If
        If params.Length > 5 Then
            If TypeOf params(5) Is Handshake Then
                m_SerialComfig.Handshake = params(5)
            End If
        End If
        Return CreateSerial(m_SerialComfig)
    End Function

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

    Public Overrides Function SendData(data() As Byte) As Boolean
        'data.CmdSendTime = DateTime.Now
        If IsNothing(m_Serial) OrElse Not m_Serial.IsOpen Then
            RuningLog.OutputLogsToTheControl(m_Control, New RuningLogConfig($"发送器未连接!", Color.Red), 1)
            Return False
        End If
        'Dim Rl As New RuningLogConfig($"TX:", Color.DarkGreen)
        'Rl.strdata = data.CmdSendData
        ' RuningLog.OutputLogsToTheControl(m_Control, Rl, 1)
        'AddQueue(m_Control, New RuningLogConfig($"TX:", Color.DarkGreen), 1)

        '清空发送缓存
        'm_Serial.DiscardOutBuffer()

        Try
            m_Serial.Write(data, 0, data.Length)
            'Dim buff As Byte() = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25}
            'm_Serial.Write(buff, 0, buff.Length)
        Catch ex As Exception
            Console.WriteLine(ex.Message)
            Return False
        End Try
        Return True
    End Function


End Class

Public Class SerialComfig
    '串口名称
    Public PortName As String
    '波特率
    Public BaudRate As Integer
    '数据位
    Public DataBits As Integer
    '校验位
    Public Parity As Parity
    '停止位
    Public StopBits As StopBits
    '流控制
    Public Handshake As Handshake

    '获取 串口名称列表
    Sub New()
        PortName = GetPortNames()(0)
        BaudRate = GetBaudRates()(0)
        DataBits = 8
        Parity = Parity.None
        StopBits = StopBits.One
        Handshake = Handshake.None

    End Sub
    Public Shared Function GetPortNames() As String()
        Return SerialPort.GetPortNames()
    End Function
    '获取波特率列表
    Public Shared Function GetBaudRates() As Integer()
        Return New Integer() {300, 600, 1200, 2400, 4800, 9600, 14400, 19200, 38400, 57600, 115200, 128000, 256000, 512000}
    End Function
End Class