Imports System.Text
Imports System.Threading
Imports UTS_Core.UTSModule.DbConnect

Imports UTS_Core.UTSModule.Station
Imports UTS_Core.UTSModule.Station.StationTestPlan

Imports UTS_Core.UTSModule.Test.Command
Imports UTS_Core.UTSModule.Test.StatusMonitor
Imports UTS_Core.UTSModule.Test.StatusMonitor.TestStatusMonitor

Namespace UTSModule.Test
    Public Class TestNodeCompletedEventArgs
        Inherits EventArgs

        Sub New(node As RowNode, testReturn As TestCommandReturn)
            Me.Node = node
            Me.TestReturn = testReturn
        End Sub

        Public Property Node() As RowNode

        Public Property TestReturn() As TestCommandReturn
    End Class

    Public Class TestNodeResultChangedEventArgs
        Inherits EventArgs

        Sub New(node As RowNode, testReturn As TestCommandReturn)
            Me.Node = node
            Me.TestReturn = testReturn
        End Sub

        Public Property Node() As RowNode

        Public Property TestReturn() As TestCommandReturn
    End Class

    Public Class TestNodeChangedEventArgs
        Inherits EventArgs

        Sub New(node As RowNode)
            Me.Node = node
        End Sub

        Public Property Node() As RowNode

    End Class


    Public Class TestProgressChangedEventArgs
        Inherits EventArgs

        Sub New(process As Double)
            Percent = process
        End Sub

        ''' <summary>
        ''' 当前百分比
        ''' </summary>
        ''' <returns></returns>
        Public Property Percent As Double
    End Class


    Public Class TestCountChangedEventArgs
        Inherits EventArgs

        Sub New(pass As Integer, fail As Integer)
            PassCount = pass
            FailCount = fail
        End Sub

        Public Property PassCount As Integer

        Public Property FailCount As Integer

        Public ReadOnly Property Count() As Integer
            Get
                Return PassCount + FailCount
            End Get
        End Property

    End Class

    Public Class TestTimeEventArgs
        Inherits EventArgs

        Sub New(span As TimeSpan)
            TimeElapsed = span
        End Sub

        Public Property TimeElapsed() As TimeSpan
    End Class

    Public Class TestPauseEventArgs
        Sub New(node As RowNode)
            Me.Node = node
        End Sub

        Public Property Node() As RowNode
    End Class

    Public Class TestFailEventArgs
        Sub New(node As RowNode)
            Me.Node = node
        End Sub

        Public Property Node() As RowNode
    End Class

    Public Class TestEndEventArgs
        Inherits EventArgs

        Sub New(result As TestResult)
            TestResult = result
        End Sub

        Public Property TestResult() As TestResult
    End Class

    Public Class UtsTester
        '''<summary>测试开始事件</summary>
        Public Event TestStart(sender As Object, e As EventArgs)

        '''<summary>测试暂停事件</summary>
        Public Event TestPause(sender As Object, e As TestPauseEventArgs)

        '''<summary>测试成功事件</summary>
        Public Event TestPass(sender As Object, e As EventArgs)

        '''<summary>测试失败事件</summary>
        Public Event TestFail(sender As Object, e As TestFailEventArgs)

        '''<summary>测试结束事件</summary>
        Public Event TestEnd(sender As Object, e As TestEndEventArgs)


        '''<summary>测试状态变更事件</summary>
        Public Event TestStatusChanged(sender As Object, e As TestStatusChangedEventArgs)

        '''<summary>测试计数变更事件</summary>
        Public Event TestCountChanged(sender As Object, e As TestCountChangedEventArgs)

        '''<summary>测试节点变更事件</summary>
        Public Event TestNodeChanged(sender As Object, e As TestNodeChangedEventArgs)

        '''<summary>测试节点完成事件</summary>
        Public Event TestNodeCompleted(sender As Object, e As TestNodeCompletedEventArgs)

        '''<summary>测试节点结果变化事件</summary>
        Public Event TestNodeResultChanged(sender As Object, e As TestNodeResultChangedEventArgs)

        '''<summary>测试时间变更事件</summary>
        Public Event TestTimeChanged(sender As Object, e As TestTimeEventArgs)

        '重试次数变更,加node
        Public Event TestProgressChanged(sender As Object, e As TestProgressChangedEventArgs)

        Public Event RetryProgressChanged(sender As Object, e As TestProgressChangedEventArgs)



        ''' <summary>当前测试节点</summary>
        Private _testNode As RowNode

        ''' <summary> 当前测试固定模块</summary>
        Private _testFixedModule As FixedModuleEnum = FixedModuleEnum.None

        ''' <summary>测试耗时计时器</summary>
        Private ReadOnly _testWatch As New Stopwatch

        ''' <summary>单步执行耗时计时器</summary>
        Private ReadOnly _stepWatch As New Stopwatch

        ''' <summary>程序运行后，测试通过总数</summary>
        Private _testPassCount As Integer

        ''' <summary>程序运行后，测试失败总数</summary>
        Private _testFailCount As Integer

        ''' <summary>是否已执行设置模块</summary>
        Private _setupModuleExecuted As Boolean

        ''' <summary>是否处于调试断点</summary>
        Private _nodePaused As Boolean

        ''' <summary>是否退出调试断点</summary>
        Private _exitPaused As Boolean

        ''' <summary>是否下一节点调试断点</summary>
        Private _nextPause As Boolean

        ''' <summary>内置模块入口集合</summary>
        Private ReadOnly _testModules As Dictionary(Of String, RowNode)

        ''' <summary>自定义模块下函数入口集合</summary>
        Private ReadOnly _customModules As Dictionary(Of String, RowNode)

        ''' <summary>测试结果内容</summary>
        Private ReadOnly _testResult As TestResult

        ''' <summary>是否退出测试</summary>
        Private _exitTest As Boolean

        ''' <summary>测试时间线程</summary>
        Private ReadOnly _timeThread As Thread

        ''' <summary>测试过程中，产生的变量存储</summary>
        Private ReadOnly _userVar As New Dictionary(Of String, String)

        ''' <summary>测试过程中，全局变量存储</summary>
        Private ReadOnly _globalVar As New Dictionary(Of String, String)

        ''' <summary>测试过程中,使用的测试站位</summary>
        Private _processStation As ProcessStation

        ''' <summary>测试器句柄,全局唯一</summary>
        Private Shared _tester As UtsTester

        ''' <summary>初始化测试器线程锁</summary>
        Private Shared ReadOnly InitLock As New Object()

        Private _startTestIndex As Integer
        Private _endTestIndex As Integer
        Private _testCount As Integer




        ''' <summary>
        ''' 创建测试器,若测试器已经创建则返回句柄
        ''' </summary>
        ''' <returns></returns>
        Public Shared Function CreateTester() As UtsTester
            If _tester Is Nothing Then
                SyncLock InitLock
                    Thread.MemoryBarrier()
                    If _tester Is Nothing Then
                        _tester = New UtsTester
                    End If
                End SyncLock
            End If
            Return _tester
        End Function



        Private Sub New()
            _testResult = New TestResult()
            _testResult.AppName = Windows.Forms.Application.ProductName & Windows.Forms.Application.ProductVersion
            _testResult.ServiceID = CInt(UtsRegistry.DataServiceIndex)

            _testModules = New Dictionary(Of String, RowNode)()
            _customModules = New Dictionary(Of String, RowNode)

            _testWatch = New Stopwatch()
            _stepWatch = New Stopwatch()

            _exitTest = False

            TestStatus = TestStatusEnum.WaitForTest

            TestFailMode = TestFailModeEnum.StepFail

            _timeThread = New Thread(AddressOf TestTime)
            _timeThread.IsBackground = True
            _timeThread.Start()
        End Sub


        Public Property DebugMode() As Boolean


        ''' <summary>测试状态</summary>
        Public Property TestStatus As TestStatusEnum
            Get
                Return TestStatusMonitor.TestStatus
            End Get
            Set(value As TestStatusEnum)
                TestStatusMonitor.TestStatus = value
            End Set
        End Property


        Public ReadOnly Property TestResult() As TestResult
            Get
                Return _testResult
            End Get
        End Property

        Public ReadOnly Property TestFiexdModule As FixedModuleEnum
            Get
                Return _testFixedModule
            End Get
        End Property

        ''' <summary>
        ''' 判定测试失败的模式
        ''' </summary>
        ''' <returns></returns>
        Public Property TestFailMode() As TestFailModeEnum

        ''' <summary>
        ''' 测试时,重新执行Setup模块
        ''' </summary>
        Public Sub RerunSetupModule()
            _setupModuleExecuted = False
        End Sub

        Public Property ProcessStation() As ProcessStation
            Get
                Return _processStation
            End Get
            Set(value As ProcessStation)
                '测试站位修改

                _processStation = value

                _setupModuleExecuted = False
                UpdateTestModule(CType(_processStation.Packet.StationPlan, StationTestPlan).RowNodes)

                _testResult.TestPlan = _processStation.Packet.FileName
            End Set
        End Property

        ''' <summary>
        ''' 获取指定名称的模块节点
        ''' </summary>
        ''' <param name="name"></param>
        ''' <returns></returns>
        Public Function GetModule(name As String) As RowNode
            If _customModules.ContainsKey($"{name}") = False Then Return Nothing
            Return _customModules($"{name}")
        End Function


        Public Function GetFixedModule(name As FixedModuleEnum) As RowNode
            If _testModules.ContainsKey($"{name}") = False Then Return Nothing
            Return _testModules($"{name}")
        End Function

        Private Sub TestTime()
            While _testWatch IsNot Nothing
                If _testWatch.IsRunning Then
                    RaiseEvent TestTimeChanged(Me, New TestTimeEventArgs(_testWatch.Elapsed))
                End If
                Thread.Sleep(100)
            End While
        End Sub

        ''' <summary>
        ''' 开始测试,此函数为正式测试调用
        ''' </summary>
        Public Sub StartTest()

            _userVar.Clear() '清空全局变量
            _testResult.ResetTestResult() '重置测试结果

            DebugMode = False
            _exitTest = False

            RaiseEvent TestStart(Me, New EventArgs())

            '重置测试进度
            RaiseEvent TestProgressChanged(Me, New TestProgressChangedEventArgs(0))

            '测试主代码
            TestFunction()

            '进度修改百分百
            RaiseEvent TestProgressChanged(Me, New TestProgressChangedEventArgs(100))

            '测试完成
            RaiseEvent TestEnd(Me, New TestEndEventArgs(_testResult))
        End Sub

        ''' <summary>
        ''' 开始调试测试,此函数为正式测试调用
        ''' </summary>
        Public Sub StartDebugTest()
            DebugMode = True
            _exitTest = False

            _userVar.Clear() '清空全局变量
            _testResult.ResetTestResult() '重置测试结果

            RaiseEvent TestStart(Me, New EventArgs())

            '重置测试进度
            RaiseEvent TestProgressChanged(Me, New TestProgressChangedEventArgs(0))



            '测试主代码
            TestFunction()

            '进度修改百分百
            RaiseEvent TestProgressChanged(Me, New TestProgressChangedEventArgs(100))

            '测试完成
            RaiseEvent TestEnd(Me, New TestEndEventArgs(_testResult))
        End Sub


        ''' <summary>
        ''' 流程修改后保存,更新模块索引
        ''' </summary>
        ''' <param name="sender"></param>
        ''' <param name="e"></param>
        Public Sub StationEditStatusChanged(sender As Object, e As StationEditStatusChangedEventArgs)
            If e.Status = StationEditStatusMonitor.StationEditStatusEnum.Saved Then
                '更新测试流程所有函数入口
                'UpdateTestModule(ProcessStation.Packet.StationPlan.HeadNode.RowNodes)
                UpdateTestModule(CType(_processStation.Packet.StationPlan, StationTestPlan).RowNodes)

                '测试将重新执行设置模块
                _setupModuleExecuted = False
            End If
        End Sub

        ''' <summary>
        ''' 更新测试模块入库
        ''' </summary>
        Public Sub UpdateTestModule(rowNodes As RowNodeCollection)
            _testModules.Clear()

            GetTestModule(_testModules, rowNodes)
        End Sub


        ''' <summary>
        ''' 获取执行行节点集合中测试模块入口，并存储至内部模块入口集合
        ''' </summary>
        ''' <param name="modules">存储测试模块入口集合</param>
        ''' <param name="nodes">行节点集合</param>
        Private Sub GetTestModule(modules As Dictionary(Of String, RowNode), nodes As RowNodeCollection)
            For Each node As RowNode In nodes
                If node.RowType = RowNode.RowTypeEnum.FixedModule OrElse node.RowType = RowNode.RowTypeEnum.Module Then
                    If String.IsNullOrWhiteSpace(node.Label) Then Continue For
                    If modules.ContainsKey(node.Label) Then Continue For
                    modules.Add(node.Label, node)
                End If

                If node.Label = $"{FixedModuleEnum.Custom}" Then
                    _customModules.Clear()
                    GetTestModule(_customModules, node.RowNodes)
                End If
            Next
        End Sub

        Public Function GetCustomModuleName() As String()
            Return _customModules.Keys.ToArray()
        End Function


        ''' <summary>
        ''' 执行内置模块函数
        ''' </summary>
        ''' <param name="moduleName"></param>
        ''' <returns></returns>
        Public Function ExecuteFixedModule(moduleName As FixedModuleEnum) As TestCommandReturn
            Dim result As TestCommandReturn

            _testFixedModule = moduleName
            If _testModules.ContainsKey(moduleName.ToString()) Then
                Dim node As RowNode = _testModules(moduleName.ToString())
                node.IsRetry = False '顶级节点最初重试状态为false
                result = ExecutePlan(node， Nothing)
            Else
                result = New TestCommandReturn()
                result.ExecuteResult = False
                result.ExecuteResultTipString = $"未搜索到[{moduleName}]模块"
            End If

            _testFixedModule = FixedModuleEnum.None '重置测试固定模块
            Return result
        End Function


