Imports System.Threading
Imports UTS_Core.UTSModule.Test.StatusMonitor.TestStatusMonitor
Imports UTS_Core.UTSModule.Test.StatusMonitor.ComportStatusMonitor
Imports UTS_Core.UTSModule.Test.StatusMonitor.UtsKeyValueMonitor
Imports UTS_Core.UTSModule.Test.StatusMonitor.TestCommandStatusMonitor
Imports UTS_Core.DebugLog

Namespace UTSModule.Test
    Public Class UtsComportTask

#Region "测试命令交互模块"
        Private Shared _sendErrorCount As Integer

        Public Shared SendErrorMaxCount As Integer


        ''' <summary>
        ''' 测试命令接收等待最长时间
        ''' </summary>
        Public Shared TestWaitReplyMaxTime As Integer = 300

        ''' <summary>
        ''' 期望回复的测试命令。发送测试命令后，判断串口接收是否为指定命令的回复数据
        ''' </summary>
        Public Shared ExpectReplyTestCommand As String = ""


        ''' <summary>
        ''' 需要发送的测试命令
        ''' </summary>
        Public Shared TestSendData As UtsComPortData


        ''' <summary>
        ''' 接收测试命令
        ''' </summary>
        Public Shared TestReceiveData As UtsComPortData

        ''' <summary>
        ''' 测试命令仅发送,不需要接受
        ''' </summary>
        Private Shared TestOnlySend As Boolean

        ''' <summary>
        ''' 提供发送测试命令统一接口
        ''' </summary>
        Public Shared Function SendTestCommand(sendData As UtsComPortData, Optional onlySend As Boolean = False) As TestCommandStatusEnum
            '多线程,等待上一个线程完成收发

            If ComportStatus <> ComPortConnectStatusEnum.Connected Then '串口未连接
                TestCommandStatus = TestCommandStatusEnum.SendingFailure
                Return TestCommandStatus
            End If

            TestOnlySend = onlySend
            TestSendData = sendData
            TestReceiveData = Nothing '清空接收数据
            TestCommandStatus = TestCommandStatusEnum.WaitingToSend

            While TestCommandStatus = TestCommandStatusEnum.WaitingToSend

                If ComportStatus <> ComPortConnectStatusEnum.Connected Then
                    TestCommandStatus = TestCommandStatusEnum.SendingFailure
                    Exit While
                End If

                Thread.Sleep(5)
            End While

            Return TestCommandStatus
        End Function


        ''' <summary>
        ''' 提供测试命令接收完成统一接口
        ''' </summary>
        ''' <param name="timeout">超时接收时间</param>
        ''' <param name="receiveData">接收到的数据</param>
        ''' <returns></returns>
        Public Shared Function ReceivedTestCommandReturn(timeout As Integer, ByRef receiveData As UtsComPortData) As TestCommandStatusEnum
            Dim watch As New Stopwatch
            watch.Start()

            TestWaitReplyMaxTime = timeout '更新回复超时时间
            timeout += 500 '由于测试执行与任务执行有差异,超时时间增加一定时间等待串口任务返回结果
            Dim result As TestCommandStatusEnum
            While TestCommandStatus = TestCommandStatusEnum.WaitingToReceive AndAlso watch.ElapsedMilliseconds <= timeout
                Thread.Sleep(5)
            End While
            watch.Stop()
            result = TestCommandStatus
            receiveData = TestReceiveData
            TestCommandStatus = TestCommandStatusEnum.None

            Return result
        End Function

#End Region


