Imports System.Drawing
Imports System.Windows.Forms
Imports FlexCell
Imports FluentFTP
Imports Mysqlx.XDevAPI.Relational
Imports UTS_Core.UTSModule.Production
Imports UTS_Core.UTSModule.Test.Command
Imports UTS_Core.UTSModule.Test.StatusMonitor

Namespace UTSModule.Station
    Public Class StationPlanGrid
        ''' <summary>表格控件对象</summary>
        Private _grd As Grid

        ''' <summary>表格树状目录起始节点</summary>
        Private _headNode As RowNode

        ''' <summary>起始绘制列</summary>
        Private _drawCol As Integer

        ''' <summary>起始绘制行</summary>
        Private _drawStartRow As Integer

        ''' <summary>调试模式是否开启</summary>
        Private _debugMode As Boolean

        ''' <summary>是否显示节点图标</summary>
        Private ReadOnly _showNodeIcon As Boolean

        ''' <summary>处于更新表格状态中,不触发表格单元格修改事件</summary>
        Private _uploading As Boolean

        ''' <summary>多个参数间的分割符号</summary>
        Private ReadOnly _paramChar As Char

        ''' <summary>测试命令管理器</summary>
        Private ReadOnly _testCmdManager As TestCmdManager

        ''' <summary>错误代码管理器</summary>
        Private ReadOnly _errCodeManager As ErrCodeManager

        ''' <summary>表格标签字段字典</summary>
        Private ReadOnly _labelNames As New Dictionary(Of String, Integer)

        ''' <summary>表格记录名称字段字典</summary>
        Private ReadOnly _recordNames As New Dictionary(Of String, Integer)

        Private gMouseOldRow As Integer '记录鼠标移动前的行号
        Private gMouseOldCol As Integer
        Private gOldRowType As Integer
        Private gRowType As Integer
        Private gDescTextColor As Brush 'description cell 文字颜色
        Private gIsGri_MouseMove_EventReady As Boolean = True 'MoveMove 函数加锁，防止嵌套执行

        ''' <summary>当前活动节点</summary>
        Public ActiveNode As RowNode

        ''' <summary>节点选择改变事件</summary>
        Public Event PlanNodeSelectChanged(ByVal sender As Object, ByVal e As PlanNodeSelectChangedEventArgs)

        ''' <summary>节点文本被修改事件</summary>
        Public Event RowNodeTextChanged(ByVal sender As Object, ByVal e As RowNodeChangedEventArgs)

        ''' <summary>节点执行可撤销事件</summary>
        Public Event PlanGridCommandChanged(ByVal sender As Object, ByVal e As EventArgs)

        ''' <summary>粘贴操作计时器</summary>
        Private WithEvents _userActionTimer As New Timer()

        ''' <summary>计时器超时时间</summary>
        Private Const UserActionTimeout As Integer = 300 ' ms

        Private _dicTextChangedRowNode As New Dictionary(Of Integer, TextChangedRowNode) '记录文本修改的行号和节点对象



        ''' <summary>
        ''' 构造函数
        ''' </summary>

        Sub New()
            NodeStartRow = 1
            _drawCol = ColNames.Description

            _showNodeIcon = False

            _uploading = False
            _paramChar = "#"c

            _testCmdManager = TestCmdManager.CreateManager()
            _errCodeManager = ErrCodeManager.CreateManager()

            _userActionTimer.Interval = UserActionTimeout
            _userActionTimer.Stop()
        End Sub

        ''' <summary>
        ''' 表格内容改变处理事件
        ''' </summary>
        Private Sub OnUserAction(node As RowNode)
            If CommandManager.IsRuning Then Return

            ' 每次用户操作都重置计时器
            _userActionTimer.Stop()
            _userActionTimer.Start()

            '记录当前活动节点
            If Not _dicTextChangedRowNode.ContainsKey(node.RowListIndex) Then
                _dicTextChangedRowNode.Add(node.RowListIndex, New TextChangedRowNode With {.BeforeRowNode = node.Clone})
            End If
        End Sub

        Private Sub OnUserActionEnd(node As RowNode)
            If CommandManager.IsRuning Then Return

            '记录当前活动节点
            If _dicTextChangedRowNode.ContainsKey(node.RowListIndex) Then
                _dicTextChangedRowNode(node.RowListIndex).AfterRowNode = node.Clone
            End If
        End Sub

        ''' <summary>
        ''' 粘贴操作计时器超时处理事件
        ''' </summary>
        ''' <param name="sender"></param>
        ''' <param name="e"></param>
        Private Sub _userActionTimer_Tick(sender As Object, e As EventArgs) Handles _userActionTimer.Tick
            _userActionTimer.Stop()

            OnUserActionTimeout()
        End Sub

        Private Sub OnUserActionTimeout()
            If CommandManager.IsRuning = False Then
                CommandManager.AddUndoCommand(New GridNodeTextChangedCommand(Me, _dicTextChangedRowNode))

                RaiseEvent PlanGridCommandChanged(Nothing, Nothing)
            End If

            _dicTextChangedRowNode = New Dictionary(Of Integer, TextChangedRowNode)
        End Sub


        ''' <summary>
        ''' 屏蔽表格引发自定义事件,避免行节点内容修改，todo:暂未完成
        ''' </summary>
        ''' <returns></returns>
        Public Property SkipEvent() As Boolean

        Property DebugMode() As Boolean
            Get
                Return _debugMode
            End Get
            Set(value As Boolean)
                _debugMode = value
                UpdateColVisible()
            End Set
        End Property


        Property Grid() As Grid
            Get
                Return _grd
            End Get
            Set(value As Grid)
                _grd = value

                InitializeGrid()

                'RemoveHandler _grd.Click, AddressOf Grid_Click
                RemoveHandler _grd.CellChange, AddressOf Grid_CellChange
                RemoveHandler _grd.SelChange, AddressOf Grid_SelChange
                RemoveHandler _grd.LeaveCell, AddressOf Grid_LeaveCell
                RemoveHandler _grd.ComboDropDown, AddressOf Grid_ComboDropDown
                RemoveHandler _grd.ComboClick, AddressOf Grid_ComboClick
                RemoveHandler _grd.MouseMove, AddressOf Grid_MouseMove
                RemoveHandler _grd.MouseLeave, AddressOf Grid_MouseLeave
                RemoveHandler _grd.MouseEnter, AddressOf Grid_MouseEnter

                'AddHandler _grd.Click, AddressOf Grid_Click
                AddHandler _grd.CellChange, AddressOf Grid_CellChange
                AddHandler _grd.SelChange, AddressOf Grid_SelChange
                AddHandler _grd.LeaveCell, AddressOf Grid_LeaveCell
                AddHandler _grd.ComboDropDown, AddressOf Grid_ComboDropDown
                AddHandler _grd.ComboClick, AddressOf Grid_ComboClick
                AddHandler _grd.MouseMove, AddressOf Grid_MouseMove
                AddHandler _grd.MouseLeave, AddressOf Grid_MouseLeave
                AddHandler _grd.MouseEnter, AddressOf Grid_MouseEnter

                _grd.ComboBox(ColNames.CommandType).Items.Clear()
                _grd.ComboBox(ColNames.CommandType).Items.AddRange(_testCmdManager.GetCommandTypes())

                _grd.ComboBox(ColNames.ErrorCode).Items.Clear()
                _grd.ComboBox(ColNames.ErrorCode).Items.AddRange(_errCodeManager.GetAllCodeAndMsg())
            End Set
        End Property

        Private _autoRedraw As Integer = 0

        Private Sub LockGridAutoRedraw()
            If _autoRedraw = 0 Then _grd.AutoRedraw = False

            _autoRedraw += 1
        End Sub

        Private Sub UnLockGridAutoRedraw()
            _autoRedraw -= 1

            If _autoRedraw = 0 Then
                _grd.AutoRedraw = True
                _grd.Refresh()
            End If
        End Sub

#Region "导航功能"
        ''' <summary>
        ''' 向后导航
        ''' </summary>
        Public Sub BackwardNavigation()
            GridNavigationManager.Backward()
        End Sub

        ''' <summary>
        ''' 向前导航
        ''' </summary>
        Public Sub ForwardNavigation()
            GridNavigationManager.Forward()
        End Sub

        ''' <summary>
        ''' 是否可以后退
        ''' </summary>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Function CanBackward() As Boolean
            Return GridNavigationManager.CanBackward
        End Function

        ''' <summary>
        ''' 是否可以前进
        ''' </summary>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Function CanForward() As Boolean
            Return GridNavigationManager.CanForward
        End Function

        ''' <summary>
        ''' 
        ''' </summary>
        Public Sub ClearNavigation()
            GridNavigationManager.Clear()
            _lastSelction = Nothing
            _lastTempSelction = Nothing
        End Sub

        Public Sub JumpCall()
            If _grd.ActiveCell Is Nothing Then Return
            Dim row As Integer = _grd.ActiveCell.Row
            Dim node As RowNode = _headNode.RowList(row - _drawStartRow + 1)
            If node.CommandType = "System" AndAlso node.Command = "Call" Then
                Dim labelName As String = node.Parameters(0).Value
                If String.IsNullOrWhiteSpace(labelName) Then
                    MsgBox("请填写有效的模块名！")
                Else
                    If IsExistLabelName(labelName) Then
                        Dim destRow As Integer = GetLabelNameGridRow(labelName)
                        _grd.Cell(destRow, ColNames.Label).SetFocus()
                    Else
                        MsgBox($"未发现当前模块名[{labelName}]匹配的模块！")
                    End If
                End If
            End If
        End Sub
#End Region


#Region "撤销功能"
        Public Sub ClearCommand()
            CommandManager.ClearCommands()
            RaiseEvent PlanGridCommandChanged(Nothing, Nothing)
        End Sub

        Public Function CanUndo() As Boolean
            Return CommandManager.CanUndo
        End Function

        Public Function CanRedo() As Boolean
            Return CommandManager.CanRedo
        End Function

        Public Sub UndoCommand()
            If CommandManager.CanUndo Then
                CommandManager.Undo()
                RaiseEvent PlanGridCommandChanged(Nothing, Nothing)
            End If
        End Sub

        Public Sub RedoCommand()
            If CommandManager.CanRedo Then
                CommandManager.Redo()
                RaiseEvent PlanGridCommandChanged(Nothing, Nothing)
            End If
        End Sub
#End Region