#Region "获取特殊情况返回"
        ''' <summary>
        ''' 获取用户退出测试的执行返回结果
        ''' </summary>
        ''' <returns></returns>
        Private Function GetExitTestReturn() As TestCommandReturn
            Dim result As New TestCommandReturn

            result.ExecuteResult = False
            result.ExecuteResultTipString = $"用户结束测试过程"

            Return result
        End Function

        ''' <summary>
        ''' 获取不运行的节点的执行返回结果
        ''' </summary>
        ''' <returns></returns>
        Private Function GetNotActionNodeResult() As TestCommandReturn
            Dim result As New TestCommandReturn

            result.ExecuteResult = True
            result.ExecuteResultTipString = $"无需执行的测试节点"

            Return result
        End Function


        ''' <summary>
        ''' 获取未知的行节点类型的执行返回结果
        ''' </summary>
        ''' <param name="node">当前行节点</param>
        ''' <returns></returns>
        Private Function GetUnknownTypeNodeResult(node As RowNode) As TestCommandReturn
            Dim result As New TestCommandReturn

            result.ExecuteResult = False
            result.ExecuteResultTipString = $"未知的节点类型：{node.RowType}"

            Return result
        End Function

        Private Function GetNullControlTypeResult() As TestCommandReturn
            Dim result As New TestCommandReturn

            result.ExecuteResult = True
            result.ExecuteResultTipString = $"无效的控制类型,默认跳过执行"

            Return result
        End Function


        Private Function GetUnknownControlTypeResult(node As RowNode) As TestCommandReturn
            Dim result As New TestCommandReturn

            result.ExecuteResult = False
            result.ExecuteResultTipString = $"未知的控制类型：{node.ControlType}"

            Return result
        End Function
#End Region

#Region "执行流程"
        Public Function ExecutePlan(node As RowNode, Optional localVariable As Dictionary(Of String, String) = Nothing) As TestCommandReturn
            Dim result As TestCommandReturn
            If _exitTest Then Return GetExitTestReturn()

            If node.Action = False Then Return GetNotActionNodeResult()
            If node.RowType = RowNode.RowTypeEnum.Flow Then
                If String.IsNullOrEmpty(node.CommandType) OrElse String.IsNullOrEmpty(node.Command) Then Return GetNotActionNodeResult()
            End If

            If TestStatus = TestStatusEnum.Testing Then _testNode = node

            '节点测试开始
            RaiseEvent TestNodeChanged(Me, New TestNodeChangedEventArgs(node))

            '调试模式检测断点
            If DebugMode Then
                '断点调试
                While node.Pause OrElse _nextPause
                    '已进入暂停状态
                    If _nodePaused = False Then
                        _nodePaused = True
                        RaiseEvent TestPause(Me, New TestPauseEventArgs(node))
                    End If

                    If _exitPaused Then Exit While
                    If _exitTest Then Return GetExitTestReturn()

                    Thread.Sleep(100)
                End While
                _nodePaused = False
                _exitPaused = False
            End If

            '获取测试结果
            Select Case node.RowType
                Case RowNode.RowTypeEnum.FixedModule, RowNode.RowTypeEnum.Module
                    result = ExecuteModuleNode(node, localVariable)
                Case RowNode.RowTypeEnum.Control
                    result = ExecuteControlNode(node, localVariable)
                Case RowNode.RowTypeEnum.Flow
                    result = ExecuteFlowNode(node, localVariable)
                Case Else
                    result = GetUnknownTypeNodeResult(node)
            End Select

            node.TestReturn = result

            '节点测试完成
            RaiseEvent TestNodeCompleted(Me, New TestNodeCompletedEventArgs(node, result))

            Return result
        End Function


        ''' <summary>
        ''' 执行模块流程
        ''' </summary>
        ''' <param name="node"></param>
        ''' <returns></returns>
        Public Function ExecuteModuleNode(node As RowNode, Optional localVariable As Dictionary(Of String, String) = Nothing) As TestCommandReturn
            Dim result As New TestCommandReturn

            For retry As Integer = 0 To node.Retry '最大执行总数为：1+重试次数
                '退出执行检测
                If _exitTest Then Return GetExitTestReturn()

                '更新重试进度
                Dim p As Double = CDbl(IIf(retry = 0, 100, (node.Retry - retry) / node.Retry * 100))
                RaiseEvent RetryProgressChanged(Me, New TestProgressChangedEventArgs(p))

                '判断节点是否为重试状态
                If retry > 0 Then node.IsRetry = True

                If localVariable IsNot Nothing AndAlso localVariable.ContainsKey("CH") Then
                    Console.WriteLine($"CH:{localVariable("CH")} Label:{node.Label} Desc:{node.Description} RetryCount:{retry} IsRetry:{node.IsRetry}")
                End If

                '重置模块测试结果
                result.ExecuteResult = True

                '测试子节点
                For Each rowNode As RowNode In node.RowNodes
                    If _exitTest Then Return GetExitTestReturn() '退出执行检测

                    rowNode.IsRetry = node.IsRetry '将父节点的重试状态赋予子节点
                    If ExecutePlan(rowNode, localVariable).ExecuteResult = False Then result.ExecuteResult = False '节点测试失败则认为模块失败
                    If result.ExecuteResult Then Continue For '执行成功则执行下一流程
                    If TestFailMode = TestFailModeEnum.StepFail Then Exit For '单步失败模式下退出测试，不再执行
                Next

                '模块测试成功则退出重试
                If result.ExecuteResult Then Exit For
                Thread.Sleep(node.RetryInterval) '执行失败,休眠重试间隔
            Next

            node.IsRetry = True 'todo:临时解决call命令调用函数时多层级模块 节点变更与节点完成的非唯一节点bug

            Return result
        End Function


        ''' <summary>
        ''' 执行控制流程
        ''' </summary>
        ''' <param name="node"></param>
        ''' <returns>测试结果</returns>
        Private Function ExecuteControlNode(node As RowNode, Optional localVariable As Dictionary(Of String, String) = Nothing) As TestCommandReturn
            If _exitTest Then Return GetExitTestReturn() '退出测试
            If String.IsNullOrWhiteSpace(node.ControlType) Then Return GetNullControlTypeResult()

            Dim result As New TestCommandReturn
            Select Case node.ControlType
                Case "If"
                    result.ExecuteResult = ExecuteIfControlNode(node).ExecuteResult
                Case "While"
                    result.ExecuteResult = ExecuteWhileControlNode(node).ExecuteResult
                Case Else
                    result = GetUnknownControlTypeResult(node)
            End Select

            Return result
        End Function

        Private Function ExecuteIfControlNode(node As RowNode, Optional localVariable As Dictionary(Of String, String) = Nothing) As TestCommandReturn
            '无子节点,默认通过
            '判断成功,无Then节点，默认成功
            '判断失败,无Else节点默认成功
            '控制流程只有子节点失败才算失败，判断条件失败，忽略失败

            Dim result As New TestCommandReturn

            Dim tmpResult As TestCommandReturn 'if分支的判断结果
            tmpResult = ExecuteControlCondition(node, localVariable) '获取If判断条件的结果
            For Each rowNode As RowNode In node.RowNodes
                If tmpResult.ExecuteResult Then
                    If rowNode.ControlType = "Then" Then

                        result = ExecuteModuleNode(rowNode, localVariable)
                        Exit For
                    End If
                Else
                    Select Case rowNode.ControlType
                        Case "ElseIf" '执行判断条件命令成功则认为匹配成功，返回执行子节点的结果

                            tmpResult = ExecuteControlCondition(rowNode, localVariable)
                            '节点测试完成
                            RaiseEvent TestNodeCompleted(Me, New TestNodeCompletedEventArgs(rowNode, tmpResult))
                            If tmpResult.ExecuteResult Then '判断条件成立
                                result = ExecuteModuleNode(rowNode, localVariable)
                                Exit For
                            End If

                        Case "Else"
                            result = ExecuteModuleNode(rowNode, localVariable)
                            Exit For
                    End Select
                End If
            Next

            Return result
        End Function


        Private Function ExecuteWhileControlNode(node As RowNode, Optional localVariable As Dictionary(Of String, String) = Nothing) As TestCommandReturn
            Dim result As TestCommandReturn
            result = ExecuteControlCondition(node, localVariable) '执行while节点判断条件
            If result.ExecuteResult Then
                result = ExecuteModuleNode(node, localVariable) '执行While内部流程

                '内部循环节点成功执行成功则递归调用本身,直至while条件不成立
                If result.ExecuteResult Then result = ExecuteWhileControlNode(node)
            Else

                '执行判断条件失败则退出While,并认为执行成功
                result.ExecuteResult = True
            End If

            Return result
        End Function

        ''' <summary>
        ''' 执行控制结构的条件部分
        ''' </summary>
        ''' <param name="node"></param>
        ''' <returns></returns>
        Private Function ExecuteControlCondition(node As RowNode, Optional localVariable As Dictionary(Of String, String) = Nothing) As TestCommandReturn
            Dim result As New TestCommandReturn
            If String.IsNullOrWhiteSpace(node.Command) Then Return result


            Dim cmd As New TestCommand(node) '获取执行参数
            ReplaceValue(cmd)  '执行前变量替换
            Dim executor As TestCommandExecutor = TestCommandManger.CreateExecutor(cmd) '创建执行器
            result = executor.Execute() '获取执行返回


            If String.IsNullOrWhiteSpace(node.RecordName) = False Then '处理需要入库的数据
                Dim filedName As String = ReplaceString(node.RecordName, localVariable) '替换记录名称中变量
                If _userVar.ContainsKey(filedName) Then
                    _userVar(filedName) = result.RecordValue
                Else
                    _userVar.Add(filedName, result.RecordValue)
                End If

                If node.SaveToDb Then
                    '添加内容至数据库字段中
                    _testResult.AddCustomRecord(filedName, result.RecordValue)
                End If
            End If

            Return result
        End Function

        ''' <summary>
        ''' 执行类型为普通流程的行节点
        ''' </summary>
        ''' <param name="node"></param>
        ''' <param name="localVariable"></param>
        ''' <returns></returns>
        Public Function ExecuteFlowNode(node As RowNode, Optional localVariable As Dictionary(Of String, String) = Nothing) As TestCommandReturn
            Dim result As New TestCommandReturn
            If String.IsNullOrWhiteSpace(node.Command) Then Return result

            Dim watch As Stopwatch = Stopwatch.StartNew() '开启单步计时
            Dim cmd As New TestCommand(node) '获取执行参数
            ReplaceValue(cmd, localVariable)  '执行前变量替换
            Dim executor As TestCommandExecutor = TestCommandManger.CreateExecutor(cmd) '创建执行器

            node.Desc4Record = ReplaceString(node.Description, localVariable) '替换记录名称中变量

            '最大执行总数为：1+重试次数
            For retry As Integer = 0 To node.Retry
                If _exitTest Then Exit For

                '更新重试进度
                Dim p As Double = CDbl(IIf(retry = 0, 100, (node.Retry - retry) / node.Retry * 100))
                RaiseEvent RetryProgressChanged(Me, New TestProgressChangedEventArgs(p))

                '更新命令是否处于重试状态
                If retry > 0 Then node.IsRetry = True
                executor.IsRetry = node.IsRetry

                '执行成功一次则判定成功,退出执行
                result = executor.Execute()
                RaiseEvent TestNodeResultChanged(Me, New TestNodeResultChangedEventArgs(node, result))
                If result.ExecuteResult Then Exit For

                '执行失败,休眠重试间隔
                Thread.Sleep(node.RetryInterval)
            Next

            watch.Stop()
            result.StepTimeSpan = watch.Elapsed

            '进度修改
            If node.RowListIndex > _startTestIndex AndAlso node.RowListIndex < _endTestIndex Then
                Dim p As Double = (node.RowListIndex - _startTestIndex) / _testCount * 100
                RaiseEvent TestProgressChanged(Me, New TestProgressChangedEventArgs(p))
            End If

            'todo:节点测试数据,考虑是否与节点测试完成事件合并

            If String.IsNullOrWhiteSpace(node.RecordName) = False Then '处理需要入库的数据
                Dim filedName As String = ReplaceString(node.RecordName, localVariable) '替换记录名称中变量
                If _userVar.ContainsKey(filedName) Then
                    _userVar(filedName) = result.RecordValue
                Else
                    _userVar.Add(filedName, result.RecordValue)
                End If

                If node.SaveToDb Then '添加内容至数据库字段中
                    _testResult.AddCustomRecord(filedName, result.RecordValue)
                End If
            End If

            Return result
        End Function

        ''' <summary>
        ''' 获取指定变量名的变量值
        ''' </summary>
        ''' <param name="varName">变量名</param>
        ''' <param name="localVariable">调用模块时传入的局部变量</param>
        ''' <returns></returns>
        Private Function ReplaceVar(varName As String, Optional localVariable As Dictionary(Of String, String) = Nothing) As String
            '替换变量的逻辑顺序
            '优先替换局部变量，也就是传进模块的变量
            '其次使用用户变量，也就是记录入库的变量
            '最后匹配系统变量，也就是预设带有特殊意义的字符串
            If localVariable IsNot Nothing AndAlso localVariable.ContainsKey(varName) Then
                Return localVariable(varName)
            Else
                If _userVar.ContainsKey(varName) Then
                    Return _userVar(varName)
                Else
                    Select Case varName
                        Case "ResourceDir"
                            Return UtsPath.StationPacketResourceDirPath
                        Case "StartDir"
                            Return UtsPath.SystemDirPath
                        Case Else
                            Return String.Empty '返回空字符
                    End Select
                End If
            End If
        End Function

        ''' <summary>
        ''' 替换字符串中使用的变量，返回替换后的字符串
        ''' </summary>
        ''' <param name="sourceString">原字符串</param>
        ''' <param name="localVariable">调用模块时传入的局部变量</param>
        ''' <returns>替换后的字符串</returns>
        Private Function ReplaceString(sourceString As String, Optional localVariable As Dictionary(Of String, String) = Nothing) As String
            Dim strBuilder As New StringBuilder
            Dim varName As New StringBuilder
            Dim findVar As Boolean

            For Each c As Char In sourceString
                If c = "{"c Then
                    If findVar Then
                        findVar = False
                        strBuilder.Append("{")
                    Else
                        findVar = True
                    End If
                ElseIf c = "}"c Then
                    If findVar Then
                        strBuilder.Append(ReplaceVar(varName.ToString()， localVariable))
                        varName.Clear()
                        findVar = False
                    Else
                        strBuilder.Append("}")
                    End If
                Else
                    If findVar Then
                        varName.Append(c)
                    Else
                        strBuilder.Append(c)
                    End If
                End If
            Next

            Return strBuilder.ToString
        End Function

        ''' <summary>
        ''' 替换测试命令参数中的变量(变量格式：{var}),待删除
        ''' </summary>
        ''' <param name="cmd"></param>
        ''' <returns></returns>
        Private Function ReplaceValue(cmd As TestCommand, Optional localVariable As Dictionary(Of String, String) = Nothing) As TestCommand
            Dim strBuilder As New StringBuilder
            Dim varName As New StringBuilder
            Dim findVar As Boolean

            For i As Integer = 0 To cmd.Parameter.Count - 1
                findVar = False
                varName.Clear()
                strBuilder.Clear()
                For Each c As Char In cmd.Parameter(i)
                    If c = "{"c Then
                        If findVar Then
                            findVar = False
                            strBuilder.Append("{")
                        Else
                            findVar = True
                        End If
                    ElseIf c = "}"c Then
                        If findVar Then
                            strBuilder.Append(ReplaceVar(varName.ToString(), localVariable))
                            varName.Clear()
                            findVar = False
                        Else
                            strBuilder.Append("}")
                        End If
                    Else
                        If findVar Then
                            varName.Append(c)
                        Else
                            strBuilder.Append(c)
                        End If
                    End If
                Next
                cmd.Parameter(i) = strBuilder.ToString()
            Next
            Return cmd
        End Function

        Public Sub StepTest()
            If TestStatus <> TestStatusEnum.Testing Then Return
            _nextPause = True
            If _nodePaused Then
                _exitPaused = True
            End If
        End Sub

        Public Sub ContinueTest()
            If TestStatus <> TestStatusEnum.Testing Then Return
            _nextPause = False
            _exitPaused = True
        End Sub

        ''' <summary>
        ''' 退出执行,测试结果为失败
        ''' </summary>
        Public Sub ExitTest()
            If TestStatus <> TestStatusEnum.Testing Then Return
            _exitTest = True

            _nextPause = False
            _exitPaused = True
            MsgBox($"{TestStatus}:{_exitTest}:{_nextPause}:{_exitPaused}")
        End Sub