#Region "任务内部状态机模块"
        ''' <summary>
        ''' 串口状态枚举
        ''' </summary>
        Enum ComportStatusEnum
            ''' <summary>
            ''' 未开启或已退出
            ''' </summary>
            Quit

            ''' <summary>
            ''' 入口
            ''' </summary>
            Idle

            ''' <summary>
            ''' 获取串口设备
            ''' </summary>
            GetComport

            ''' <summary>
            ''' 发送同步命令
            ''' </summary>
            SendHeartBeat

            ''' <summary>
            ''' 等待回复
            ''' </summary>
            WaitReply

            ''' <summary>
            ''' 键值回复
            ''' </summary>
            ReplyKeyStatusAck

            ''' <summary>
            ''' 发送测试命令
            ''' </summary>
            SendCommand

            ''' <summary>
            ''' 连接成功
            ''' </summary>
            ComportConnected

            ''' <summary>
            ''' 连接失败
            ''' </summary>
            ComportDisconnected
        End Enum


        ''' <summary>
        ''' 串口任务状态,根据状态不同进行不同的串口操作
        ''' </summary>
        Public Shared ComportTaskStatus As ComportStatusEnum



        ''' <summary>
        ''' 发送同步命令的时间
        ''' </summary>
        Public Shared SendSyncTime As DateTime

        ''' <summary>
        ''' 已发送通讯数据,等待回复的时间
        ''' </summary>
        Public Shared WaitReplyStartTime As DateTime


        ''' <summary>
        ''' 发送同步命令的间隔时间(ms)
        ''' </summary>
        Public Shared SendSyncInterval As Integer


        ''' <summary>
        ''' 测试状态是否发生变化
        ''' </summary>
        Public Shared TestStatusChanged As Boolean

        ''' <summary>
        ''' 是否收到按键主动数据，需要回复ACK
        ''' </summary>
        Public Shared WaitReplyKeyStatusAck As Boolean

        ''' <summary>
        ''' 状态机执行间隔，默认10ms
        ''' </summary>
        Public Shared StateMachineInterval As Integer

        ''' <summary>
        ''' 当前可以访问的串口名集合
        ''' </summary>
        Private Shared ReadOnly ComportNames As New Queue(Of String)


        ''' <summary>
        ''' 当前串口对象
        ''' </summary>
        Public Shared Conn As New UtsComPort


        ''' <summary>
        ''' UTS串口波特率
        ''' </summary>
        Public Shared ComportBaudRate As Integer


        ''' <summary>
        ''' 接收等待最长时间
        ''' </summary>
        Public Shared NormalWaitReplyMaxTime As Integer


        ''' <summary>
        ''' 串口通讯写入失败计数
        ''' </summary>
        Public Shared ComportWriteFailCount As Integer


        ''' <summary>
        ''' 串口通讯写入失败最大上限
        ''' </summary>
        Public Shared ComportWriteMaxFailCount As Integer

        Public Sub New()

        End Sub


        ''' <summary>
        ''' 处理高优先级接收数据
        ''' 当前包含按键键值与测试状态回复包等数据
        ''' </summary>
        Private Shared Sub DealPriorityData()
            If Conn.CacheData.Count = 0 Then Return

            For i As Integer = 0 To Conn.CacheData.Count - 1
                Dim receivedData As UtsComPortData = Conn.CacheData.Peek()

                If String.Compare(receivedData.ReceiverAddress, DeviceAddress.LocalAddress(), True) <> 0 Then
                    Conn.CacheData.Dequeue() '移除头部
                    Continue For
                End If

                '处理自发上报通讯数据
                If String.Compare(receivedData.Command, "KEY_EVENT", True) = 0 Then '按键按下
                    Dim tmp As UtsKeyValueEnum = UtsKeyValueEnum.None
                    If [Enum].TryParse(receivedData.Params(0), tmp) Then
                        UtsKeyValue = tmp
                        WaitReplyKeyStatusAck = True '需要回复键值包ACK
                    End If
                    Conn.CacheData.Dequeue() '移除头部
                Else
                    If ComportTaskStatus = ComportStatusEnum.WaitReply Then
                        Continue For
                    Else
                        Conn.CacheData.Dequeue() '移除头部
                    End If
                End If
            Next
        End Sub

        ''' <summary>
        ''' UTS串口任务状态机
        ''' </summary>
        Private Shared Sub UtsComportTask()
            DealPriorityData() '处理非测试命令数据

            Select Case ComportTaskStatus
                Case ComportStatusEnum.Idle  '空闲状态
                    Idle()
                Case ComportStatusEnum.GetComport '获取串口
                    Thread.Sleep(200) '避免串口打开操作过于频繁
                    GetComport()
                Case ComportStatusEnum.ComportConnected '串口连接成功
                    ComportConnected()
                Case ComportStatusEnum.ComportDisconnected '串口断开连接
                    ComportDisconnected()
                Case ComportStatusEnum.SendHeartBeat '发送同步状态包
                    SendHeartBeat()
                Case ComportStatusEnum.ReplyKeyStatusAck '回复按键状态
                    ReplyKeyStatusAck()
                Case ComportStatusEnum.SendCommand '发送测试命令
                    SendCommand()
                Case ComportStatusEnum.WaitReply '等待回复
                    WaitReply()
                Case ComportStatusEnum.Quit '退出串口任务
                    'todo：完善串口退出逻辑
                Case Else
                    OtherComportStatus()
            End Select
        End Sub

        Private Shared Sub Idle()
            If ComportStatus = ComPortConnectStatusEnum.Connected Then
                '有测试命令需要下发
                If TestCommandStatus = TestCommandStatusEnum.WaitingToSend Then
                    ComportTaskStatus = ComportStatusEnum.SendCommand
                    Return
                End If

                '收到按键状态命令，需要回复ACK
                If WaitReplyKeyStatusAck Then
                    WaitReplyKeyStatusAck = False
                    ComportTaskStatus = ComportStatusEnum.ReplyKeyStatusAck
                    Return
                End If

                '测试状态变化，需要发送状态变化指令
                If TestStatusChanged Then
                    TestStatusChanged = False
                    ComportTaskStatus = ComportStatusEnum.SendHeartBeat
                    Return
                End If

                '定期发送Sync命令
                Static span As TimeSpan
                span = Now - SendSyncTime
                If span.TotalMilliseconds >= SendSyncInterval Then
                    ComportTaskStatus = ComportStatusEnum.SendHeartBeat '发送同步命令
                    Return
                End If
            Else
                ComportTaskStatus = ComportStatusEnum.GetComport '获取设备
            End If
        End Sub

        Private Shared Sub GetComport()
            ComportStatus = ComPortConnectStatusEnum.Connecting

            If ComportNames.Count > 0 Then
                Dim comportName As String = ComportNames.Dequeue()

                If Conn.OpenSerialPort(comportName, ComportBaudRate) = False Then Return

                '发送询问包
                Dim sendData As New UtsComPortData(DeviceAddress.UtsHw03Address, "*IDN?")
                If Conn.SendTextAppendCrLf(sendData) Then
                    _sendErrorCount = 0
                    WaitReplyStartTime = Now
                    ComportTaskStatus = ComportStatusEnum.WaitReply
                Else
                    ComportTaskStatus = ComportStatusEnum.Idle
                End If
            Else
                For Each comportName As String In UtsComPort.GetPortNames()
                    ComportNames.Enqueue(comportName) '添加可用串口列表
                Next
                ComportTaskStatus = ComportStatusEnum.Idle
            End If
        End Sub

        Private Shared Sub ComportConnected()
            ComportStatus = ComPortConnectStatusEnum.Connected
            ComportTaskStatus = ComportStatusEnum.Idle
        End Sub

        Private Shared Sub ComportDisconnected()
            ComportStatus = ComPortConnectStatusEnum.UnConnected
            ComportTaskStatus = ComportStatusEnum.Idle
            UtsTester.CreateTester.RerunSetupModule()
        End Sub

        Private Shared Sub SendHeartBeat()
            Dim str As String = Choose(TestStatus + 1, "IDEL", "TESTING", "PASS", "FAIL", "DevError").ToString()
            Dim sendData As New UtsComPortData(DeviceAddress.UtsHw03Address, "TEST_STATUS", str)

            If Conn.SendTextAppendCrLf(sendData) Then
                _sendErrorCount = 0
                SendSyncTime = Now '有数据通讯,发送同步命令时间更新

                WaitReplyStartTime = Now
                ComportTaskStatus = ComportStatusEnum.WaitReply
            Else
                _sendErrorCount += 1
                If _sendErrorCount >= SendErrorMaxCount Then
                    _sendErrorCount = 0
                    ComportTaskStatus = ComportStatusEnum.ComportDisconnected
                Else
                    ComportTaskStatus = ComportStatusEnum.Idle
                End If
            End If
        End Sub

        Private Shared Sub ReplyKeyStatusAck()
            Dim sendData As New UtsComPortData(DeviceAddress.UtsHw03Address, "KEY_EVENT", "TRUE")

            If Conn.SendTextAppendCrLf(sendData) Then
                _sendErrorCount = 0
                ComportTaskStatus = ComportStatusEnum.Idle
            Else
                _sendErrorCount += 1
                If _sendErrorCount >= SendErrorMaxCount Then
                    _sendErrorCount = 0
                    ComportTaskStatus = ComportStatusEnum.ComportDisconnected
                Else
                    ComportTaskStatus = ComportStatusEnum.Idle
                End If
            End If
        End Sub

        Private Shared Sub SendCommand()
            Dim sendData As UtsComPortData = TestSendData
            If sendData Is Nothing Then
                ComportTaskStatus = ComportStatusEnum.Idle
                Return
            End If

            ExpectReplyTestCommand = sendData.Command   '期望回复的命令

            If Conn.SendTextAppendCrLf(sendData) Then
                _sendErrorCount = 0

                If TestOnlySend Then '仅发送数据，不接受回复信息
                    WaitReplyStartTime = Now '更新等待接收的起始时间
                    TestCommandStatus = TestCommandStatusEnum.ReceiveCompleted '接收完成
                    ComportTaskStatus = ComportStatusEnum.Idle
                Else
                    WaitReplyStartTime = Now '更新等待接收的起始时间
                    TestCommandStatus = TestCommandStatusEnum.WaitingToReceive '等待接收
                    ComportTaskStatus = ComportStatusEnum.WaitReply
                End If
            Else
                TestCommandStatus = TestCommandStatusEnum.SendingFailure '发送失败

                _sendErrorCount += 1
                If _sendErrorCount >= SendErrorMaxCount Then
                    _sendErrorCount = 0
                    ComportTaskStatus = ComportStatusEnum.ComportDisconnected
                Else
                    ComportTaskStatus = ComportStatusEnum.Idle
                End If
            End If
        End Sub

        Private Shared Sub WaitReply()
            Dim timeout As Integer
            If TestCommandStatus = TestCommandStatusEnum.WaitingToReceive Then
                timeout = TestWaitReplyMaxTime
            Else
                timeout = NormalWaitReplyMaxTime
            End If

            Dim span As TimeSpan = Now - WaitReplyStartTime
            If span.TotalMilliseconds > timeout Then '超时
                If TestCommandStatus = TestCommandStatusEnum.WaitingToReceive Then
                    TestCommandStatus = TestCommandStatusEnum.ReceiveTimeout '测试命令接收超时
                    ComportTaskStatus = ComportStatusEnum.Idle
                Else
                    If ComportTaskStatus = ComportStatusEnum.ComportConnected Then
                        ComportWriteFailCount += 1
                        If ComportWriteFailCount >= ComportWriteMaxFailCount Then '超过写入失败上限
                            ComportWriteFailCount = 0
                            ComportTaskStatus = ComportStatusEnum.ComportDisconnected
                        Else
                            ComportTaskStatus = ComportStatusEnum.Idle
                        End If
                    Else
                        ComportTaskStatus = ComportStatusEnum.Idle
                    End If
                End If
            Else
                If Conn.CacheData.Count <= 0 Then Return
                Dim receivedData As UtsComPortData = Conn.CacheData.Dequeue() '接收数据

                If receivedData Is Nothing Then
                    ApplicationLog.WriteInfoLog("异常的接收信息进入缓存队列！")
                    Return
                End If

                If String.Compare(receivedData.Command, ExpectReplyTestCommand, True) = 0 Then
                    TestCommandStatus = TestCommandStatusEnum.ReceiveCompleted
                    TestReceiveData = receivedData

                    ComportWriteFailCount = 0
                    ComportTaskStatus = ComportStatusEnum.Idle
                ElseIf String.Compare(receivedData.Command, "TEST_STATUS", True) = 0 Then '测试结果变更回复
                    ComportWriteFailCount = 0
                    ComportTaskStatus = ComportStatusEnum.Idle

                ElseIf String.Compare(receivedData.Command, "*IDN?", True) = 0 Then '测试结果变更回复
                    If receivedData.Params.Count = 0 Then Return
                    If String.Compare(receivedData.Params(0), "uts-hw03-rev10a", True) = 0 Then
                        ComportTaskStatus = ComportStatusEnum.ComportConnected
                    End If
                Else
                    '其他系统命令
                End If
            End If
        End Sub

        Private Shared Sub OtherComportStatus()
            ComportTaskStatus = ComportStatusEnum.Idle
        End Sub


        ''' <summary>
        ''' 串口任务,包含索引串口,发送数据,接收数据
        ''' </summary>
        Public Shared Sub StartTask()
            If ComportTaskStatus <> ComportStatusEnum.Quit Then Return

            '初始化
            SendSyncTime = Now
            SendSyncInterval = 1000
            ComportTaskStatus = ComportStatusEnum.Idle

            ComportBaudRate = 115200
            NormalWaitReplyMaxTime = 300
            ComportWriteMaxFailCount = 2
            _sendErrorCount = 0
            SendErrorMaxCount = 3

            StateMachineInterval = 10

            Try
                '状态机执行
                While ComportTaskStatus <> ComportStatusEnum.Quit
                    UtsComportTask()

                    Thread.Sleep(StateMachineInterval)
                End While
            Catch ex As Exception
                MsgBox($"测试器致命错误,{ex}")
                ApplicationLog.WriteFatalLog($"测试器致命错误,{ex}")
            End Try


            '状态机退出
            Conn.CloseSerialPort()
        End Sub

        Public Shared Sub EndTask()
            ComportTaskStatus = ComportStatusEnum.Quit
        End Sub
#End Region

    End Class
End Namespace