#Region "字段去重"
        ''' <summary>
        ''' 当前模块名是否已存在
        ''' </summary>
        ''' <param name="labelName"></param>
        ''' <returns></returns>
        Public Function IsExistLabelName(labelName As String) As Boolean
            labelName = labelName.ToLower()
            Return _labelNames.ContainsKey(labelName)
        End Function

        ''' <summary>
        ''' 当前字段名
        ''' </summary>
        ''' <param name="recordName"></param>
        ''' <returns></returns>
        Public Function IsExistRecordName(recordName As String) As Boolean
            recordName = recordName.ToLower()

            Return _recordNames.ContainsKey(recordName)
        End Function

        ''' <summary>
        ''' 清空标签名称
        ''' </summary>
        Private Sub ClearLabelNames()
            _labelNames.Clear()
        End Sub

        ''' <summary>
        ''' 清空记录名称
        ''' </summary>
        Private Sub ClearRecordNames()
            _recordNames.Clear()
        End Sub

        ''' <summary>
        ''' 添加标签名称
        ''' </summary>
        ''' <param name="labelName">标签名称</param>
        ''' <param name="row">行号</param>
        Private Sub AddLabelName(labelName As String, row As Integer)
            labelName = labelName.ToLower()
            If Not _labelNames.ContainsKey(labelName) Then
                _labelNames.Add(labelName, row)
            End If
        End Sub

        ''' <summary>
        ''' 添加记录名称
        ''' </summary>
        ''' <param name="recordName">记录名</param>
        ''' <param name="row">行号</param>
        Private Sub AddRecordName(recordName As String, row As Integer)
            recordName = recordName.ToLower()

            If Not _recordNames.ContainsKey(recordName) Then
                _recordNames.Add(recordName, row)
            End If
        End Sub

        ''' <summary>
        ''' 移出标签名称
        ''' </summary>
        ''' <param name="labelName">标签名称</param>
        Private Sub RemoveLabelName(labelName As String)
            labelName = labelName.ToLower()

            If _labelNames.ContainsKey(labelName) Then
                _labelNames.Remove(labelName)
            End If
        End Sub

        ''' <summary>
        ''' 移出记录名称
        ''' </summary>
        ''' <param name="recordName">记录名称</param>
        Private Sub RemoveRecordName(recordName As String)
            recordName = recordName.ToLower()

            If _recordNames.ContainsKey(recordName) Then
                _recordNames.Remove(recordName)
            End If
        End Sub

        ''' <summary>
        ''' 获取标签名称行号
        ''' </summary>
        ''' <param name="labelName"></param>
        ''' <returns></returns>
        Private Function GetLabelNameGridRow(labelName As String) As Integer
            labelName = labelName.ToLower()

            Return _labelNames(labelName)
        End Function

        ''' <summary>
        ''' 获取记录名称行号
        ''' </summary>
        ''' <param name="recordName"></param>
        ''' <returns></returns>
        Private Function GetRecordNameGridRow(recordName As String) As Integer
            recordName = recordName.ToLower()

            Return _recordNames(recordName)
        End Function

        ''' <summary>
        ''' 从表格中重载标签名称
        ''' </summary>
        Private Sub ReflushLabelNames()
            ClearLabelNames()
            If _headNode Is Nothing Then Return
            For Each node As RowNode In _headNode.RowList
                If node.RowType = RowNode.RowTypeEnum.Module OrElse node.RowType = RowNode.RowTypeEnum.FixedModule Then
                    If Not String.IsNullOrWhiteSpace(node.Label) AndAlso Not IsExistLabelName(node.Label) Then
                        AddLabelName(node.Label, node.RowListIndex)
                    End If
                End If
            Next
        End Sub


        ''' <summary>
        ''' 从表格中重载记录名称
        ''' </summary>
        Private Sub ReflushRecordNames()
            ClearRecordNames()
            If _headNode Is Nothing Then Return
            For Each node As RowNode In _headNode.RowList
                If Not String.IsNullOrWhiteSpace(node.RecordName) AndAlso Not IsExistRecordName(node.RecordName) Then
                    AddRecordName(node.RecordName, node.RowListIndex)
                End If
            Next
        End Sub