#End Region

#Region "测试函数集合"

        ''' <summary>
        ''' 测试主函数，测试Main模块，根据测试结果测试Pass/Fail模块
        ''' </summary>
        Private Sub TestFunction()
            _testResult.ResetTestResult() '重置测试结果信息

            '初始化测试总项数据,用于进度条计算
            _startTestIndex = _testModules(FixedModuleEnum.Main.ToString).RowListIndex
            _endTestIndex = _startTestIndex + _testModules(FixedModuleEnum.Main.ToString).AllChildCount
            _testCount = _testModules(FixedModuleEnum.Main.ToString).AllChildCount

            TestStatus = TestStatusEnum.Testing
            RaiseEvent TestStatusChanged(Me, New TestStatusChangedEventArgs(TestStatus))

            Dim result As TestCommandReturn
            If _setupModuleExecuted = False Then '设定模块未执行
                result = ExecuteFixedModule(FixedModuleEnum.Setup)
                If result.ExecuteResult Then
                    _setupModuleExecuted = True
                Else
                    result.ExecuteResultTipString &= $"[{FixedModuleEnum.Setup}]模块执行失败,{result.ExecuteResultTipString}"

                    TestStatus = TestStatusEnum.TestFail
                    RaiseEvent TestStatusChanged(Me, New TestStatusChangedEventArgs(TestStatus))

                    'todo：结束测试
                    ' TestEnd
                    Return
                End If
            End If

            _testWatch.Restart()
            RaiseEvent TestTimeChanged(Me, New TestTimeEventArgs(_testWatch.Elapsed))

            result = ExecuteFixedModule(FixedModuleEnum.Main)
            If result.ExecuteResult Then  '执行测试主模块
                _testPassCount += 1
                _testResult.TestResult = TestResult.TestResultEnum.Pass
                ExecuteFixedModule(FixedModuleEnum.Pass) '执行测试成功模块
                RaiseEvent TestPass(Me, New EventArgs())

                TestStatus = TestStatusEnum.TestPass
                RaiseEvent TestStatusChanged(Me, New TestStatusChangedEventArgs(TestStatus))
            Else
                _testFailCount += 1
                _testResult.TestResult = TestResult.TestResultEnum.Fail
                _testResult.FailSteps.Add(_testNode)
                _testResult.ErrCode = _testNode.ErrorCode

                ExecuteFixedModule(FixedModuleEnum.Fail) '执行测试失败模块
                RaiseEvent TestFail(Me, New TestFailEventArgs(_testNode))

                TestStatus = TestStatusEnum.TestFail
                RaiseEvent TestStatusChanged(Me, New TestStatusChangedEventArgs(TestStatus))
            End If
            RaiseEvent TestCountChanged(Me, New TestCountChangedEventArgs(_testPassCount, _testFailCount))

            _testWatch.Stop()
            RaiseEvent TestTimeChanged(Me, New TestTimeEventArgs(_testWatch.Elapsed))

            _testResult.UserID = ProcessStation.UserId
            _testResult.UsedTime = _testWatch.Elapsed
            _testResult.OrderID = DbConnector.SearchOrderID(_processStation.ParentProject.Index, _testResult.DUT_SN)
        End Sub


        ''' <summary>
        ''' 调试测试当前节点及其子节点
        ''' </summary>
        ''' <param name="obj">节点对象</param>
        Public Function TestNode(obj As Object) As TestCommandReturn
            Dim node As RowNode = CType(obj, RowNode)

            DebugMode = True
            '初始化测试总项数据,用于进度条计算
            _startTestIndex = node.RowListIndex
            _endTestIndex = _startTestIndex + node.AllChildCount
            _testCount = node.AllChildCount

            _exitTest = False
            _testResult.ResetTestResult() '重置测试结果信息

            RaiseEvent TestStart(Me, New EventArgs())
            _userVar.Clear() '清空全局变量

            TestStatus = TestStatusEnum.Testing
            RaiseEvent TestStatusChanged(Me, New TestStatusChangedEventArgs(TestStatus))

            _testWatch.Restart()
            RaiseEvent TestTimeChanged(Me, New TestTimeEventArgs(_testWatch.Elapsed))

            Dim result As TestCommandReturn = ExecutePlan(node, Nothing)
            If result.ExecuteResult Then
                TestStatus = TestStatusEnum.TestPass
                RaiseEvent TestStatusChanged(Me, New TestStatusChangedEventArgs(TestStatus))

                _testFailCount += 1
                _testResult.TestResult = TestResult.TestResultEnum.Fail
                RaiseEvent TestPass(Me, New EventArgs())
            Else
                TestStatus = TestStatusEnum.TestFail
                RaiseEvent TestStatusChanged(Me, New TestStatusChangedEventArgs(TestStatus))

                _testFailCount += 1
                _testResult.TestResult = TestResult.TestResultEnum.Fail
                RaiseEvent TestFail(Me, New TestFailEventArgs(_testNode))
            End If

            _testWatch.Stop()
            RaiseEvent TestTimeChanged(Me, New TestTimeEventArgs(_testWatch.Elapsed))

            '进度修改百分百
            RaiseEvent TestProgressChanged(Me, New TestProgressChangedEventArgs(100))

            RaiseEvent TestEnd(Me, New TestEndEventArgs(_testResult))

            Return result
        End Function

        ''' <summary>
        ''' 从当前节点调试测试,直到测试完当前顶级模块
        ''' </summary>
        ''' <param name="obj">节点对象</param>
        Public Sub TestBeginWithNode(obj As Object)
            Dim node As RowNode = CType(obj, RowNode)


            '获取顶级节点
            Dim tmpNode As RowNode = node
            While tmpNode.RowLever > 0
                tmpNode = tmpNode.ParentNode
            End While

            '初始化测试总项数据,用于进度条计算
            _startTestIndex = node.RowListIndex
            _endTestIndex = tmpNode.RowIndex + tmpNode.AllChildCount
            _testCount = _endTestIndex - _startTestIndex

            DebugMode = True
            _exitTest = False
            _testResult.ResetTestResult() '重置测试结果信息

            RaiseEvent TestStart(Me, New EventArgs())
            _userVar.Clear() '清空全局变量

            TestStatus = TestStatusEnum.Testing
            RaiseEvent TestStatusChanged(Me, New TestStatusChangedEventArgs(TestStatus))

            _testWatch.Restart()
            RaiseEvent TestTimeChanged(Me, New TestTimeEventArgs(_testWatch.Elapsed))

            Dim result As TestCommandReturn = BeginWithNode(node)
            If result.ExecuteResult Then
                TestStatus = TestStatusEnum.TestPass
                RaiseEvent TestStatusChanged(Me, New TestStatusChangedEventArgs(TestStatus))

                _testFailCount += 1
                _testResult.TestResult = TestResult.TestResultEnum.Fail
                RaiseEvent TestPass(Me, New EventArgs())
            Else
                TestStatus = TestStatusEnum.TestFail
                RaiseEvent TestStatusChanged(Me, New TestStatusChangedEventArgs(TestStatus))

                _testFailCount += 1
                _testResult.TestResult = TestResult.TestResultEnum.Fail
                RaiseEvent TestFail(Me, New TestFailEventArgs(_testNode))
            End If

            _testWatch.Stop()
            RaiseEvent TestTimeChanged(Me, New TestTimeEventArgs(_testWatch.Elapsed))

            '进度修改百分百
            RaiseEvent TestProgressChanged(Me, New TestProgressChangedEventArgs(100))

            RaiseEvent TestEnd(Me, New TestEndEventArgs(_testResult))
        End Sub

        ''' <summary>
        ''' 测试单节点及之后的所有节点
        ''' </summary>
        ''' <param name="node"></param>
        Private Function BeginWithNode(node As RowNode) As TestCommandReturn
            Dim result As New TestCommandReturn
            While node IsNot Nothing
                If _exitTest Then Return GetExitTestReturn()

                result = ExecutePlan(node, Nothing)
                If result.ExecuteResult = False Then
                    If TestFailMode = TestFailModeEnum.StepFail Then
                        Exit While '单步失败模式下退出测试，不再执行
                    End If
                End If

                If node.RowLever = 0 Then Exit While
                If node.NextNode Is Nothing Then

                    node = node.ParentNode.NextNode
                Else
                    node = node.NextNode
                End If
            End While

            Return result
        End Function


        ''' <summary>
        ''' 调试测试节点数组
        ''' </summary>
        ''' <param name="nodes"></param>
        Public Sub TestNodes(nodes() As RowNode)
            _exitTest = False
            _testResult.ResetTestResult() '重置测试结果信息

            RaiseEvent TestStart(Me, New EventArgs())
            _userVar.Clear() '清空全局变量

            TestStatus = TestStatusEnum.Testing
            RaiseEvent TestStatusChanged(Me, New TestStatusChangedEventArgs(TestStatus))

            _testWatch.Restart()
            RaiseEvent TestTimeChanged(Me, New TestTimeEventArgs(_testWatch.Elapsed))

            Dim result As TestCommandReturn = TestNodesFunction(nodes)
            If result.ExecuteResult Then
                TestStatus = TestStatusEnum.TestPass
                RaiseEvent TestStatusChanged(Me, New TestStatusChangedEventArgs(TestStatus))

                _testFailCount += 1
                _testResult.TestResult = TestResult.TestResultEnum.Fail
                RaiseEvent TestPass(Me, New EventArgs())
            Else
                TestStatus = TestStatusEnum.TestFail
                RaiseEvent TestStatusChanged(Me, New TestStatusChangedEventArgs(TestStatus))

                _testFailCount += 1
                _testResult.TestResult = TestResult.TestResultEnum.Fail
                RaiseEvent TestFail(Me, New TestFailEventArgs(_testNode))
            End If

            _testWatch.Stop()
            RaiseEvent TestTimeChanged(Me, New TestTimeEventArgs(_testWatch.Elapsed))

            '进度修改百分百
            RaiseEvent TestProgressChanged(Me, New TestProgressChangedEventArgs(100))

            RaiseEvent TestEnd(Me, New TestEndEventArgs(_testResult))
        End Sub



        ''' <summary>
        ''' 调试测试节点数组,包含其子节点
        ''' </summary>
        ''' <param name="nodes"></param>
        Public Function TestNodesFunction(nodes() As RowNode) As TestCommandReturn
            Dim result As New TestCommandReturn
            For Each node As RowNode In nodes
                If _exitTest Then Return GetExitTestReturn() '退出测试

                result = ExecutePlan(node, Nothing)
                If result.ExecuteResult Then Continue For '执行成功则执行下一流程
                If TestFailMode = TestFailModeEnum.StepFail Then Exit For '单步失败模式下退出测试，不再执行
            Next
            Return result
        End Function


        ''' <summary>
        ''' 调试测试子节点集合
        ''' </summary>
        ''' <param name="nodes"></param>
        Public Sub TestChildNodes(nodes As RowNodeCollection)
            _exitTest = False
            _testResult.ResetTestResult() '重置测试结果信息

            RaiseEvent TestStart(Me, New EventArgs())
            _userVar.Clear() '清空全局变量

            TestStatus = TestStatusEnum.Testing
            RaiseEvent TestStatusChanged(Me, New TestStatusChangedEventArgs(TestStatus))

            _testWatch.Restart()
            RaiseEvent TestTimeChanged(Me, New TestTimeEventArgs(_testWatch.Elapsed))

            Dim result As TestCommandReturn = TestNodesFunction(nodes)
            If result.ExecuteResult Then
                TestStatus = TestStatusEnum.TestPass
                RaiseEvent TestStatusChanged(Me, New TestStatusChangedEventArgs(TestStatus))

                _testFailCount += 1
                _testResult.TestResult = TestResult.TestResultEnum.Fail
                RaiseEvent TestPass(Me, New EventArgs())
            Else
                TestStatus = TestStatusEnum.TestFail
                RaiseEvent TestStatusChanged(Me, New TestStatusChangedEventArgs(TestStatus))

                _testFailCount += 1
                _testResult.TestResult = TestResult.TestResultEnum.Fail
                RaiseEvent TestFail(Me, New TestFailEventArgs(_testNode))
            End If

            _testWatch.Stop()
            RaiseEvent TestTimeChanged(Me, New TestTimeEventArgs(_testWatch.Elapsed))

            '进度修改百分百
            RaiseEvent TestProgressChanged(Me, New TestProgressChangedEventArgs(100))

            RaiseEvent TestEnd(Me, New TestEndEventArgs(_testResult))
        End Sub


        ''' <summary>
        ''' 调试测试节点集合,及其子节点
        ''' </summary>
        ''' <param name="nodes"></param>
        Public Function TestNodesFunction(nodes As RowNodeCollection) As TestCommandReturn
            Dim result As New TestCommandReturn
            For Each node As RowNode In nodes
                If _exitTest Then Return GetExitTestReturn() '退出测试

                result = ExecutePlan(node, Nothing)
                If result.ExecuteResult Then Continue For '执行成功则执行下一流程
                If TestFailMode = TestFailModeEnum.StepFail Then Exit For '单步失败模式下退出测试，不再执行
            Next
            Return result
        End Function

        Public Sub TestFixedModule([module] As FixedModuleEnum)
            TestNode(_testModules($"{[module]}"))
        End Sub

        Public Sub TestSetupModule()
            TestFixedModule(FixedModuleEnum.Setup)
        End Sub

        Public Sub TestMainModule()
            TestFixedModule(FixedModuleEnum.Main)
        End Sub

        Public Sub TestPassModule()
            TestFixedModule(FixedModuleEnum.Pass)
        End Sub

        Public Sub TestFailModule()
            TestFixedModule(FixedModuleEnum.Fail)
        End Sub

        Public Sub TestCleanupModule()
            TestFixedModule(FixedModuleEnum.Cleanup)
        End Sub
#End Region


        Enum TestFailModeEnum
            ''' <summary>
            ''' 单步测试失败后退出测试,判定测试失败
            ''' </summary>
            StepFail
            ''' <summary>
            ''' 单步测试失败后继续测试，执行所有测试流程后，判定测试失败
            ''' </summary>
            AllFail
        End Enum

        Enum NodeDebugMode
            ''' <summary>
            ''' 仅测试当前节点及其子节点
            ''' </summary>
            OnlyNode

            ''' <summary>
            ''' 从节点开始往下测试
            ''' </summary>
            BeginNode
        End Enum
    End Class

End Namespace