#End Region

        '离开表格时活动单元格背景颜色
        Private leaveColor As Color = Color.White

        '离开表格时活动单元格行号
        Private leaveRow As Integer = 0

        ''' <summary>
        ''' 光标离开grd表格时，黄色底色切换到选中行
        ''' </summary>
        Public Sub Grid_MouseLeave(sender As Object, e As EventArgs)
            If _grd.EditorVisible Then Return '处于编辑状态
            If _grd.ActiveCell.Row <= 0 Then Return

            leaveRow = _grd.ActiveCell.Row
            leaveColor = _grd.ActiveCell.BackColor

            _grd.Range(leaveRow, ColNames.Result, leaveRow, ColNames.ErrorMessage).BackColor = Color.LightCyan

        End Sub

        ''' <summary>
        ''' 光标进入grd表格时，原底色切换到选中行
        ''' </summary>
        Public Sub Grid_MouseEnter(sender As Object, e As EventArgs)
            If _grd.EditorVisible Then Return '处于编辑状态
            If leaveRow <= 0 Then Return
            Try
                _grd.Range(leaveRow, ColNames.Result, leaveRow, ColNames.ErrorMessage).BackColor = leaveColor
            Catch ex As Exception

            End Try

        End Sub


        ''' <summary>
        ''' 鼠标移动到节点位置时所有子节点背景着色
        ''' </summary>
        Private Sub Mouse_MoveOnNode_BackColorRepain(grd As Grid, rowType As Integer, startRowIdx As Integer, backColor As Color)
            '起始行NodeLevel
            Dim startRowNodeLevel As Integer = GetRowNodeLevel(grd, startRowIdx)
            '结束行号
            Dim endRowNodeIdx As Integer
            '当前行行号和node level
            Dim tmpCurrRowIdx As Integer = startRowIdx
            Dim tmpCurrRowNodeLevel As Integer = GetRowNodeLevel(grd, tmpCurrRowIdx)
            '判断当前行Row type = 1，记录下本行的NodeLevel = tempNodeLevel，记录NodeLstIndx

            Dim node As RowNode = _headNode.RowList(startRowIdx - _drawStartRow + 1)
            If rowType = RowNode.RowTypeEnum.Module OrElse rowType = RowNode.RowTypeEnum.Control Then
                ''从起始行开始往下遍历
                'Do
                '    If tmpCurrRowIdx < Grid.Rows Then '遍历到最大行还未结束
                '        tmpCurrRowIdx = tmpCurrRowIdx + 1
                '        tmpCurrRowNodeLevel = GetRowNodeLevel(grd, tmpCurrRowIdx)
                '    Else
                '        Return
                '    End If
                'Loop Until tmpCurrRowNodeLevel <= startRowNodeLevel Or tmpCurrRowIdx >= Grid.Rows '再次遍历到与起始行同级或更高级别的行，就算是该节点遍历结束
                'endRowNodeIdx = tmpCurrRowIdx - 1
                endRowNodeIdx = startRowIdx + node.AllChildCount

                '底色着色
                LockGridAutoRedraw()
                grd.Range(startRowIdx, ColNames.Result, endRowNodeIdx, ColNames.ErrorMessage).BackColor = backColor

                UnLockGridAutoRedraw()
            End If

        End Sub

        Private Function GetRowNodeAction(node As RowNode) As Boolean
            If node.ParentNode.RowLever <= 1 Then
                Return node.ParentNode.Action
            Else
                If node.ParentNode.Action Then
                    Return GetRowNodeAction(node.ParentNode)
                Else
                    Return False
                End If
            End If
        End Function

        Private Function GetRowNodeForeColor(node As RowNode) As Color
            If node.Action Then
                If node.RowLever > 1 AndAlso GetRowNodeAction(node) = False Then Return Color.LightGray

                Select Case node.RowType
                    Case RowNode.RowTypeEnum.Flow
                        If String.IsNullOrEmpty(node.CommandType) OrElse String.IsNullOrEmpty(node.Command) Then
                            Return Color.DarkGray
                        Else
                            Select Case node.CommandType
                                Case "System"
                                    If node.Command = "Call" Then
                                        Return Color.Blue
                                    Else
                                        Return Color.DarkSlateGray
                                    End If
                                Case "ComPort"
                                    Return Color.Olive
                                Case "UtsComPort"
                                    Return Color.SeaGreen
                                Case "Converter"
                                    Return Color.DarkOrange
                                Case "Process"
                                    Return Color.DarkBlue
                                Case Else
                                    Return Color.Black
                            End Select
                        End If
                    Case RowNode.RowTypeEnum.Module
                        Return Color.DeepPink
                    Case RowNode.RowTypeEnum.FixedModule
                        Return Color.Blue
                    Case Else
                        Return Color.Black
                End Select
            Else
                Return Color.LightGray
            End If
        End Function

        ''' <summary>
        ''' 节点类型变更，修改节点行样式
        ''' </summary>
        ''' <param name="node"></param>
        Private Sub NodeRowTypeChanged(node As RowNode)
            Dim row As Integer = node.RowListIndex
            With _grd
                If node.RowType = RowNode.RowTypeEnum.FixedModule Then
                    .Cell(row, ColNames.Label).FontSize = 10
                    .Cell(row, ColNames.Label).FontBold = True
                ElseIf node.RowType = RowNode.RowTypeEnum.Module Then
                    .Cell(row, ColNames.Label).FontSize = 8
                    .Cell(row, ColNames.Label).FontBold = True
                ElseIf node.RowType = RowNode.RowTypeEnum.Flow Then
                    .Cell(row, ColNames.Label).FontSize = 8
                    .Cell(row, ColNames.Label).FontBold = False
                End If
                NodeActionChanged(node)
            End With
        End Sub


        ''' <summary>
        ''' 节点状态变更，修改节点行颜色
        ''' </summary>
        ''' <param name="node"></param>
        Private Sub NodeActionChanged(node As RowNode)
            Dim row As Integer = node.RowListIndex
            Dim tmpRowColor As Color

            LockGridAutoRedraw()

            tmpRowColor = GetRowNodeForeColor(node) '获取选颜色
            _grd.Range(row, ColNames.Result, row, ColNames.ErrorMessage).ForeColor = tmpRowColor '着色

            If node.SaveToDb = False Then
                _grd.Cell(row, ColNames.RecordName).ForeColor = Color.DarkGray  '着色
            End If

            If node.CommandType = "" And node.Description = "" Then '空白行字体与底色同色，避免对编辑产生干扰
                _grd.Range(row, ColNames.Result, row, ColNames.ErrorMessage).ForeColor = _grd.Cell(row, ColNames.Description).BackColor   '着色
            End If

            If node.RowType = RowNode.RowTypeEnum.FixedModule Then
                _grd.Range(row, ColNames.CommandType, row, ColNames.ErrorMessage).ForeColor = _grd.Cell(row, ColNames.Description).BackColor   '着色
            End If

            For i As Integer = 0 To node.RowNodes.Count - 1
                NodeActionChanged(CType(node.RowNodes(i), RowNode), node.Action)
            Next

            UnLockGridAutoRedraw()
        End Sub

        ''' <summary>
        ''' 节点状态变更，修改节点行颜色
        ''' </summary>
        ''' <param name="node"></param>
        Private Sub NodeActionChanged(node As RowNode, action As Boolean)
            Dim row As Integer = node.RowListIndex
            Dim tmpRowColor As Color

            tmpRowColor = GetRowNodeForeColor(node) '获取选颜色
            _grd.Range(row, ColNames.Result, row, ColNames.ErrorMessage).ForeColor = tmpRowColor '着色

            If node.SaveToDb = False Then
                _grd.Cell(row, ColNames.RecordName).ForeColor = Color.DarkGray  '着色
            End If

            If node.CommandType = "" And node.Description = "" Then '空白行字体与底色同色，避免对编辑产生干扰
                _grd.Range(row, ColNames.Result, row, ColNames.ErrorMessage).ForeColor = _grd.Cell(row, ColNames.Description).BackColor   '着色
            End If

            If node.RowType = RowNode.RowTypeEnum.FixedModule Then
                _grd.Range(row, ColNames.CommandType, row, ColNames.ErrorMessage).ForeColor = _grd.Cell(row, ColNames.Description).BackColor   '着色
            End If

            For i As Integer = 0 To node.RowNodes.Count - 1
                NodeActionChanged(CType(node.RowNodes(i), RowNode), action)
            Next
        End Sub


        ''' <summary>
        ''' 选定行使能或清除Action选项
        ''' </summary>
        Public Sub Check_Uncheck_Action(grd As Grid, startRow As Integer, endRow As Integer, IsCheck As Boolean)
            Dim idx As Integer
            If endRow < startRow Then Return

            For idx = startRow To endRow
                grd.Cell(idx, ColNames.Action).Text = IsCheck.ToString
            Next
        End Sub

        ''' <summary>
        ''' 光标移动时，所在行底色高亮
        ''' </summary>
        Private Sub Grid_MouseMove(Sender As Object, e As MouseEventArgs)
            If _grd.EditorVisible Then Return '处于编辑状态

            If gIsGri_MouseMove_EventReady = True Then

                Dim tmpMouseRow As Integer = _grd.MouseRow '鼠标当前行’
                Dim tmpMouseCol As Integer = _grd.MouseCol '鼠标当前列’

                If tmpMouseRow <= 0 Or tmpMouseCol <= 0 Then Return

                Dim tmpMouseRowNodeIdx As Integer = GetRowNodeIdx(_grd, tmpMouseRow) '同级节点计数’
                Dim tmpMouseRowNodeLevel As Integer = GetRowNodeLevel(_grd, tmpMouseRow) '节点级数
                Dim tmpMouseRowNodeLstIdx As Integer = GetRowNodeLstIdx(_grd, tmpMouseRow) '就是行号
                gRowType = GetRowType(_grd, tmpMouseRow)      '现在所在行的row type
                gOldRowType = GetRowType(_grd, gMouseOldRow) '前一次的row type

                Try
                    If tmpMouseRow <> gMouseOldRow Or tmpMouseCol <> gMouseOldCol Then '鼠标所在行变化
                        If gOldRowType <> RowNode.RowTypeEnum.FixedModule Then '光标离开Flow节点
                            _grd.Range(gMouseOldRow, ColNames.Result, gMouseOldRow, ColNames.ErrorMessage).BackColor = Color.White
                            _grd.Cell(gMouseOldRow, gMouseOldCol).FontBold = False
                            If gOldRowType = RowNode.RowTypeEnum.Module Then '光标离开Module节点
                                Mouse_MoveOnNode_BackColorRepain(_grd, gOldRowType, gMouseOldRow, Color.White)
                            ElseIf gOldRowType = RowNode.RowTypeEnum.Control Then '光标离开Module节点
                                Mouse_MoveOnNode_BackColorRepain(_grd, gOldRowType, gMouseOldRow, Color.White)
                            End If
                        End If
                        gMouseOldRow = tmpMouseRow
                        gMouseOldCol = tmpMouseCol
                    Else
                        If gRowType <> RowNode.RowTypeEnum.FixedModule Then '光标移动到flow节点
                            _grd.Range(tmpMouseRow, ColNames.Result, tmpMouseRow, ColNames.ErrorMessage).BackColor = Color.FromArgb(40, Color.LemonChiffon)
                            _grd.Cell(tmpMouseRow, tmpMouseCol).FontBold = True
                            If gRowType = RowNode.RowTypeEnum.Module Then '光标移动到module节点
                                Mouse_MoveOnNode_BackColorRepain(_grd, gRowType, tmpMouseRow, Color.FromArgb(40, Color.LemonChiffon))
                            ElseIf gRowType = RowNode.RowTypeEnum.Control Then
                                Mouse_MoveOnNode_BackColorRepain(_grd, gRowType, tmpMouseRow, Color.FromArgb(40, Color.LemonChiffon))
                            End If
                        End If
                    End If
                Catch ex As Exception
                    gIsGri_MouseMove_EventReady = True
                End Try
            End If
            gIsGri_MouseMove_EventReady = True
        End Sub





        ''' <summary>
        ''' 获取指定行的NodeIndex
        ''' </summary>
        ''' <returns></returns>
        Public Function GetRowNodeLstIdx(grd As Grid, cellRow As Integer) As Integer
            Try
                Dim node As RowNode = _headNode.RowList(cellRow - _drawStartRow + 1)
                Return node.RowListIndex
            Catch ex As Exception
                Return 0
            End Try

        End Function

        ''' <summary>
        ''' 获取指定行的NodeLever
        ''' </summary>
        ''' <returns></returns>
        Public Function GetRowNodeLevel(grd As Grid, cellRow As Integer) As Integer
            Try
                Dim node As RowNode = _headNode.RowList(cellRow - _drawStartRow + 1)
                Return node.RowLever
            Catch ex As Exception
                Return 0
            End Try

        End Function

        ''' <summary>
        ''' 获取指定行的NodeIndex
        ''' </summary>
        ''' <returns></returns>
        Public Function GetRowNodeIdx(grd As Grid, cellRow As Integer) As Integer
            Try
                Dim node As RowNode = _headNode.RowList(cellRow - _drawStartRow + 1)
                Return node.RowIndex
            Catch ex As Exception
                Return 0
            End Try
        End Function

        ''' <summary>
        ''' 获取指定行的RowType
        ''' </summary>
        ''' <returns></returns>
        Public Function GetRowType(grd As Grid, cellRow As Integer) As Integer
            Try
                Dim node As RowNode = _headNode.RowList(cellRow - _drawStartRow + 1)
                Return node.RowType
            Catch ex As Exception
                Return 3
            End Try

        End Function
        Property HeadNode() As RowNode
            Get
                Return _headNode
            End Get
            Set(value As RowNode)
                _headNode = value
            End Set
        End Property

        ''' <summary>
        ''' 折叠的列号
        ''' </summary>
        ''' <returns></returns>
        Property NodeCol() As Integer
            Get
                Return _drawCol
            End Get
            Set(value As Integer)
                _drawCol = value
            End Set
        End Property

        ''' <summary>
        ''' 折叠的起始行号
        ''' </summary>
        ''' <returns></returns>
        Property NodeStartRow() As Integer
            Get
                Return _drawStartRow
            End Get
            Set(value As Integer)
                _drawStartRow = value
            End Set
        End Property


        ''' <summary>
        ''' 初始化测试站表格
        ''' </summary>
        Public Sub InitializeGrid()
            With _grd
                LockGridAutoRedraw()

                .Rows = 1
                .Cols = ColNames.Max
                .DrawMode = DrawModeEnum.OwnerDraw '绘制方式为自绘

                .DisplayRowNumber = True    '首列显示数字
                .ExtendLastCol = True   '最后一列自动扩充
                '.MultiSelect = False '是否允许选择多行
                .FrozenCols = ColNames.Description '冻结列

                .BorderStyle = BorderStyleEnum.None
                .DefaultRowHeight = 22 '默认行高
                .DefaultFont = New Font("微软雅黑", 8)
                '.DefaultFont = New Font("Consolas", 8)
                .Range(0, 0, 0, .Cols - 1).Font = New Font($"幼圆", 8) '首行样式
                ' .SelectionMode = SelectionModeEnum.ByRow '选中一行
                '.BackColor1 = Color.AliceBlue'单行背景色
                '.BackColor2 = Color.Azure'双行背景色

                .Tree.Row = 2
                .Tree.Col = ColNames.Description
                .Tree.ShowLines = True
                .Tree.CheckBoxes = False
                .Tree.Nodes.Clear()

                For col As Integer = 0 To ColNames.Max - 1
                    Select Case col
                        Case ColNames.Pause
                            .Cell(0, col).Text = $"P"
                        Case ColNames.Action
                            .Cell(0, col).Text = $"A"
                        Case ColNames.SaveToDb
                            .Cell(0, col).Text = $"S"
                        Case Else
                            .Cell(0, col).Text = [Enum].GetName(GetType(ColNames), col) '设置列名
                    End Select
                Next

                '设置位宽
                .Column(ColNames.Step).Width = 40
                .Column(ColNames.Label).Width = 80
                .Column(ColNames.Result).Width = 80
                .Column(ColNames.LowerLimit).Width = 80
                .Column(ColNames.UpperLimit).Width = 80
                .Column(ColNames.TimeElapsed).Width = 80
                .Column(ColNames.Pause).Width = 20
                .Column(ColNames.Action).Width = 20
                .Column(ColNames.Description).Width = 200
                .Column(ColNames.ControlType).Width = 80
                .Column(ColNames.CommandType).Width = 80
                .Column(ColNames.Command).Width = 120
                .Column(ColNames.Parameters).Width = 340
                .Column(ColNames.SaveToDb).Width = 20
                .Column(ColNames.RecordName).Width = 100
                .Column(ColNames.Retry).Width = 40
                .Column(ColNames.RetryInterval).Width = 40
                .Column(ColNames.ErrorCode).Width = 100

                '设置类型
                .Column(ColNames.Pause).CellType = CellTypeEnum.CheckBox
                .Column(ColNames.Action).CellType = CellTypeEnum.CheckBox
                .Column(ColNames.SaveToDb).CellType = CellTypeEnum.CheckBox

                .Column(ColNames.ControlType).CellType = CellTypeEnum.ComboBox
                .Column(ColNames.CommandType).CellType = CellTypeEnum.ComboBox
                .Column(ColNames.Command).CellType = CellTypeEnum.ComboBox
                .Column(ColNames.ErrorCode).CellType = CellTypeEnum.ComboBox

                .ComboBox(ColNames.ControlType).Locked = True
                .ComboBox(ColNames.CommandType).Locked = True
                .ComboBox(ColNames.Command).Locked = True
                .ComboBox(ColNames.ErrorCode).Locked = True

                '格栅颜色
                .GridColor = Color.WhiteSmoke

                '可见度
                UpdateColVisible()

                '对齐方式
                UpdateColAlignment()

                UnLockGridAutoRedraw()
            End With

            Dim s As Integer = _grd.DefaultRowHeight
            Console.WriteLine(s)
        End Sub



        Private Sub UpdateColAlignment()
            With _grd
                .Column(ColNames.Step).Alignment = AlignmentEnum.CenterCenter
                .Column(ColNames.Label).Alignment = AlignmentEnum.RightCenter
                .Column(ColNames.Pause).Alignment = AlignmentEnum.CenterCenter
                .Column(ColNames.Action).Alignment = AlignmentEnum.CenterCenter
                .Column(ColNames.Description).Alignment = AlignmentEnum.LeftCenter
                .Column(ColNames.ControlType).Alignment = AlignmentEnum.LeftCenter
                .Column(ColNames.CommandType).Alignment = AlignmentEnum.LeftCenter
                .Column(ColNames.Command).Alignment = AlignmentEnum.LeftCenter
                .Column(ColNames.Parameters).Alignment = AlignmentEnum.LeftCenter
                .Column(ColNames.SaveToDb).Alignment = AlignmentEnum.CenterCenter
                .Column(ColNames.RecordName).Alignment = AlignmentEnum.LeftCenter
                .Column(ColNames.Retry).Alignment = AlignmentEnum.RightCenter
                .Column(ColNames.RetryInterval).Alignment = AlignmentEnum.RightCenter
                .Column(ColNames.ErrorCode).Alignment = AlignmentEnum.RightCenter
            End With
        End Sub

        Private Sub UpdateColVisible()
            With _grd
                .Column(ColNames.Pause).Visible = _debugMode

                .Column(ColNames.Result).Visible = _debugMode
                .Column(ColNames.LowerLimit).Visible = _debugMode
                .Column(ColNames.UpperLimit).Visible = _debugMode
                .Column(ColNames.TimeElapsed).Visible = _debugMode
            End With
        End Sub

        Private Function ParamsToString(params As List(Of TestCmdParam)) As String
            Return String.Join($" {_paramChar} ", params.Select(Function(p) p.Value))
        End Function

        Public Sub UpdateGrid2(node As RowNode)
            Dim row As Integer = node.RowListIndex
            Dim rowNode As RowNode = _headNode.RowList(row)
            rowNode.CopyFrom(node)

            _uploading = True
            UpdateGrid(_grd, row, rowNode)
            _uploading = False

            '触发事件
            If SkipEvent Then Return
            Dim event2 As New PlanNodeSelectChangedEventArgs With {.Node = node,
                                                                   .LineNumber = row,
                                                                   .LineActionEn = node.Action
                                                                    }

            RaiseEvent PlanNodeSelectChanged(_grd, event2)
        End Sub

        Public Sub UpdateGrid(grd As Grid, row As Integer, node As RowNode)
            Dim rowCmdType As String
            Dim tmpIsAction As Boolean
            Dim tmpColor As Color
            With grd
                .Cell(row, ColNames.Pause).Text = IIf(node.Pause, "1", "0").ToString()
                .Cell(row, ColNames.Action).Text = IIf(node.Action, "1", "0").ToString()
                .Cell(row, ColNames.Label).Text = $"{node.Label}"
                .Cell(row, ColNames.Description).Text = $"{node.Description}"
                .Cell(row, ColNames.ControlType).Text = node.ControlType
                .Cell(row, ColNames.CommandType).Text = $"{node.CommandType}"
                .Cell(row, ColNames.Command).Text = $"{node.Command}"
                .Cell(row, ColNames.Parameters).Text = $"{ParamsToString(node.Parameters)}"
                .Cell(row, ColNames.SaveToDb).Text = $"{node.SaveToDb}"
                .Cell(row, ColNames.RecordName).Text = $"{node.RecordName}"
                .Cell(row, ColNames.ErrorCode).Text = $"{node.ErrorCode}"
                .Cell(row, ColNames.ErrorMessage).Text = $"{node.ErrorMessage}"

                If node.RowType <> RowNode.RowTypeEnum.FixedModule Then
                    .Cell(row, ColNames.Retry).Text = $"{node.Retry}"
                    .Cell(row, ColNames.RetryInterval).Text = $"{node.RetryInterval}"
                Else
                    .Cell(row, ColNames.Retry).Text = String.Empty
                    .Cell(row, ColNames.RetryInterval).Text = String.Empty
                End If


                rowCmdType = .Cell(row, ColNames.CommandType).Text
                tmpIsAction = .Cell(row, ColNames.Action).BooleanValue
                'tmpColor = setRowTextForeColor(rowCmdType, tmpIsAction, node.RowType)
                tmpColor = GetRowNodeForeColor(node)

                If node.RowType = RowNode.RowTypeEnum.FixedModule Then
                    .Range(row, ColNames.Result, row, ColNames.ErrorMessage).FontSize = 10
                    .Range(row, ColNames.Result, row, ColNames.ErrorMessage).BackColor = Color.PeachPuff
                    .Range(row, ColNames.Result, row, ColNames.ErrorMessage).ForeColor = Color.Blue
                    .Range(row, ColNames.Result, row, ColNames.ErrorMessage).FontBold = True
                    .Range(row, ColNames.Result, row, ColNames.ErrorMessage).Locked = True

                ElseIf node.RowType = RowNode.RowTypeEnum.Module Then
                    If .Cell(row, ColNames.Action).BooleanValue = True Then
                        .Range(row, ColNames.Result, row, ColNames.ErrorMessage).ForeColor = Color.DeepPink
                    Else
                        .Range(row, ColNames.Result, row, ColNames.ErrorMessage).ForeColor = Color.DarkGray
                    End If
                    .Range(row, ColNames.Result, row, ColNames.ErrorMessage).FontSize = 8
                    .Range(row, ColNames.Result, row, ColNames.ErrorMessage).FontBold = False
                    .Range(row, ColNames.Result, row, ColNames.ErrorMessage).Locked = False
                Else
                    .Range(row, ColNames.Result, row, ColNames.ErrorMessage).ForeColor = tmpColor
                    .Range(row, ColNames.Result, row, ColNames.ErrorMessage).Locked = False
                End If
            End With

        End Sub


        Public Sub UpdateGrid(grd As Grid, nodes As RowNodeCollection)
            For Each node As RowNode In nodes

                grd.AddItem(node.RowLever, node.Description, String.Empty)

                Dim row As Integer = grd.Rows - 1
                UpdateGrid(grd, row, node)

                If node.RowNodes.Count > 0 Then
                    UpdateGrid(grd, node.RowNodes)
                End If
            Next
        End Sub

        ''' <summary>
        ''' 加载测试文件后更新表格
        ''' </summary>
        ''' <remarks></remarks>
        Public Sub UpdateGrid()
            _uploading = True

            InitializeGrid()

            With _grd
                LockGridAutoRedraw()

                UpdateGrid(_grd, _headNode.RowNodes)
                ReflushLabelNames()
                ReflushRecordNames()

                UnLockGridAutoRedraw()
            End With

            _uploading = False

        End Sub

#Region "表格事件"

        Private Sub Grid_Click(ByVal sender As Object, ByVal e As EventArgs)
            If _grd.EditorVisible Then Return '处于编辑状态

            Dim point As Point = _grd.PointToClient(Windows.Forms.Cursor.Position)
            Dim cel As Cell = _grd.HitTest(point.X, point.Y)
            If cel Is Nothing Then Return
            If cel.Row < _drawStartRow OrElse cel.Col <> _drawCol Then Return

            If _headNode Is Nothing Then Return
            Dim node As RowNode = _headNode.RowList(cel.Row - _drawStartRow + 1)
            If node Is Nothing Then Return

            Dim intWidth As Integer = CInt(IIf(_showNodeIcon, 20, 10))
            Dim rect As New Rectangle(cel.Bounds.Left + 2 + node.RowLever * intWidth, cel.Bounds.Top + ((cel.Bounds.Height - 9) \ 2), 8, 8)
            If rect.Contains(point) = False Then Return

            If node.Expanded Then
                node.Collapse()

                LockGridAutoRedraw()

                For i As Integer = 1 To node.AllChildCount
                    _grd.Row(node.RowListIndex + i).Visible = False
                Next

                UnLockGridAutoRedraw()
            Else
                node.Expand()

                LockGridAutoRedraw()

                ExpandRows(node)

                UnLockGridAutoRedraw()
            End If

        End Sub

        ''' <summary>
        ''' 展开行节点
        ''' </summary>
        ''' <param name="node"></param>
        Public Sub ExpandRows(node As RowNode)
            For Each child As RowNode In node.Children
                _grd.Row(child.RowListIndex).Visible = True

                '如果子节点上一次为收缩的，则展开依旧为收缩的
                If child.Children.Count <= 0 Then Continue For

                If child.Expanded Then ExpandRows(child)
            Next
        End Sub


        Private Sub CommandChanged(node As RowNode)
            _grd.Cell(node.RowListIndex, ColNames.Action).Text = IIf(node.Action, "1", "0").ToString
            _grd.Cell(node.RowListIndex, ColNames.Parameters).Text = ParamsToString(node.Parameters)
        End Sub

        Private Sub CommandTypeChanged(node As RowNode)
            _grd.Cell(node.RowListIndex, ColNames.Action).Text = IIf(node.Action, "1", "0").ToString
            _grd.Cell(node.RowListIndex, ColNames.Command).Text = node.Command
            _grd.Cell(node.RowListIndex, ColNames.Parameters).Text = ParamsToString(node.Parameters)
        End Sub

        Private Sub ErrCodeChanged(node As RowNode)
            _grd.Cell(node.RowListIndex, ColNames.ErrorMessage).Text = node.ErrorMessage
        End Sub

        Private Sub UpdateNode_ActionChanged(node As RowNode)
            If node.IsRunning Then
                For i As Integer = 0 To node.RowNodes.Count - 1

                    node.Children(i).IsRunning = node.Children(i).Action

                    UpdateNode_ActionChanged(node.Children(i))
                Next
            Else
                For i As Integer = 0 To node.RowNodes.Count - 1

                    node.Children(i).IsRunning = node.IsRunning

                    UpdateNode_ActionChanged(node.Children(i))
                Next
            End If

        End Sub

        Private Sub Grid_CellChange(sender As Object, e As Grid.CellChangeEventArgs)
            If _uploading Then Return
            If _headNode Is Nothing Then Return

            Dim node As RowNode = _headNode.RowList(e.Row - _drawStartRow + 1)
            If node Is Nothing Then Return

            Dim changeType As RowNodeChangedEventArgs.RowNodeChangeType
            changeType = RowNodeChangedEventArgs.RowNodeChangeType.None

            Dim beforeNode As RowNode = node.Clone()
            If e.Col = ColNames.Action OrElse e.Col >= ColNames.Label Then OnUserAction(beforeNode)

            Select Case e.Col
                Case ColNames.[Pause]
                    node.[Pause] = _grd.Cell(e.Row, e.Col).BooleanValue
                    If node.[Pause] Then
                        _grd.Cell(e.Row, e.Col).BackColor = Color.Red
                    Else
                        _grd.Cell(e.Row, e.Col).BackColor = Color.White
                    End If
                Case ColNames.Action
                    node.Action = _grd.Cell(e.Row, e.Col).BooleanValue
                    changeType = RowNodeChangedEventArgs.RowNodeChangeType.Action
                    If node.RowType = RowNode.RowTypeEnum.FixedModule Then
                        _grd.Cell(e.Row, e.Col).Text = "1"
                        node.Action = _grd.Cell(e.Row, e.Col).BooleanValue
                    End If

                    NodeActionChanged(node)
                Case ColNames.Description
                    node.Description = _grd.Cell(e.Row, e.Col).Text
                    changeType = RowNodeChangedEventArgs.RowNodeChangeType.Description
                Case ColNames.[Label]
                    Dim labelName As String = _grd.Cell(e.Row, e.Col).Text
                    If IsExistLabelName(labelName) Then '标签名称冲突检测,不区分大小写
                        MsgBox($"标签名称[{labelName}]已存在于{GetLabelNameGridRow(labelName)}行，请重新输入！", MsgBoxStyle.Critical, "提示")
                        _uploading = True
                        _grd.Cell(e.Row, e.Col).Text = node.Label
                        _grd.Cell(e.Row, e.Col).SetFocus()
                        _uploading = False
                        Return
                    Else
                        If Not String.IsNullOrEmpty(node.Label) Then RemoveLabelName(node.Label) '移出之前记录标签名称

                        node.Label = labelName
                        If String.IsNullOrWhiteSpace(node.Label) Then
                            node.RowType = RowNode.RowTypeEnum.Flow
                        Else
                            node.RowType = RowNode.RowTypeEnum.Module
                            AddLabelName(node.Label, e.Row) '记录新的标签名称
                            NodeActionChanged(node)
                        End If

                        changeType = RowNodeChangedEventArgs.RowNodeChangeType.Label
                    End If
                Case ColNames.ControlType
                    node.ControlType = _grd.Cell(e.Row, e.Col).Text
                    If String.IsNullOrWhiteSpace(node.ControlType) Then
                        node.RowType = RowNode.RowTypeEnum.Flow

                        Dim cmd As New GridControlNodeDeleteCommand(Me, e.Row, node.Children)
                        CommandManager.RunCommand(cmd) '添加到撤销堆栈执行
                        RaiseEvent PlanGridCommandChanged(Nothing, Nothing)
                        'clear
                    Else
                        node.RowType = RowNode.RowTypeEnum.Control
                        Dim nodes As New List(Of RowNode)
                        Dim tmpNode As New RowNode
                        If node.ControlType = "If" Then
                            tmpNode.RowType = RowNode.RowTypeEnum.Control
                            tmpNode.ControlType = "Then"
                            tmpNode.AddNode(New RowNode)
                            tmpNode.AddNode(New RowNode)
                            nodes.Add(tmpNode)
                            nodes.Add(New RowNode) '添加空行

                            tmpNode = New RowNode
                            tmpNode.RowType = RowNode.RowTypeEnum.Control
                            tmpNode.ControlType = "ElseIf"
                            tmpNode.AddNode(New RowNode)
                            tmpNode.AddNode(New RowNode)
                            nodes.Add(tmpNode)
                            nodes.Add(New RowNode) '添加空行

                            tmpNode = New RowNode
                            tmpNode.RowType = RowNode.RowTypeEnum.Control
                            tmpNode.ControlType = "Else"
                            tmpNode.AddNode(New RowNode)
                            tmpNode.AddNode(New RowNode)
                            nodes.Add(tmpNode)
                            nodes.Add(New RowNode) '添加空行

                            Dim cmd As New GridControlNodeAddCommand(Me, e.Row, nodes)
                            CommandManager.RunCommand(cmd) '添加到撤销堆栈执行
                            RaiseEvent PlanGridCommandChanged(Nothing, Nothing)
                        ElseIf node.ControlType = "ElseIf" Then
                            nodes.Add(New RowNode)
                            nodes.Add(New RowNode)

                            Dim cmd As New GridControlNodeAddCommand(Me, e.Row, nodes)
                            CommandManager.RunCommand(cmd) '添加到撤销堆栈执行
                            RaiseEvent PlanGridCommandChanged(Nothing, Nothing)
                        End If

                    End If
                    changeType = RowNodeChangedEventArgs.RowNodeChangeType.ControlType

                Case ColNames.CommandType
                    If String.Compare(node.CommandType, _grd.Cell(e.Row, e.Col).Text) <> 0 Then

                        node.CommandType = _grd.Cell(e.Row, e.Col).Text
                        node.Command = ""
                        node.Parameters.Clear()
                        'node.Action = Not String.IsNullOrWhiteSpace(node.Command)
                        CommandTypeChanged(node)

                        changeType = RowNodeChangedEventArgs.RowNodeChangeType.CommandType
                    End If

                    NodeActionChanged(node)
                Case ColNames.Command
                    If node.Command <> _grd.Cell(e.Row, e.Col).Text Then
                        node.Command = _grd.Cell(e.Row, e.Col).Text
                        node.Parameters.Clear()
                        'node.Action = Not String.IsNullOrWhiteSpace(node.Command)

                        '拷贝所有参数到当前节点信息中
                        Dim planCommand As TestCmd = _testCmdManager.GetCommand(node.CommandType, node.Command)
                        If planCommand Is Nothing Then Return
                        For Each cmdParam As TestCmdParam In planCommand.Params
                            node.Parameters.Add(cmdParam.Clone())
                        Next

                        CommandChanged(node)
                        changeType = RowNodeChangedEventArgs.RowNodeChangeType.Command
                    End If
                    NodeActionChanged(node)

                Case ColNames.Parameters
                    Dim str() As String = _grd.Cell(e.Row, e.Col).Text.Split(New Char() {_paramChar})

                    For i As Integer = 0 To node.Parameters.Count - 1
                        If i < str.Length Then
                            node.Parameters(i).Value = str(i).Trim()
                        Else
                            node.Parameters(i).Value = String.Empty
                        End If
                    Next
                    changeType = RowNodeChangedEventArgs.RowNodeChangeType.Parameters
                Case ColNames.SaveToDb
                    node.SaveToDb = _grd.Cell(e.Row, e.Col).BooleanValue
                    changeType = RowNodeChangedEventArgs.RowNodeChangeType.SaveToDb
                    If node.RowType = RowNode.RowTypeEnum.FixedModule Then
                        _grd.Cell(e.Row, e.Col).Text = "0"
                    End If
                    ' NodeAction_Change(_grd, node.RowType, e.Row, node.Action)
                    NodeActionChanged(node)
                Case ColNames.RecordName
                    Dim recordName As String = _grd.Cell(e.Row, e.Col).Text

                    If IsExistRecordName(recordName) Then
                        MsgBox($"记录名称[{recordName}]已存在于{GetRecordNameGridRow(recordName)}行，请重新输入！", MsgBoxStyle.Critical, "提示")
                        _uploading = True
                        _grd.Cell(e.Row, e.Col).Text = node.Label
                        _grd.Cell(e.Row, e.Col).SetFocus()
                        _uploading = False
                        Return
                    Else
                        If IsExistRecordName(node.RecordName) Then RemoveRecordName(node.RecordName) '移出之前记录名称

                        node.RecordName = recordName
                        If Not String.IsNullOrWhiteSpace(recordName) Then
                            AddRecordName(node.RecordName, e.Row) '记录新的记录名称
                        End If
                        changeType = RowNodeChangedEventArgs.RowNodeChangeType.RecordName
                    End If
                Case ColNames.Retry
                    node.Retry = _grd.Cell(e.Row, e.Col).IntegerValue
                    changeType = RowNodeChangedEventArgs.RowNodeChangeType.Retry
                Case ColNames.RetryInterval
                    node.RetryInterval = _grd.Cell(e.Row, e.Col).IntegerValue
                    changeType = RowNodeChangedEventArgs.RowNodeChangeType.RetryInterval
                Case ColNames.ErrorCode
                    node.ErrorCode = _grd.Cell(e.Row, e.Col).Text
                    changeType = RowNodeChangedEventArgs.RowNodeChangeType.ErrorCode
                Case ColNames.ErrorMessage
                    node.ErrorMessage = _grd.Cell(e.Row, e.Col).Text
                    changeType = RowNodeChangedEventArgs.RowNodeChangeType.ErrorMessage
            End Select



            '触发事件
            If SkipEvent Then Return
            Select Case changeType
                Case RowNodeChangedEventArgs.RowNodeChangeType.None
                    Return
                Case Else
                    OnUserActionEnd(node)
            End Select

            StationEditStatusMonitor.StationEditStatus = StationEditStatusMonitor.StationEditStatusEnum.Changed

            Dim args As New RowNodeChangedEventArgs With {
                .Node = node,
                .BeforeNode = beforeNode,
                .ChangeType = changeType
            }
            RaiseEvent RowNodeTextChanged(sender, args)
        End Sub

        Private _lastSelction As GridSelectRange
        Private _lastTempSelction As GridSelectRange
        Private _isSelection As Boolean = False

        Private Sub Grid_SelChange(sender As Object, e As Grid.SelChangeEventArgs)
            'todo:更新详情表格
            If _headNode Is Nothing Then Return
            Dim node As RowNode = _headNode.RowList(e.FirstRow - _drawStartRow + 1)
            'Dim node As RowNode = _headNode.RowList(_grd.ActiveCell.Row - _drawStartRow + 1)
            If node Is Nothing Then Return
            Console.WriteLine($"Grid_SelChange: row:{e.FirstRow} col:{e.FirstCol} end row{e.LastRow} {e.LastCol}")
            If Not GridNavigationManager.IsRunning Then
                Dim newSelection As New GridSelectRange With {.StartRow = e.FirstRow, .StartCol = e.FirstCol, .EndRow = e.LastRow, .EndCol = e.LastCol}
                If _lastSelction IsNot Nothing AndAlso _lastTempSelction IsNot Nothing Then
                    If _lastTempSelction IsNot Nothing AndAlso _lastTempSelction.StartRow = newSelection.StartRow AndAlso _lastTempSelction.StartCol = newSelection.StartCol Then
                        _isSelection = True
                        GridNavigationManager.RemoveForward()
                        GridNavigationManager.Append(New GridNavigation(_grd, _lastSelction, newSelection))
                    Else
                        If _isSelection Then
                            GridNavigationManager.RemoveForward()
                            GridNavigationManager.Append(New GridNavigation(_grd, _lastSelction, _lastTempSelction))
                            GridNavigationManager.Append(New GridNavigation(_grd, _lastTempSelction, newSelection))
                            _lastSelction = _lastTempSelction
                            _isSelection = False
                        Else
                            GridNavigationManager.Append(New GridNavigation(_grd, _lastTempSelction, newSelection))
                            If _lastSelction Is Nothing OrElse (_lastSelction.StartRow <> _lastTempSelction.StartRow AndAlso _lastSelction.StartCol <> _lastTempSelction.StartCol) Then _lastSelction = _lastTempSelction
                        End If
                    End If
                    _lastTempSelction = newSelection
                Else
                    _lastTempSelction = newSelection
                    _lastSelction = _lastTempSelction
                End If
            End If

            ActiveNode = node


            Dim LineNumber As Integer = e.FirstRow
            Dim LineActionEn As Boolean = node.Action

            '触发事件
            If SkipEvent Then Return
            Dim event2 As New PlanNodeSelectChangedEventArgs With {.Node = node,
                                                                   .LineNumber = LineNumber,
                                                                   .LineActionEn = LineActionEn
                                                                    }

            RaiseEvent PlanNodeSelectChanged(sender, event2)
        End Sub

        Private Sub Grid_LeaveCell(sender As Object, e As Grid.LeaveCellEventArgs)
            'If Not GrdiNavigationManager.IsRunning Then
            '    Dim lastSelection As New GridSelectRange With {.StartRow = e.Row, .StartCol = e.Col, .EndRow = e.Row, .EndCol = e.Col}
            '    Dim newSelection As New GridSelectRange With {.StartRow = e.NewRow, .StartCol = e.NewCol, .EndRow = e.NewRow, .EndCol = e.NewCol}
            '    GrdiNavigationManager.Append(New GridNavigation(_grd, lastSelection, newSelection))
            '    Console.WriteLine($"GridNavigation: row:{e.NewRow} col:{e.NewCol} last row{e.Row} {e.Col}")
            'End If
        End Sub


        Private _dropCol As Integer = 0
        Private _dropRow As Integer = 0

        Private Sub Grid_ComboDropDown(sender As Object, e As Grid.ComboDropDownEventArgs)
            _dropRow = e.Row
            _dropCol = e.Col
            Select Case e.Col
                Case ColNames.Command
                    _grd.ComboBox(ColNames.Command).Items.Clear()

                    If _testCmdManager Is Nothing Then Return
                    Dim type As String = _grd.Cell(e.Row, ColNames.CommandType).Text
                    _grd.ComboBox(ColNames.Command).Items.AddRange(_testCmdManager.GetCommandNames(type))
                Case ColNames.ControlType
                    _grd.ComboBox(ColNames.ControlType).Items.Clear()
                    If ActiveNode.ParentNode.ControlType = "If" Then
                        _grd.ComboBox(ColNames.ControlType).Items.Add("Then")
                        _grd.ComboBox(ColNames.ControlType).Items.Add("ElseIf")
                        _grd.ComboBox(ColNames.ControlType).Items.Add("Else")
                    Else
                        _grd.ComboBox(ColNames.ControlType).Items.Add("")
                        _grd.ComboBox(ColNames.ControlType).Items.Add("If")
                        _grd.ComboBox(ColNames.ControlType).Items.Add("While")
                    End If
            End Select
        End Sub

        Private Sub Grid_ComboClick(sender As Object, e As Grid.ComboClickEventArgs)
            Select Case _dropCol
                Case ColNames.ErrorCode
                    Dim codeMsg As String = _grd.Cell(_dropRow, ColNames.ErrorCode).Text
                    Dim errCode As String = ErrCodeManager.CodeMsgToCode(codeMsg)
                    If String.Compare(ActiveNode.ErrorCode, errCode) <> 0 Then
                        ActiveNode.ErrorCode = errCode
                        ActiveNode.ErrorMessage = _errCodeManager(ActiveNode.ErrorCode).Msg
                        _grd.Cell(ActiveNode.RowListIndex, ColNames.ErrorCode).Text = ActiveNode.ErrorCode
                        _grd.Cell(ActiveNode.RowListIndex, ColNames.ErrorCode).BackColor = _errCodeManager(ActiveNode.ErrorCode).Color
                        _grd.Cell(ActiveNode.RowListIndex, ColNames.ErrorMessage).BackColor = _grd.Cell(ActiveNode.RowListIndex, ColNames.ErrorCode).BackColor
                        ErrCodeChanged(ActiveNode)
                    End If
            End Select
        End Sub
#End Region

#Region "表格操作"
        Public Sub SetNodeActionShowMode(action As Boolean)
            If _headNode Is Nothing Then Return

            LockGridAutoRedraw()

            For i As Integer = 0 To _headNode.RowList.Count - 1
                If _headNode.RowList(i).RowType = RowNode.RowTypeEnum.FixedModule Then
                    '_grd.Row(i).Visible = True
                    SetRowVisible(i, True)
                ElseIf _headNode.RowList(i).RowType = RowNode.RowTypeEnum.Module Then
                    If action = False Then
                        '_grd.Row(i).Visible = True
                        SetRowVisible(i, True)
                    Else
                        If _headNode.RowList(i).Action Then
                            ' _grd.Row(i).Visible = True
                            SetRowVisible(i, True)
                        Else
                            ' _grd.Row(i).Visible = False
                            SetRowVisible(i, False)
                            For j As Integer = 1 To _headNode.RowList(i).AllChildCount
                                '_grd.Row(i + j).Visible = False
                                SetRowVisible(i + j, False)
                            Next

                            i += _headNode.RowList(i).AllChildCount
                        End If
                    End If
                Else
                    If action Then
                        If _headNode.RowList(i).Action Then
                            '_grd.Row(i).Visible = True
                            SetRowVisible(i, True)
                        Else
                            '_grd.Row(i).Visible = False
                            SetRowVisible(i, False)
                        End If
                    Else
                        '_grd.Row(i).Visible = True
                        SetRowVisible(i, True)
                    End If
                End If
            Next

            UnLockGridAutoRedraw()
        End Sub

        Public Sub SetRowVisible(row As Integer, visible As Boolean)
            If visible Then
                _grd.Row(row).Height = _grd.Row(0).Height
            Else
                _grd.Row(row).Height = 0
            End If
        End Sub


        Public Sub SetNodeRecordShowMode(recode As Boolean)
            If _headNode Is Nothing Then Return

            LockGridAutoRedraw()

            For i As Integer = 0 To _headNode.RowList.Count - 1
                If _headNode.RowList(i).RowType = RowNode.RowTypeEnum.FixedModule Then
                    ' _grd.Row(i).Visible = True
                    SetRowVisible(i, True)
                ElseIf _headNode.RowList(i).RowType = RowNode.RowTypeEnum.Module Then
                    '_grd.Row(i).Visible = Not recode
                    SetRowVisible(i, Not recode)
                Else
                    If recode Then
                        If _headNode.RowList(i).SaveToDb Then
                            '_grd.Row(i).Visible = True
                            SetRowVisible(i, True)
                        Else
                            '_grd.Row(i).Visible = False
                            SetRowVisible(i, False)
                        End If
                    Else
                        '_grd.Row(i).Visible = True
                        SetRowVisible(i, True)
                    End If
                End If
            Next

            UnLockGridAutoRedraw()

        End Sub

        Public Sub SetNodeExpand(expand As Boolean)
            If _headNode Is Nothing Then Return

            LockGridAutoRedraw()

            For i As Integer = 1 To _headNode.RowList.Count - 1
                If _headNode.RowList(i).RowType = RowNode.RowTypeEnum.FixedModule Then
                    ' _grd.Row(i).Visible = True
                    SetRowVisible(i, True)

                ElseIf _headNode.RowList(i).RowType = RowNode.RowTypeEnum.Module Then
                    _headNode.RowList(i).Expanded = expand

                    For Each node As RowNode In _headNode.RowList(i).Children
                        '_grd.Row(node.RowListIndex).Visible = expand
                        SetRowVisible(node.RowListIndex, expand)
                    Next
                Else
                    If expand Then
                        '_grd.Row(i).Visible = True
                        SetRowVisible(i, True)
                    Else
                        If String.IsNullOrEmpty(_headNode.RowList(i).Description) Then
                            '_grd.Row(i).Visible = expand
                            SetRowVisible(i, expand)
                        End If
                    End If
                End If
            Next

            UnLockGridAutoRedraw()
        End Sub


        Public Sub NodesAdd(childNode As RowNode)
            If _grd.ActiveCell Is Nothing Then Return
            If _headNode Is Nothing Then Return

            Dim node As RowNode = _headNode.RowList(_grd.ActiveCell.Row - _drawStartRow + 1)
            If node Is Nothing Then Return

            '更新内存
            node.AddNode(childNode)

            '更新控件
            NodesAdd2(childNode)
        End Sub


        Public Sub NodesAdd2(node As RowNode)
            If node.RowListIndex = _grd.Rows Then
                _grd.AddItem("")
            Else
                _grd.InsertRow(node.RowListIndex, 1)
            End If
            UpdateGrid(_grd, node.RowListIndex, node)

            If node.Children.Count > 0 Then
                For Each rowNode As RowNode In node.Children
                    NodesAdd2(rowNode)
                Next
            End If
        End Sub


        Public Sub NodeAdd(startMoveRow As Integer, rows As Integer)

            If rows < 1 Then Return
            If _headNode Is Nothing Then Return

            Dim cmd As New GridNodeAddCommand(Me, startMoveRow, rows)
            CommandManager.RunCommand(cmd) '添加到撤销堆栈执行
            RaiseEvent PlanGridCommandChanged(Nothing, Nothing)


        End Sub

        ''' <summary>
        ''' 子级节点添加
        ''' </summary>
        ''' <param name="startMoveRow"></param>
        ''' <param name="nodes"></param>
        Friend Sub NodeAddChildCommand(startMoveRow As Integer, nodes As List(Of RowNode))
            Dim node As RowNode
            Dim startNode As RowNode = _headNode.RowList(startMoveRow - _drawStartRow + 1)
            Dim grdNode As FlexCell.Node = _grd.Tree.FindNode(startMoveRow)
            LockGridAutoRedraw()
            _uploading = True

            For i As Integer = 0 To nodes.Count - 1
                '更新内存
                node = nodes(i)

                '在当前节点插入节点
                startNode.AddNode(node)

                '更新控件
                grdNode.Nodes.Add("", "")
                UpdateGrid(_grd, node.RowListIndex, node)

                Dim pNode As FlexCell.Node = _grd.Tree.FindNode(node.RowListIndex)
                AddGridTreeNode(pNode, node)
            Next

            _uploading = False
            UnLockGridAutoRedraw()
        End Sub

        Friend Sub NodeDeleteChildCommand(startMoveRow As Integer, nodes As List(Of RowNode))
            LockGridAutoRedraw()
            _uploading = True

            Dim node As RowNode
            Dim grdNode As FlexCell.Node
            For i As Integer = 0 To nodes.Count - 1
                node = nodes(i) '前面节点已删除，所以每次都是固定位置
                If node Is Nothing Then Exit For

                '更新控件
                grdNode = _grd.Tree.FindNode(node.RowListIndex)
                grdNode.Remove()

                '更新内存
                If node.CanDelete = False Then Exit For
                node.Remove()

                '其他操作
                StationEditStatusMonitor.StationEditStatus = StationEditStatusMonitor.StationEditStatusEnum.Changed
            Next

            _uploading = False
            UnLockGridAutoRedraw()
        End Sub


        ''' <summary>
        ''' 同级节点添加
        ''' </summary>
        ''' <param name="startMoveRow"></param>
        ''' <param name="rows"></param>
        ''' <param name="nodes"></param>
        Friend Sub NodeAddCommand(startMoveRow As Integer, rows As Integer, Optional nodes As List(Of RowNode) = Nothing)
            Dim node As RowNode
            Dim startNode As RowNode = _headNode.RowList(startMoveRow - _drawStartRow + 1)
            Dim row As Integer = startMoveRow + rows
            Dim grdNode As FlexCell.Node = _grd.Tree.FindNode(startMoveRow)
            LockGridAutoRedraw()
            _uploading = True

            For i As Integer = 1 To rows
                '更新内存
                If nodes Is Nothing OrElse nodes.Count < i Then
                    node = New RowNode With {.RowType = RowNode.RowTypeEnum.Flow}
                Else
                    node = nodes(i - 1)
                End If

                '在当前节点后插入节点
                startNode.ParentNode.InsertNode(startNode.RowIndex, node)

                '更新控件
                grdNode.Parent.Nodes.Insert(grdNode.Index, "")
                UpdateGrid(_grd, node.RowListIndex, node)

                Dim pNode As FlexCell.Node = _grd.Tree.FindNode(node.RowListIndex)
                AddGridTreeNode(pNode, node)
            Next

            '其他操作
            _grd.Cell(row, _drawCol).SetFocus()

            _uploading = False
            UnLockGridAutoRedraw()
        End Sub


        Public Sub NodeDel(startMoveRow As Integer, rows As Integer)
            If rows < 1 Then Return
            If _headNode Is Nothing Then Return
            Dim nodes As New List(Of RowNode)
            Dim node As RowNode
            Dim startNode As RowNode = _headNode.RowList(startMoveRow - _drawStartRow + 1)
            For i As Integer = 1 To rows
                node = _headNode.RowList(startMoveRow - _drawStartRow + i)
                If Not node.CanDelete Then
                    MsgBox("当前级别节点不允许删除") '过滤非删除节点
                    Return
                End If

                If startNode.RowLever <> node.RowLever Then
                    MsgBox("请选择同一级别节点再尝试此操作") '非同级别节点不移动
                    Return
                End If
                nodes.Add(node)
            Next

            Dim cmd As New GridNodeDeleteCommand(Me, startMoveRow, rows, nodes)
            CommandManager.RunCommand(cmd) '添加到撤销堆栈执行
            RaiseEvent PlanGridCommandChanged(Nothing, Nothing)

        End Sub

        Friend Sub NodeDeleteCommand(startMoveRow As Integer, rows As Integer)
            LockGridAutoRedraw()
            _uploading = True

            Dim node As RowNode
            Dim grdNode As FlexCell.Node
            Dim row As Integer = startMoveRow - 1
            For i As Integer = 1 To rows
                node = _headNode.RowList(startMoveRow - _drawStartRow + 1) '前面节点已删除，所以每次都是固定位置
                If node Is Nothing Then Exit For

                '更新控件
                grdNode = _grd.Tree.FindNode(node.RowListIndex)
                grdNode.Remove()

                '更新内存
                If node.CanDelete = False Then Exit For
                node.Remove()

                '其他操作
                StationEditStatusMonitor.StationEditStatus = StationEditStatusMonitor.StationEditStatusEnum.Changed
            Next
            _grd.Cell(row, _drawCol).SetFocus()

            _uploading = False
            UnLockGridAutoRedraw()
        End Sub

        Public Sub SetFocus(row As Integer, col As Integer)
            If row >= _grd.Rows Then row = 0
            If col >= _grd.Cols Then col = 0

            _grd.Cell(row, col).SetFocus()

        End Sub

        Public Sub SetRowFocus(row As Integer)
            If row >= _grd.Rows Then row = 0

            _grd.Range(row, 0, row, _grd.Cols - 1).SelectCells()
        End Sub

        Public Sub NodeClear()
            If _grd.ActiveCell Is Nothing OrElse _grd.Tree.SelectedNode Is Nothing Then Return

            If _headNode Is Nothing Then Return
            Dim node As RowNode = _headNode.RowList(_grd.ActiveCell.Row - _drawStartRow + 1)
            If node Is Nothing Then Return

            '更新内存
            Dim nodeCount As Integer = node.AllChildCount
            node.Clear()

            '更新控件
            If nodeCount > 0 Then
                _grd.Tree.SelectedNode.Nodes.Clear()
            End If

            '其他操作
            StationEditStatusMonitor.StationEditStatus = StationEditStatusMonitor.StationEditStatusEnum.Changed
        End Sub


        Public Sub NodeMoveUp(startMoveRow As Integer, moveRows As Integer)
            If _headNode Is Nothing Then Return

            Dim startNode As RowNode = _headNode.RowList(startMoveRow - _drawStartRow + 1)
            Dim prevNode As RowNode = startNode.PrevNode
            If prevNode Is Nothing Then Return
            If Not startNode.CanMove Then Return

            Dim node As RowNode
            For i As Integer = 2 To moveRows
                node = _headNode.RowList(startMoveRow - _drawStartRow + i)
                If startNode.RowLever <> node.RowLever Then
                    MsgBox("请选择同一级别节点再尝试此操作") '非同级别节点不移动
                    Return
                End If

                If Not node.CanMove Then
                    MsgBox("当前选择节点不允许移动")
                    Return
                End If
            Next


            Dim moveEndRow As Integer = prevNode.RowListIndex
            Dim cmd As New GridNodeMoveUpCommand(Me, startMoveRow, moveRows, moveEndRow)
            CommandManager.RunCommand(cmd) '添加到撤销堆栈执行
            RaiseEvent PlanGridCommandChanged(Nothing, Nothing)

        End Sub

        Public Sub NodeMoveDown(startMoveRow As Integer, moveRows As Integer)
            If _headNode Is Nothing Then Return

            Dim startNode As RowNode = _headNode.RowList(startMoveRow - _drawStartRow + 1)
            Dim endNode As RowNode = _headNode.RowList(startMoveRow + moveRows - _drawStartRow)
            Dim nextNode As RowNode = endNode.NextNode
            If nextNode Is Nothing Then Return
            If Not startNode.CanMove Then Return

            Dim node As RowNode
            For i As Integer = 2 To moveRows
                node = _headNode.RowList(startMoveRow - _drawStartRow + i)
                If startNode.RowLever <> node.RowLever Then
                    MsgBox("请选择同一级别节点再尝试此操作") '非同级别节点不移动
                    Return
                End If

                If Not node.CanMove Then
                    MsgBox("当前选择节点不允许移动")
                    Return
                End If
            Next

            Dim moveEndRow As Integer = startMoveRow + nextNode.AllChildCount + 1
            Dim cmd As New GridNodeMoveDownCommand(Me, startMoveRow, moveRows, moveEndRow)
            CommandManager.RunCommand(cmd) '添加到撤销堆栈执行
            RaiseEvent PlanGridCommandChanged(Nothing, Nothing)

        End Sub


        Friend Sub NodeMoveUpCommand(startMoveRow As Integer, moveRows As Integer)
            Dim startNode As RowNode = _headNode.RowList(startMoveRow - _drawStartRow + 1)
            Dim prevNode As RowNode = startNode.PrevNode

            Dim endNode As RowNode = _headNode.RowList(startMoveRow + moveRows - _drawStartRow)
            Dim nextNode As RowNode = endNode.NextNode

            Dim node As RowNode = startNode
            Dim moveUpRows As Integer = node.AllChildCount + 1

            '获取起始节点信息
            Dim lstMoveNodes As New List(Of RowNode)
            Dim lstFixNodes As New List(Of RowNode)
            Dim isMoveUp As Boolean = True
            lstMoveNodes.Add(node)
            For i As Integer = 2 To moveRows
                node = _headNode.RowList(startMoveRow - _drawStartRow + i)
                If node.RowLever < node.RowLever Then Exit For '高于起始节点级别不移动
                If node.RowLever > node.RowLever Then Continue For '低于起始节点级别认为是子节点不记录

                moveUpRows += node.AllChildCount + 1
                lstMoveNodes.Add(node)
            Next

            '如果需要上移的节点过多则修改为上一节点下移操作
            lstFixNodes.Add(prevNode)
            isMoveUp = moveUpRows < prevNode.AllChildCount

            If isMoveUp Then
                NodeMoveUp(lstFixNodes, lstMoveNodes)
            Else
                NodeMoveDown(lstFixNodes, lstMoveNodes)
            End If
        End Sub


        Friend Sub NodeMoveDownCommand(startMoveRow As Integer, moveRows As Integer)
            Dim endNode As RowNode = _headNode.RowList(startMoveRow + moveRows - _drawStartRow)
            Dim nextNode As RowNode = endNode.NextNode

            Dim node As RowNode = endNode
            Dim moveDownRows As Integer = 0

            '获取起始节点信息
            Dim lstMoveNodes As New List(Of RowNode)
            Dim lstFixNodes As New List(Of RowNode)
            Dim isMoveDown As Boolean = True
            For i As Integer = 1 To moveRows
                node = _headNode.RowList(startMoveRow - _drawStartRow + i)
                If node.RowLever < node.RowLever Then Exit For '高于起始节点级别不移动
                If node.RowLever > node.RowLever Then Continue For '低于起始节点级别认为是子节点不记录

                moveDownRows += node.AllChildCount + 1
                lstMoveNodes.Add(node)
            Next

            '如果需要上移的节点过多则修改为上一节点下移操作
            lstFixNodes.Add(nextNode)
            isMoveDown = moveDownRows < nextNode.AllChildCount

            If isMoveDown Then
                NodeMoveDown(lstMoveNodes, lstFixNodes)
            Else
                NodeMoveUp(lstMoveNodes, lstFixNodes)
            End If
        End Sub



        Private Sub NodeMoveDown(upNode As List(Of RowNode), downNode As List(Of RowNode))
            LockGridAutoRedraw()
            _uploading = True

            Dim grdNode As FlexCell.Node
            Dim grdParentNode As FlexCell.Node
            Dim preNode As RowNode = downNode(downNode.Count - 1)
            For Each node As RowNode In upNode
                grdNode = _grd.Tree.FindNode(node.RowListIndex)
                grdParentNode = grdNode.Parent

                node.Remove()
                preNode.ParentNode.InsertNode(preNode.RowIndex + 1, node)

                grdNode.Remove()
                grdParentNode.Nodes.Insert(node.RowIndex, String.Empty)
                UpdateGrid(_grd, node.RowListIndex, node)

                '获取表格节点，添加其子节点
                Dim pNode As FlexCell.Node = _grd.Tree.FindNode(node.RowListIndex)
                AddGridTreeNode(pNode, node)

                preNode = node
            Next

            _grd.Range(downNode(0).RowListIndex, 0, downNode(0).RowListIndex + downNode.Count - 1, _grd.Cols - 1).SelectCells() '设置焦点

            '节点修改
            StationEditStatusMonitor.StationEditStatus = StationEditStatusMonitor.StationEditStatusEnum.Changed

            _uploading = False
            UnLockGridAutoRedraw()
        End Sub

        Private Sub NodeMoveUp(upNode As List(Of RowNode), downNode As List(Of RowNode))
            LockGridAutoRedraw()
            _uploading = True

            Dim grdNode As FlexCell.Node
            Dim grdParentNode As FlexCell.Node
            Dim nextNode As RowNode = upNode(0)
            For Each node As RowNode In downNode
                grdNode = _grd.Tree.FindNode(node.RowListIndex)
                grdParentNode = grdNode.Parent

                node.Remove()
                nextNode.ParentNode.InsertNode(nextNode.RowIndex, node)

                grdNode.Remove()
                grdParentNode.Nodes.Insert(node.RowIndex, String.Empty)
                UpdateGrid(_grd, node.RowListIndex, node)

                '获取表格节点，添加其子节点
                Dim pNode As FlexCell.Node = _grd.Tree.FindNode(node.RowListIndex)
                AddGridTreeNode(pNode, node)

                nextNode = node
            Next

            _grd.Range(upNode(0).RowListIndex, 0, upNode(0).RowListIndex + upNode.Count - 1, _grd.Cols - 1).SelectCells() '设置焦点

            '节点修改
            StationEditStatusMonitor.StationEditStatus = StationEditStatusMonitor.StationEditStatusEnum.Changed

            _uploading = False
            UnLockGridAutoRedraw()
        End Sub

        Private Sub AddGridTreeNode(pNode As FlexCell.Node, srcNode As RowNode)
            For Each node As RowNode In srcNode.RowNodes
                pNode.Nodes.Insert(node.RowIndex, "")
                UpdateGrid(_grd, node.RowListIndex, node)

                AddGridTreeNode(_grd.Tree.FindNode(node.RowListIndex), node)
            Next
        End Sub


        ''' <summary>
        ''' 节点升级
        ''' </summary>
        Public Sub NodeMoveLeft(startMoveRow As Integer, moveRows As Integer)
            If _headNode Is Nothing Then Return

            Dim startNode As RowNode = _headNode.RowList(startMoveRow - _drawStartRow + 1)
            If startNode.RowLever <= 1 Then Return

            Dim startNodeIndex As Integer = startNode.RowIndex
            Dim node As RowNode
            Dim moveEndRow As Integer = startNode.ParentNode.RowListIndex + startNode.ParentNode.AllChildCount + 1
            For i As Integer = 1 To moveRows
                node = _headNode.RowList(startMoveRow - _drawStartRow + i)
                If startNode.RowLever <> node.RowLever Then
                    MsgBox("请选择同一级别节点再尝试此操作") '非同级别节点不移动
                    Return
                End If

                If Not node.CanChangeLever Then
                    MsgBox("当前选择节点不允许修改级别")
                    Return
                End If

                moveEndRow -= (node.AllChildCount + 1)
            Next

            Dim cmd As New GridNodeMoveLeftCommand(Me, startMoveRow, moveRows, moveEndRow, startNodeIndex)
            CommandManager.RunCommand(cmd) '添加到撤销堆栈执行
            RaiseEvent PlanGridCommandChanged(Nothing, Nothing)

        End Sub


        Public Sub NodeMoveLeftCommand(startMoveRow As Integer, moveRows As Integer)
            Dim node As RowNode
            Dim grdNode As FlexCell.Node
            Dim grdParentNode As FlexCell.Node
            LockGridAutoRedraw()
            _uploading = True

            Dim parentNode As RowNode = _headNode.RowList(startMoveRow - _drawStartRow + 1).ParentNode
            For i As Integer = moveRows To 1 Step -1
                node = _headNode.RowList(startMoveRow - _drawStartRow + i)

                If node Is Nothing Then Exit For
                If node.RowLever = 0 Then Exit For
                If node.RowType = RowNode.RowTypeEnum.FixedModule Then Exit For
                grdNode = _grd.Tree.FindNode(node.RowListIndex)
                grdParentNode = grdNode.Parent

                node.MoveLeft()

                grdNode.Remove()
                grdParentNode.Parent.Nodes.Insert(grdParentNode.Index + 1, "")
                UpdateGrid(_grd, node.RowListIndex, node)
                AddGridTreeNode(_grd.Tree.FindNode(node.RowListIndex), node)
                StationEditStatusMonitor.StationEditStatus = StationEditStatusMonitor.StationEditStatusEnum.Changed
            Next
            If parentNode.Children.Count = 0 Then
                parentNode.RowType = RowNode.RowTypeEnum.Flow
                NodeRowTypeChanged(parentNode)
            End If

            _uploading = False
            UnLockGridAutoRedraw()
        End Sub


        ''' <summary>
        ''' 节点降级
        ''' </summary>
        Public Sub NodeMoveRight(startMoveRow As Integer, moveRows As Integer)
            If _headNode Is Nothing Then Return

            Dim startNode As RowNode = _headNode.RowList(startMoveRow - _drawStartRow + 1)
            If startNode.RowLever < 1 Then Return '固定节点不移动
            If startNode.PrevNode Is Nothing Then Return '没有上一级节点

            Dim node As RowNode
            For i As Integer = 2 To moveRows
                node = _headNode.RowList(startMoveRow - _drawStartRow + i)
                If startNode.RowLever <> node.RowLever Then
                    MsgBox("请选择同一级别节点再尝试此操作") '非同级别节点不移动
                    Return
                End If

                If Not node.CanChangeLever Then
                    MsgBox("当前选择节点不允许修改级别")
                    Return
                End If
            Next

            Dim moveRightNodeIndex As Integer = startNode.PrevNode.Children.Count
            Dim moveEndRow As Integer = startNode.PrevNode.RowListIndex + startNode.PrevNode.AllChildCount + 1

            Dim cmd As New GridNodeMoveRightCommand(Me, startMoveRow, moveRows, moveEndRow, moveRightNodeIndex)
            CommandManager.RunCommand(cmd) '添加到撤销堆栈执行
            RaiseEvent PlanGridCommandChanged(Nothing, Nothing)

        End Sub


        Public Sub NodeMoveRightCommand(startMoveRow As Integer, moveRows As Integer, nodeIndex As Integer)
            Dim node As RowNode = _headNode.RowList(startMoveRow - _drawStartRow + 1)
            Dim parentNode As RowNode = node.PrevNode
            Dim grdNode As FlexCell.Node = _grd.Tree.FindNode(node.RowListIndex)
            Dim grdPreNode As FlexCell.Node = grdNode.PrevNode

            LockGridAutoRedraw()
            _uploading = True

            For i As Integer = 1 To moveRows
                node = _headNode.RowList(startMoveRow - _drawStartRow + i)
                If node Is Nothing Then Exit For
                If parentNode.RowLever > node.RowLever Then Continue For
                If parentNode.RowLever < node.RowLever Then Exit For

                grdNode = _grd.Tree.FindNode(node.RowListIndex)

                '  node.MoveRight()
                node.Remove()
                parentNode.InsertNode(nodeIndex + i - 1, node)

                grdNode.Remove()
                grdPreNode.Nodes.Insert(nodeIndex + i - 1, "")

                UpdateGrid(_grd, node.RowListIndex, node)
                AddGridTreeNode(_grd.Tree.FindNode(node.RowListIndex), node)
                StationEditStatusMonitor.StationEditStatus = StationEditStatusMonitor.StationEditStatusEnum.Changed
            Next

            If parentNode.RowType = RowNode.RowTypeEnum.Flow Then
                parentNode.RowType = RowNode.RowTypeEnum.Module
                NodeRowTypeChanged(parentNode)
            End If

            _uploading = False
            UnLockGridAutoRedraw()
        End Sub

#End Region

#Region "外部事件"
        Public Sub Grid_RowNodeTextChanged(sender As Object, e As RowNodeChangedEventArgs)
            Dim row As Integer = e.Node.RowListIndex
            Dim node As RowNode = e.Node

            _uploading = True
            With _grd
                Select Case e.ChangeType
                    Case RowNodeChangedEventArgs.RowNodeChangeType.Action
                        .Cell(row, ColNames.Action).Text = IIf(node.Action, "1", "0").ToString()
                        NodeActionChanged(node)
                    Case RowNodeChangedEventArgs.RowNodeChangeType.RowType
                        NodeRowTypeChanged(node)
                    Case RowNodeChangedEventArgs.RowNodeChangeType.Label
                        .Cell(row, ColNames.Label).Text = $"{node.Label}"
                    Case RowNodeChangedEventArgs.RowNodeChangeType.ControlType
                        .Cell(row, ColNames.ControlType).Text = $"{node.ControlType}"
                    Case RowNodeChangedEventArgs.RowNodeChangeType.Description
                        .Cell(row, ColNames.Description).Text = $"{node.Description}"
                    Case RowNodeChangedEventArgs.RowNodeChangeType.SaveToDb
                        .Cell(row, ColNames.SaveToDb).Text = IIf(node.SaveToDb, "1", "0").ToString()
                    Case RowNodeChangedEventArgs.RowNodeChangeType.RecordName
                        .Cell(row, ColNames.RecordName).Text = $"{node.RecordName}"
                    Case RowNodeChangedEventArgs.RowNodeChangeType.Retry
                        .Cell(row, ColNames.Retry).Text = $"{node.Retry}"
                    Case RowNodeChangedEventArgs.RowNodeChangeType.RetryInterval
                        .Cell(row, ColNames.RetryInterval).Text = $"{node.RetryInterval}"
                    Case RowNodeChangedEventArgs.RowNodeChangeType.ErrorCode
                        .Cell(row, ColNames.ErrorCode).Text = $"{node.ErrorCode}"
                        If String.IsNullOrWhiteSpace(node.ErrorCode) = False Then
                            .Cell(row, ColNames.ErrorCode).BackColor = _errCodeManager(node.ErrorCode).Color
                            .Cell(row, ColNames.ErrorMessage).BackColor = .Cell(row, ColNames.ErrorCode).BackColor
                        Else
                            .Cell(row, ColNames.ErrorCode).BackColor = Color.White
                            .Cell(row, ColNames.ErrorMessage).BackColor = Color.White
                        End If
                    Case RowNodeChangedEventArgs.RowNodeChangeType.ErrorMessage
                        .Cell(row, ColNames.ErrorMessage).Text = $"{node.ErrorMessage}"
                    Case RowNodeChangedEventArgs.RowNodeChangeType.CommandType
                        If String.Compare(.Cell(row, ColNames.CommandType).Text, node.CommandType) <> 0 Then
                            .Cell(row, ColNames.CommandType).Text = $"{node.CommandType}"
                            CommandTypeChanged(node)
                            NodeActionChanged(node)
                        End If
                    Case RowNodeChangedEventArgs.RowNodeChangeType.Command
                        If String.Compare(.Cell(row, ColNames.Command).Text, node.Command) <> 0 Then
                            .Cell(row, ColNames.Command).Text = $"{node.Command}"
                            CommandChanged(node)
                            NodeActionChanged(node)
                        End If
                    Case RowNodeChangedEventArgs.RowNodeChangeType.Parameters
                        .Cell(row, ColNames.Parameters).Text = $"{ParamsToString(node.Parameters)}"
                End Select
            End With
            OnUserAction(e.BeforeNode)
            OnUserActionEnd(e.Node)

            StationEditStatusMonitor.StationEditStatus = StationEditStatusMonitor.StationEditStatusEnum.Changed

            _uploading = False
        End Sub

        Public Function CheckRecordDuplicateName() As Boolean
            Dim recodeNames As New Dictionary(Of String, Integer)
            For Each node As RowNode In _headNode.RowList
                If node.SaveToDb Then
                    If String.IsNullOrWhiteSpace(node.RecordName) Then
                        If MsgBox($"Record Name 无效！" & vbNewLine &
                                 "行号： " & node.RowListIndex & vbNewLine &
                                 "RecordName: [" & node.RecordName & "]" & vbNewLine &
                                 "是否继续操作？", MsgBoxStyle.OkCancel) = MsgBoxResult.Cancel Then Return False
                    Else
                        If recodeNames.ContainsKey(node.RecordName) Then
                            If MsgBox("Record Name 重名！" & vbNewLine &
                                  "当前行号： " & node.RowListIndex & vbNewLine &
                                  "冲突行号： " & recodeNames(node.RecordName) & vbNewLine &
                                  "RecordName: " & node.RecordName &
                                  vbNewLine & vbNewLine &
                                  "请注意，记录名不能重复，否则前面的数据将会被覆盖！是否继续操作？", MsgBoxStyle.OkCancel
                           ) = MsgBoxResult.Cancel Then Return False
                        Else
                            recodeNames.Add(node.RecordName, node.RowListIndex)
                        End If
                    End If

                End If
            Next

            Return True
        End Function

        ''' <summary>
        ''' 反显节点所在行
        ''' </summary>
        ''' <param name="node"></param>
        Public Sub TestNodeChanged(node As RowNode)
            _grd.Cell(node.RowListIndex, ColNames.Action).EnsureVisible()
            _grd.Cell(node.RowListIndex, ColNames.Action).BackColor = Color.Blue
            _grd.Range(node.RowListIndex, 1, node.RowListIndex, _grd.Cols - 1).SelectCells()
        End Sub


        Public Sub NodeCompleted(node As RowNode, testReturn As TestCommandReturn)
            Dim row As Integer = node.RowListIndex
            _grd.Cell(row, ColNames.Action).BackColor = CType(IIf(testReturn.ExecuteResult, Color.Green, Color.Red), Color)
            _grd.Cell(row, ColNames.Result).Text = testReturn.RecordValue
            _grd.Cell(row, ColNames.LowerLimit).Text = testReturn.LowerLimit
            _grd.Cell(row, ColNames.UpperLimit).Text = testReturn.UpperLimit
            _grd.Cell(row, ColNames.TimeElapsed).Text = testReturn.StepTimeSpan.TotalMilliseconds.ToString("N0")

        End Sub

        Public Sub ClearDebug()
            For i As Integer = 1 To _grd.Rows - 1
                _grd.Cell(i, ColNames.Action).BackColor = Color.White
                _grd.Cell(i, ColNames.LowerLimit).Text = ""
                _grd.Cell(i, ColNames.UpperLimit).Text = ""
                _grd.Cell(i, ColNames.TimeElapsed).Text = ""
                _grd.Cell(i, ColNames.Result).Text = ""
            Next
        End Sub
#End Region

        Public Enum ColNames
            ''' <summary>
            ''' 步骤,固定
            ''' </summary>
            [Step]

            ''' <summary>
            ''' 断点执行，固定列
            ''' </summary>
            [Pause]


            ''' <summary>
            ''' 是否运行
            ''' </summary>
            Action

            ''' <summary>
            ''' 调试结果
            ''' </summary>
            Result

            ''' <summary>
            ''' 节点下限
            ''' </summary>
            LowerLimit

            ''' <summary>
            ''' 节点上限
            ''' </summary>
            UpperLimit

            ''' <summary>
            ''' 节点测试耗时
            ''' </summary>
            TimeElapsed

            ''' <summary>
            ''' 模块名称
            ''' </summary>
            [Label]

            ''' <summary>
            ''' 节点解析
            ''' </summary>
            Description

            ''' <summary>
            ''' 节点控制执行类型
            ''' </summary>
            ControlType

            ''' <summary>
            ''' 命令类型
            ''' </summary>
            CommandType

            ''' <summary>
            ''' 命令关键字
            ''' </summary>
            Command

            ''' <summary>
            ''' 执行命令参数
            ''' </summary>
            Parameters

            ''' <summary>
            ''' 失败重试次数
            ''' </summary>
            Retry

            ''' <summary>
            ''' 重试间隔
            ''' </summary>
            RetryInterval

            ''' <summary>
            ''' 当前返回值是否记录入库
            ''' </summary>
            SaveToDb

            ''' <summary>
            ''' 返回值变量名
            ''' </summary>
            RecordName

            ''' <summary>
            ''' 错误代码
            ''' </summary>
            ErrorCode

            ''' <summary>
            ''' 错误提示
            ''' </summary>
            ErrorMessage

            Max
        End Enum
    End Class
End Namespace