From c1c3772a11b1f3fbad8846cc714120771d85fa53 Mon Sep 17 00:00:00 2001
From: alielie <alielie@git.com>
Date: 星期四, 12 六月 2025 20:10:59 +0800
Subject: [PATCH] 新增SeqEditor

---
 Plugin.SeqEditor/SettingPanel/ActionPanel.xaml.cs           |   39 
 Plugin.SeqEditor/AssemblyInfo.cs                            |   10 
 WPFProject/BuildCmd.bat                                     |   10 
 Plugin.TestResult/SingleCell.xaml                           |    8 
 Plugin.SeqEditor/SettingPanel/StringValTestPanel.xaml       |   57 +
 Plugin.SeqEditor/Util/Step2EPO.cs                           |   44 
 Plugin.SeqEditor/SettingPanel/CommonProperty.xaml.cs        |   45 +
 Plugin.TestResult/TestResultView.xaml.cs                    |    1 
 WPFProject/Lang/zh-CN/app.json                              |    4 
 HMI.UI/Controls/treegrid/TreeItemConverter.cs               |   28 
 HMI.MenuManage/RoleManage.xaml                              |    4 
 Plugin.SeqEditor/Entity/EditablePO/StringValTestEPO.cs      |   66 +
 Plugin.SeqEditor/SettingPanel/WaitPanel.xaml                |   48 +
 WPFProject/LoadingWindow.xaml.cs                            |    9 
 Plugin.SeqEditor/SettingPanel/NumValTestPanel.xaml          |   50 +
 Plugin.SeqEditor/ViewModel/SeqEditorViewModel.cs            |  454 ++++++++++
 HMI.UI/MaskWindow.xaml                                      |   49 
 Plugin.SeqEditor/SettingPanel/NumValTestPanel.xaml.cs       |   20 
 WPFProject/Lang/zh-CN/SeqEditor.json                        |   34 
 HMI.UI/Controls/treegrid/Controls/TreeItemData.cs           |    6 
 Plugin.SeqEditor/Entity/StepUI.cs                           |   91 ++
 WPFProject/App.config                                       |    2 
 HMI.UI/Controls/treegrid/Themes/TreeItemContentControl.xaml |    9 
 HMI.UI/Controls/MultiCombox.xaml.cs                         |  201 ++++
 Plugin.TsExecution/TsExecution.xaml.cs                      |   48 
 HMI.UnitTest/MainWindow.xaml.cs                             |    4 
 Plugin.SeqEditor/Constant/DefaultStepName.cs                |   12 
 Plugin.SeqEditor/Util/ItemData.cs                           |  147 +++
 Plugin.SeqEditor/SettingPanel/ActionPanel.xaml              |   45 +
 WPFProject/packages.config                                  |    1 
 HMI.UI/Controls/treegrid/Controls/TreeDataGrid.cs           |   25 
 HMI.UI/Util/MsgBoxHelper.cs                                 |    5 
 Plugin.SeqEditor/Entity/EditablePO/IEPO.cs                  |   46 +
 Plugin.SeqEditor/SeqEditor.xaml.cs                          |   70 +
 Plugin.SeqEditor/SettingPanel/CommonProperty.xaml           |   25 
 WPFProject/Lang/en-US/SeqEditor.json                        |   34 
 Plugin.SeqEditor/Entity/EditablePO/NumValTestEPO.cs         |   57 +
 HMI.UI/Style/FormStyle.xaml                                 |   11 
 WPFProject/WPFProject.csproj                                |   22 
 Plugin.SeqEditor/SettingPanel/StringValTestPanel.xaml.cs    |   21 
 HMI.UI/Style/PanuonResource.xaml                            |   38 
 HMI.UI/Controls/MultiCombox.xaml                            |   10 
 Plugin.TsExecution/SingleControl.xaml                       |   10 
 WPFProject/WPFProject.sln                                   |   10 
 Plugin.JarUI/JarUI.xaml.cs                                  |  156 +++
 Plugin.TsExecution/SingleControl.xaml.cs                    |   43 
 Plugin.SeqEditor/Entity/SeqUI.cs                            |   27 
 Plugin.SeqEditor/Plugin.SeqEditor.csproj                    |   33 
 Plugin.SeqEditor/Entity/EditablePO/WaitEPO.cs               |   37 
 Plugin.SeqEditor/Entity/VarUI.cs                            |   58 +
 Plugin.JarUI/JarUI.xaml                                     |   31 
 Plugin.SeqEditor/SettingPanel/WaitPanel.xaml.cs             |   19 
 Plugin.SeqEditor/Constant/StepTypeStr.cs                    |   24 
 Plugin.SeqEditor/SeqEditor.xaml                             |  254 +++++
 Plugin.TestResult/SingleCell.xaml.cs                        |   12 
 Plugin.SeqEditor/Util/PoExplain.cs                          |   44 
 HMI.UI/HMI.UI.csproj                                        |    8 
 57 files changed, 2,579 insertions(+), 97 deletions(-)

diff --git a/HMI.MenuManage/RoleManage.xaml b/HMI.MenuManage/RoleManage.xaml
index 39e2f3e..eda62eb 100644
--- a/HMI.MenuManage/RoleManage.xaml
+++ b/HMI.MenuManage/RoleManage.xaml
@@ -99,8 +99,8 @@
 Command="{Binding CheckCommand}"
 CommandParameter="{Binding Path=IsChecked,RelativeSource={RelativeSource Mode=Self}}"
 />
-                                <Image Width="{StaticResource ContentFontSize}" 
-                                       Height="{StaticResource ContentFontSize}" Margin="5" Source="{Binding MenuIcon,Converter={StaticResource path2Image},Mode=OneWay}"/>
+                                <!--<Image Width="{StaticResource ContentFontSize}" 
+                                       Height="{StaticResource ContentFontSize}" Margin="5" Source="{Binding MenuIcon,Converter={StaticResource path2Image},Mode=OneWay}"/>-->
                                 <TextBlock VerticalAlignment="Center" Text="{Binding MenuName}"/>
                             </StackPanel>
                         </HierarchicalDataTemplate>
diff --git a/HMI.UI/Controls/MultiCombox.xaml b/HMI.UI/Controls/MultiCombox.xaml
new file mode 100644
index 0000000..c098596
--- /dev/null
+++ b/HMI.UI/Controls/MultiCombox.xaml
@@ -0,0 +1,10 @@
+锘�<UserControl x:Class="HMI.UI.Controls.MultiCombox"
+             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
+             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
+             xmlns:local="clr-namespace:HMI.UI.Controls"
+             mc:Ignorable="d" 
+             d:DesignHeight="450" d:DesignWidth="800">
+    <ComboBox ></ComboBox>
+</UserControl>
diff --git a/HMI.UI/Controls/MultiCombox.xaml.cs b/HMI.UI/Controls/MultiCombox.xaml.cs
new file mode 100644
index 0000000..85f5bcc
--- /dev/null
+++ b/HMI.UI/Controls/MultiCombox.xaml.cs
@@ -0,0 +1,201 @@
+锘縰sing System;
+using System.Collections;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Globalization;
+using System.Linq;
+using System.Reflection;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Input;
+
+namespace HMI.UI
+{
+    public class MultiComboBoxItem : ComboBoxItem, INotifyPropertyChanged
+    {
+        #region ...
+        public event PropertyChangedEventHandler PropertyChanged;
+
+        protected void RaisePropertyChanged(string propertyName)
+        {
+            if (PropertyChanged != null)
+            {
+                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
+            }
+        }
+        #endregion
+
+        private MultiComboBox parent;
+        public MultiComboBoxItem(MultiComboBox parent)
+        {
+            this.parent = parent;
+            this.DataContextChanged += MultiComboBoxItem_DataContextChanged;
+        }
+
+        protected override void OnMouseMove(MouseEventArgs e)
+        {
+            this.PreviewMouseUp += MultiComboBoxItem_PreviewMouseUp;
+            base.OnMouseMove(e);
+        }
+
+        protected override void OnMouseLeave(MouseEventArgs e)
+        {
+            this.PreviewMouseUp -= MultiComboBoxItem_PreviewMouseUp;
+            base.OnMouseLeave(e);
+        }
+
+        protected override void OnLostFocus(RoutedEventArgs e)
+        {
+            this.PreviewMouseUp -= MultiComboBoxItem_PreviewMouseUp;
+            base.OnLostFocus(e);
+        }
+
+        private void MultiComboBoxItem_PreviewMouseUp(object sender, MouseButtonEventArgs e)
+        {
+            if (DataContext is MultiComboBoxData data)
+            {
+                data.IsChecked = !data.IsChecked;
+                e.Handled = true;
+            }
+        }
+
+        private void MultiComboBoxItem_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
+        {
+            if (e.OldValue != null && e.OldValue is MultiComboBoxData oldData)
+            {
+                oldData.IsCheckedChanged -= DataIsExpandedChange;
+            }
+            if (DataContext != null && DataContext is MultiComboBoxData newData)
+            {
+                newData.IsCheckedChanged += DataIsExpandedChange;
+            }
+        }
+
+        private void DataIsExpandedChange(object sender, bool IsExpanded)
+        {
+            parent.Item_IsCheckedChanged((MultiComboBoxData)sender, this, IsExpanded);
+        }
+    }
+
+    public class MultiComboBoxDataConverter : IValueConverter
+    {
+        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+        {
+            if (value is IEnumerable<object> items)
+            {
+                return items.Select(item => new MultiComboBoxData(item, "DisplayName", "DataValue")).ToList();
+            }
+            return null;
+        }
+
+        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+        {
+            return value;
+        }
+    }
+
+    public class MultiComboBoxData : INotifyPropertyChanged
+    {
+        #region ...
+        public event PropertyChangedEventHandler PropertyChanged;
+
+        protected void RaisePropertyChanged(string propertyName)
+        {
+            if (PropertyChanged != null)
+            {
+                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
+            }
+        }
+        #endregion
+
+        public event EventHandler<bool> IsCheckedChanged;
+
+        private bool isChecked;
+
+        public bool IsChecked
+        {
+            get { return isChecked; }
+            set
+            {
+                isChecked = value;
+                RaisePropertyChanged("IsChecked");
+                IsCheckedChanged?.Invoke(this, value);
+            }
+        }
+        public string DisplayName
+        {
+            get => showName?.GetValue(Data).ToString();
+        }
+
+        public string DataValue
+        {
+            get => valueName?.GetValue(Data).ToString();
+        }
+
+        private PropertyInfo showName;
+        private PropertyInfo valueName;
+
+        public MultiComboBoxData(object data, string showName, string valueName)
+        {
+            this.Data = data;
+            this.showName = data.GetType().GetProperty(showName);
+            this.valueName = data.GetType().GetProperty(valueName);
+        }
+
+        public object Data { get; private set; }
+    }
+
+    /// <summary>
+    /// MultiCombox.xaml 鐨勪氦浜掗�昏緫
+    /// </summary>
+    public partial class MultiComboBox : ComboBox
+    {
+        protected override DependencyObject GetContainerForItemOverride()
+        {
+            var item = new MultiComboBoxItem(this);
+            return item;
+        }
+
+        [Bindable(true)]
+        [Category("Appearance")]
+        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+        [Localizability(LocalizationCategory.NeverLocalize)]
+        public object SelectedValues
+        {
+            get
+            {
+                return GetValue(SelectedValuesProperty);
+            }
+            set
+            {
+                SetValue(SelectedValuesProperty, value);
+            }
+        }
+
+        public static readonly DependencyProperty SelectedValuesProperty =
+            DependencyProperty.RegisterAttached("SelectedValues", typeof(object), typeof(MultiComboBox),
+                new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
+
+
+        public void Item_IsCheckedChanged(MultiComboBoxData data, MultiComboBoxItem item, bool isChecked)
+        {
+            var selectedValues = SelectedValues?.ToString() ?? "";
+            var showNames = this.Text?.ToString() ?? "";
+            List<string> values = string.IsNullOrEmpty(selectedValues) ? new List<string>() : selectedValues.Split(',').ToList();
+            List<string> names = string.IsNullOrEmpty(showNames) ? new List<string>() : showNames.Split(',').ToList();
+            if (isChecked)
+            {
+                values.Add(data.DataValue);
+                names.Add(data.DisplayName);
+            }
+            else
+            {
+                values.Remove(data.DataValue);
+                names.Remove(data.DisplayName);
+            }
+            Text = string.Join(",", names);
+            SelectedValues = string.Join(",", values);
+        }
+    }
+}
diff --git a/HMI.UI/Controls/treegrid/Controls/TreeDataGrid.cs b/HMI.UI/Controls/treegrid/Controls/TreeDataGrid.cs
index fdd2866..8823656 100644
--- a/HMI.UI/Controls/treegrid/Controls/TreeDataGrid.cs
+++ b/HMI.UI/Controls/treegrid/Controls/TreeDataGrid.cs
@@ -48,21 +48,36 @@
         {
             if (e.OldValue != null && e.OldValue is TreeItemData oldTreeData)
             {
+                oldTreeData.IsExpandedChange -= DataIsExpandedChange;
                 oldTreeData.VisibleChange -= DataVisiableChange;
+
             }
             if (DataContext != null && DataContext is TreeItemData treeData)
             {
+                treeData.IsExpandedChange += DataIsExpandedChange;
                 treeData.VisibleChange += DataVisiableChange;
+            }
+        }
+
+        private void DataIsExpandedChange(object sender, bool IsExpanded)
+        {
+            object parent = ItemsControl.ItemsControlFromItemContainer(this);
+            if (sender is TreeItemData tid)
+            {
+                foreach (var data in tid.Items)
+                {
+                    data.IsVisible = IsExpanded;
+                }
             }
         }
 
         private void DataVisiableChange(object sender, bool visiable)
         {
-            object parent = ItemsControl.ItemsControlFromItemContainer(this);
-            if (parent != null && parent is TreeDataGrid treeDataGrid)
-            {
-                treeDataGrid.OnExpandedChange(visiable);
-            }
+            this.Visibility = visiable ? Visibility.Visible : Visibility.Collapsed;
+            //if (parent != null && parent is TreeDataGrid treeDataGrid)
+            //{
+            //    treeDataGrid.OnExpandedChange(visiable);
+            //}
         }
     }
 }
diff --git a/HMI.UI/Controls/treegrid/Controls/TreeItemData.cs b/HMI.UI/Controls/treegrid/Controls/TreeItemData.cs
index bec9926..46a045c 100644
--- a/HMI.UI/Controls/treegrid/Controls/TreeItemData.cs
+++ b/HMI.UI/Controls/treegrid/Controls/TreeItemData.cs
@@ -13,6 +13,7 @@
     public class TreeItemData : NotifyBase
     {
 
+        public event EventHandler<bool> IsExpandedChange;
         public event EventHandler<bool> VisibleChange;
 
         protected override void Set(string name)
@@ -38,7 +39,7 @@
                 _IsExpanded = value;
                 Set("IsExpanded");
                 SetItemsVisible(value);
-                VisibleChange?.Invoke(this, value);
+                IsExpandedChange?.Invoke(this, value);
             }
         }
         #endregion
@@ -206,7 +207,7 @@
             {
                 return _IsVisible;
             }
-            private set
+            set
             {
                 if (_IsVisible == value)
                 {
@@ -215,6 +216,7 @@
                 _IsVisible = value;
                 Set("IsVisible");
                 SetItemsVisible(value);
+                VisibleChange?.Invoke(this, value);
             }
         }
 
diff --git a/HMI.UI/Controls/treegrid/Themes/TreeItemContentControl.xaml b/HMI.UI/Controls/treegrid/Themes/TreeItemContentControl.xaml
index 741fe6b..7e13490 100644
--- a/HMI.UI/Controls/treegrid/Themes/TreeItemContentControl.xaml
+++ b/HMI.UI/Controls/treegrid/Themes/TreeItemContentControl.xaml
@@ -114,6 +114,8 @@
         <Setter Property="VerticalContentAlignment" Value="Center"/>
         <Setter Property="BorderThickness" Value="0"/>
         <Setter Property="BorderBrush" Value="LightGray"/>
+        <Setter Property="Foreground" Value="{Binding Foreground,RelativeSource={RelativeSource Mode=FindAncestor,
+AncestorType=DataGridCell,AncestorLevel=1}}"/>
         <Setter Property="Template">
             <Setter.Value>
                 <ControlTemplate TargetType="{x:Type local:TreeItemContentControl}">
@@ -145,7 +147,7 @@
                                                 <Line x:Name="line" X1="7.5" Y1="0" X2="7.5" Y2="{Binding ElementName=lineGrid,Path=ActualHeight}" StrokeThickness="1"
                                                       Stroke="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=DataGridRow},Path=Foreground}" StrokeDashArray="1,1"/>
                                             </Grid>
-                                            <!-- 杩欓噷鏀惧紑鏈変釜闂锛屼細璁╁彧鏈変竴涓牴鑺傜偣鐨勬爲鍒楄〃鏄剧ず閿欒 -->
+                                             <!--杩欓噷鏀惧紑鏈変釜闂锛屼細璁╁彧鏈変竴涓牴鑺傜偣鐨勬爲鍒楄〃鏄剧ず閿欒--> 
                                             <!--<DataTemplate.Triggers>
                                                 <DataTrigger Binding="{Binding IsLastItem}" Value="true">
                                                     <Setter Property="Visibility" Value="Collapsed"/>
@@ -156,7 +158,10 @@
                                 </ListBox>
                             </Grid>
 
-                            <ToggleButton x:Name="ExpanderButton" Grid.Column="1" Style="{StaticResource ClearToggleStyle}" HorizontalAlignment="Left"
+                            <ToggleButton x:Name="ExpanderButton" 
+                                          Cursor="Hand"
+                                          Grid.Column="1" Style="{StaticResource ClearToggleStyle}" HorizontalAlignment="Left"
+                                          
                                           IsChecked="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},Path=TreeData.IsExpanded,Mode=TwoWay}"
                                           Content="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},Path=TreeData.IconType}"
                                           ContentTemplate="{StaticResource Icon_Template}"
diff --git a/HMI.UI/Controls/treegrid/TreeItemConverter.cs b/HMI.UI/Controls/treegrid/TreeItemConverter.cs
new file mode 100644
index 0000000..bff69af
--- /dev/null
+++ b/HMI.UI/Controls/treegrid/TreeItemConverter.cs
@@ -0,0 +1,28 @@
+锘縰sing System;
+using System.Collections;
+using System.Globalization;
+using System.Windows.Data;
+
+namespace HMI.UI
+{
+    public class TreeItemConverter : IValueConverter
+    {
+        object IValueConverter.Convert(object value, Type targetType, object parameter, CultureInfo culture)
+        {
+            if (value == null || parameter == null)
+            {
+                return value;
+            }
+            if (value is IEnumerable items)
+            {
+                return new TreeItemDataCollection(items, parameter.ToString(), true);
+            }
+            return value;
+        }
+
+        object IValueConverter.ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+        {
+            return value;
+        }
+    }
+}
diff --git a/HMI.UI/HMI.UI.csproj b/HMI.UI/HMI.UI.csproj
index 1e9ec04..9582666 100644
--- a/HMI.UI/HMI.UI.csproj
+++ b/HMI.UI/HMI.UI.csproj
@@ -83,6 +83,9 @@
     <Compile Include="Controls\KeyboradWindow.xaml.cs">
       <DependentUpon>KeyboradWindow.xaml</DependentUpon>
     </Compile>
+    <Compile Include="Controls\MultiCombox.xaml.cs">
+      <DependentUpon>MultiCombox.xaml</DependentUpon>
+    </Compile>
     <Compile Include="Controls\NumberPanel.xaml.cs">
       <DependentUpon>NumberPanel.xaml</DependentUpon>
     </Compile>
@@ -90,6 +93,7 @@
     <Compile Include="Controls\treegrid\Controls\TreeGrid.cs" />
     <Compile Include="Controls\treegrid\Controls\TreeItemContentControl.cs" />
     <Compile Include="Controls\treegrid\Controls\TreeItemData.cs" />
+    <Compile Include="Controls\treegrid\TreeItemConverter.cs" />
     <Compile Include="Controls\treegrid\TreeNodeExtend.cs" />
     <Compile Include="Converter\VisibilityBoolConverter.cs" />
     <Compile Include="Converter\DataGridRowFlagConverter.cs" />
@@ -127,6 +131,10 @@
       <Generator>MSBuild:Compile</Generator>
       <SubType>Designer</SubType>
     </Page>
+    <Page Include="Controls\MultiCombox.xaml">
+      <SubType>Designer</SubType>
+      <Generator>MSBuild:Compile</Generator>
+    </Page>
     <Page Include="Controls\NumberPanel.xaml">
       <Generator>MSBuild:Compile</Generator>
       <SubType>Designer</SubType>
diff --git a/HMI.UI/MaskWindow.xaml b/HMI.UI/MaskWindow.xaml
index 3364c0d..c648b68 100644
--- a/HMI.UI/MaskWindow.xaml
+++ b/HMI.UI/MaskWindow.xaml
@@ -11,15 +11,16 @@
         pu:WindowXCaption.Height="0"
         ResizeMode="NoResize"
         SizeToContent="WidthAndHeight"
-        Height="20"
         CanClose="False"
-        WindowStyle="None"
+        MinWidth="300"
+        MinHeight="0"
+        Width="Auto"
         x:Name="self"
         WindowStartupLocation="CenterOwner">
     <WindowChrome.WindowChrome>
         <WindowChrome GlassFrameThickness="-1"/>
     </WindowChrome.WindowChrome>
-    <Window.Style>
+    <!--<Window.Style>
         <Style TargetType="Window">
             <Style.Triggers>
                 <DataTrigger Binding="{Binding Status}" Value="Info">
@@ -27,12 +28,50 @@
                 </DataTrigger>
             </Style.Triggers>
         </Style>
-    </Window.Style>
+    </Window.Style>-->
     <Grid>
+        <Grid.RowDefinitions>
+            <RowDefinition Height="Auto"/>
+            <RowDefinition />
+        </Grid.RowDefinitions>
+        <Border Padding="10,5,0,5" Background="{StaticResource PrimaryColor}" CornerRadius="5,5,0,0">
+            <Label Padding="5" FontSize="14" Margin="0" Foreground="#FFF" Content="{DynamicResource App.PleaseWait}"/>
+        </Border>
+
+        <StackPanel Grid.Row="1" Margin="20" Orientation="Horizontal" VerticalAlignment="Center"
+                    HorizontalAlignment="Center">
+            <TextBlock FontSize="45" 
+                       FontFamily="{StaticResource iconfont}" Foreground="{StaticResource PrimaryColor}" 
+                       RenderTransformOrigin="0.5,0.5"
+                       VerticalAlignment="Center" Text="&#xe891;">
+                <TextBlock.RenderTransform>
+                    <RotateTransform x:Name="MyAnimatedTransform" Angle="0" />
+                </TextBlock.RenderTransform>
+                <TextBlock.Triggers>
+                    <EventTrigger RoutedEvent="TextBlock.Loaded">
+                        <BeginStoryboard>
+                            <Storyboard>
+                                <DoubleAnimation 
+                                Storyboard.TargetName="MyAnimatedTransform"
+                                Storyboard.TargetProperty="(RotateTransform.Angle)"
+                                        From="0" To="360" Duration="0:0:3"
+                                        RepeatBehavior="Forever"/>
+                            </Storyboard>
+                        </BeginStoryboard>
+                    </EventTrigger>
+                </TextBlock.Triggers>
+            </TextBlock>
+
+            <Label x:Name="label" VerticalContentAlignment="Center" HorizontalContentAlignment="Left" 
+                   FontSize="20" FontWeight="Bold" Margin="20,0,0,0" />
+        </StackPanel>
+
+    </Grid>
+    <!--<Grid>
         <Label x:Name="label" 
                Margin="20"
                VerticalContentAlignment="Center"
                HorizontalContentAlignment="Center"
                FontSize="20" FontWeight="Bold" Foreground="White"/>
-    </Grid>
+    </Grid>-->
 </pu:WindowX>
diff --git a/HMI.UI/Style/FormStyle.xaml b/HMI.UI/Style/FormStyle.xaml
index 5172944..b9a908f 100644
--- a/HMI.UI/Style/FormStyle.xaml
+++ b/HMI.UI/Style/FormStyle.xaml
@@ -67,6 +67,8 @@
         <Setter Property="pu:TextBoxHelper.FocusedWatermarkForeground" Value="#AAAAAA" />
         <Setter Property="Height" Value="35" />
         <Setter Property="Padding" Value="5,0" />
+        <Setter Property="BorderThickness" Value="1" />
+        <Setter Property="BorderBrush" Value="Gray" />
         <Setter Property="Background" Value="White" />
         <Setter Property="Foreground" Value="Black" />
         <Setter Property="VerticalContentAlignment" Value="Center"/>
@@ -85,7 +87,6 @@
         <Setter Property="pu:TextBoxHelper.FocusedWatermarkForeground" Value="#AAAAAA" />
         <Setter Property="Height" Value="35" />
         <Setter Property="Padding" Value="5,0" />
-        <Setter Property="Background" Value="Transparent" />
         <Setter Property="Foreground" Value="Black" />
         <Setter Property="VerticalContentAlignment" Value="Center"/>
         <Style.Triggers>
@@ -151,6 +152,14 @@
         <Setter Property="HorizontalAlignment" Value="Stretch"/>
         <Setter Property="Foreground" Value="Black" />
         <Setter Property="SelectedValuePath" Value="Content"/>
+        <Setter Property="BorderThickness" Value="1" />
+        <Setter Property="BorderBrush" Value="Gray" />
+        <Style.Triggers>
+            <Trigger Property="IsEnabled" Value="False">
+                <Setter Property="Background" Value="#e8e8e8"/>
+                <Setter Property="Opacity" Value="1"/>
+            </Trigger>
+        </Style.Triggers>
     </Style>
 
     <Style x:Key="TimePicker" TargetType="pu:DateTimePicker">
diff --git a/HMI.UI/Style/PanuonResource.xaml b/HMI.UI/Style/PanuonResource.xaml
index 4fdbc63..246d9d4 100644
--- a/HMI.UI/Style/PanuonResource.xaml
+++ b/HMI.UI/Style/PanuonResource.xaml
@@ -12,7 +12,7 @@
              xmlns:dp="clr-namespace:HMI.UI.DpUtil;assembly=HMI.UI">
     <ResourceDictionary.MergedDictionaries>
         <ResourceDictionary Source="/HMI.UI;component/Style/Colors.xaml"/>
-        <pu:StyleDictionary Includes="PasswordBox,TextBox,ComboBox,Button,DataGrid,TreeView,Menu,ListBox" />
+        <pu:StyleDictionary Includes="PasswordBox,TextBox,ComboBox,Button,DataGrid,TreeView,Menu,ListBox,ContextMenu" />
         <ResourceDictionary Source="/HMI.UI;component/Resources/Font/Font.xaml"/>
     </ResourceDictionary.MergedDictionaries>
     <sys:Double x:Key="TitleFontSize">20</sys:Double>
@@ -157,7 +157,7 @@
         <Setter Property="BorderBrush" Value="{StaticResource CommonBoarder}"/>
         <Setter Property="BorderThickness" Value="2"/>
         <Setter Property="Background" Value="#f3f9ff"/>
-        <Setter Property="Margin" Value="10"/>
+        <Setter Property="Margin" Value="5"/>
         <Setter Property="Padding" Value="10"/>
     </Style>
 
@@ -172,30 +172,35 @@
     </Style>
 
     <Style x:Key="TableTextBlock" TargetType="TextBlock">
-        <Setter Property="Padding" Value="0"/>
+        <Setter Property="Padding" Value="0,2"/>
         <Setter Property="Margin" Value="0"/>
         <Setter Property="VerticalAlignment" Value="Center"/>
+        <Setter Property="TextWrapping" Value="Wrap"/>
     </Style>
-    
+
+
+    <Style TargetType="DataGridCell" 
+       BasedOn="{StaticResource {x:Static pu:StyleKeys.DataGridCellStyle}}">
+        <Setter Property="Background" Value="Transparent"/>
+        <Setter Property="Padding" Value="5"/>
+        <Setter Property="Margin" Value="0"/>
+        <Setter Property="BorderThickness" Value="0"/>
+        <Setter Property="VerticalAlignment" Value="Stretch"/>
+    </Style>
+
     <Style x:Key="DefaultDataGrid" TargetType="DataGrid" BasedOn="{StaticResource {x:Static pu:StyleKeys.DataGridStyle}}">
         <Style.Resources>
-            <Style TargetType="DataGridCell" 
-                   BasedOn="{StaticResource {x:Static pu:StyleKeys.DataGridCellStyle}}">
-                <Setter Property="Background" Value="Transparent"/>
-                <Setter Property="Padding" Value="5"/>
-                <Setter Property="Margin" Value="0"/>
-                <Setter Property="BorderThickness" Value="0"/>
-                <Setter Property="VerticalAlignment" Value="Stretch"/>
-            </Style>
             <Style TargetType="DataGridColumnHeader" 
        BasedOn="{StaticResource {x:Static pu:StyleKeys.DataGridColumnHeaderStyle}}">
                 <Setter Property="Padding" Value="10"/>
                 <Setter Property="BorderThickness" Value="0"/>
             </Style>
         </Style.Resources>
-        <Setter Property="pu:DataGridHelper.ColumnHeaderPadding" Value="0"/>
+
         <Setter Property="RowHeight" Value="30"/>
         <Setter Property="ColumnHeaderHeight" Value="36"/>
+        
+        <Setter Property="pu:DataGridHelper.ColumnHeaderPadding" Value="0"/>
         <Setter Property="ForceCursor" Value="True"/>
         <Setter Property="SelectionMode" Value="Single"/>
         <Setter Property="RowStyle" Value="{StaticResource DefaultDataGridRow}"/>
@@ -286,13 +291,6 @@
                 Value="{DynamicResource App.Save}"/>
         <Setter Property="pu:WindowXModalDialog.ButtonPanelMargin" Value="0,30,10,10"/>
     </Style>
-
-    <Popup x:Key="testPop" Placement="Relative" StaysOpen="True" Width="1920" Height="1080">
-        <Rectangle x:Name="RectMask"
-           Fill="Red"
-           Opacity="0"
-           Visibility="Visible" />
-    </Popup>
 
     <Style x:Key="DefaultListBox" TargetType="ListBox" BasedOn="{StaticResource {x:Static pu:StyleKeys.ListBoxStyle}}">
         <Setter Property="pu:ListBoxHelper.ItemsHoverBackground" Value="{StaticResource SecondPrimaryColor}"/>
diff --git a/HMI.UI/Util/MsgBoxHelper.cs b/HMI.UI/Util/MsgBoxHelper.cs
index 79174a2..18f2a30 100644
--- a/HMI.UI/Util/MsgBoxHelper.cs
+++ b/HMI.UI/Util/MsgBoxHelper.cs
@@ -74,7 +74,10 @@
                 owner.Dispatcher.Invoke(() =>
                 {
                     window = Window.GetWindow(owner) ?? Application.Current.MainWindow;
-                    res = MessageBoxX.Show(window, message, caption, button, icon, defaultButton, setting, countdown);
+                    window.Dispatcher.Invoke(() =>
+                    {
+                        res = MessageBoxX.Show(window, message, caption, button, icon, defaultButton, setting, countdown);
+                    });
                 });
             }
             else
diff --git a/HMI.UnitTest/MainWindow.xaml.cs b/HMI.UnitTest/MainWindow.xaml.cs
index 2de0f1d..11bfb1a 100644
--- a/HMI.UnitTest/MainWindow.xaml.cs
+++ b/HMI.UnitTest/MainWindow.xaml.cs
@@ -38,7 +38,8 @@
                 for (int j = 0; j < 1000; j++)
                 {
                     string status = (j % 2 == 0) ? "Passed" : "Failed";
-                    tsExecution.AddTestData(0, $"{j},2,3,4,5,6,{status}");
+                    string d = (j % 2 == 0) ? "9" : "9999999999999999999999999999999999999999999999999999999999999999995";
+                    tsExecution.AddTestData(0, $"ConnectivityCANRec,2,3,4,{d},6,{status}");
                 }
             });
 
@@ -46,6 +47,7 @@
             tsExecution.UpdatePositionProcessValue(0, 12);
             tsExecution.PausePosition(0);
             tsExecution.SetPositionBarcode(0, "123456789");
+            tsExecution.SavePositionCSV(0);
 
 
             //tsExecution.UpdatePositionStatus(1, "Failed");
diff --git a/Plugin.JarUI/JarUI.xaml b/Plugin.JarUI/JarUI.xaml
index c160e66..b0a870c 100644
--- a/Plugin.JarUI/JarUI.xaml
+++ b/Plugin.JarUI/JarUI.xaml
@@ -21,23 +21,38 @@
             </Grid.RowDefinitions>
             <WindowsFormsHost x:Name="host" Visibility="Collapsed"/>
             <DockPanel>
+                <DockPanel.Resources>
+                    <Style TargetType="CheckBox">
+                        <Setter Property="FontSize" Value="20"/>
+                        <Setter Property="VerticalContentAlignment" Value="Center"/>
+                    </Style>
+                </DockPanel.Resources>
+
                 <StackPanel DockPanel.Dock="Right" Orientation="Horizontal" dp:StackPanelHelper.ChildSpace="20">
                     <Button Style="{StaticResource SuccessButton}" x:Name="startBtn" Click="Button_Click"
-                            Content="{DynamicResource App.StopSequence}"/>
-                    <Button Style="{StaticResource DangerButton}" x:Name="stopBtn" Click="Button_Click"
                             Content="{DynamicResource App.StartSequence}"/>
+                    <Button Style="{StaticResource DangerButton}" x:Name="stopBtn" Click="Button_Click"
+                            Content="{DynamicResource App.StopSequence}"/>
                     <Button Style="{StaticResource PrimaryButton}" x:Name="scannerSettingBtn" Click="Button_Click"
                             Content="{DynamicResource App.ScannerSetting}"/>
                 </StackPanel>
-                <pu:FormGroup HeaderHeight="Auto" HeaderPadding="0" HorizontalContentAlignment="Left">
-                    <pu:FormGroup.Header>
-                        <TextBlock FontSize="20" Foreground="#000" Text="{DynamicResource Ts.SelectProductModel}"/>
-                    </pu:FormGroup.Header>
-                    <ComboBox Style="{StaticResource FormComboxStyle}" MinWidth="180" ItemsSource="{Binding Products}"
+
+                <StackPanel Orientation="Horizontal" VerticalAlignment="Center" dp:StackPanelHelper.ChildSpace="20">
+                    <pu:FormGroup HeaderHeight="Auto" HeaderPadding="0" HorizontalContentAlignment="Left">
+                        <pu:FormGroup.Header>
+                            <TextBlock FontSize="20" Foreground="#000" Text="{DynamicResource Ts.SelectProductModel}"/>
+                        </pu:FormGroup.Header>
+                        <ComboBox Style="{StaticResource FormComboxStyle}" MinWidth="180" ItemsSource="{Binding Products}"
 DisplayMemberPath="Name" SelectedValuePath="Name"
                               MaxWidth="200"
 SelectedItem="{Binding SelectedProduct}" x:Name="CbProduct"/>
-                </pu:FormGroup>
+                    </pu:FormGroup>
+
+                    <CheckBox Grid.Column="1"
+IsChecked="{Binding EnableMes,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
+Content="{DynamicResource Ts.EnableMes}"
+/>
+                </StackPanel>
             </DockPanel>
             <UniformGrid Grid.Row="1" x:Name="uGrid" dp:UniformGridHelper.InnerBorder="1" Columns="6">
 
diff --git a/Plugin.JarUI/JarUI.xaml.cs b/Plugin.JarUI/JarUI.xaml.cs
index cdaea7c..4313b08 100644
--- a/Plugin.JarUI/JarUI.xaml.cs
+++ b/Plugin.JarUI/JarUI.xaml.cs
@@ -29,8 +29,8 @@
         {
             DataContext = this;
             InitializeComponent();
-            InitProducts();
             InitTsEngine();
+            InitProducts();
         }
 
         public override void InitFinish()
@@ -97,8 +97,21 @@
             get { return _selectedProduct; }
             set
             {
-                SetTestSeqFile(value);
                 Set(ref _selectedProduct, value);
+                SetTestSeqFile(value);
+            }
+        }
+
+        private bool _enableMes;
+        public bool EnableMes
+        {
+            get => _enableMes;
+            set
+            {
+                if (WriteConfig("EnableMes", value))
+                {
+                    Set(ref _enableMes, value);
+                }
             }
         }
 
@@ -156,17 +169,130 @@
                 if (!string.IsNullOrEmpty(lastModel))
                 {
                     var product = Products.FirstOrDefault(p => p.Name == lastModel);
-                    if (product != null) SelectedProduct = product;
-                    goto Write;
+                    if (product != null)
+                    {
+                        SelectedProduct = product;
+                        RefershUI();
+                        return;
+                    }
                 }
-                goto Write;
-
-            Write:
                 SelectedProduct = Products[0];
-                engine?.Globals.SetValString("StationState.ProductModel", 1, Products[0].Name);
-
                 RefershUI();
             }
+        }
+
+        private bool WriteConfig(string name, bool value)
+        {
+            //IniFileHelper.WriteIniData("Config", name, value.ToString(), CountIniPath);
+            try
+            {
+                if (ParameterManager.UpdateCommonParameter(name, value, out string errMsg))
+                {
+                    SequenceContext?.StationGlobals.SetValBoolean($"SystemSetting.{name}", 1, value);
+                    return true;
+                }
+                else
+                {
+                    throw new Exception(errMsg);
+                }
+            }
+            catch (Exception ex)
+            {
+                MsgBoxHelper2.Error(ex.Message);
+                return false;
+            }
+        }
+
+        /// <summary>
+        /// 璇诲彇閰嶇疆鏂囦欢閲岀殑鍑犱釜灞炴��
+        /// </summary>
+        /// <param name="pass"></param>
+        /// <param name="fail"></param>
+        private void ReadConfig()
+        {
+            ParameterData value;
+            try
+            {
+                value = ParameterManager.GetCommonParameterByName("EnableMes");
+                _enableMes = bool.Parse(value.Value);
+                Set("EnableMes");
+            }
+            catch
+            {
+                _enableMes = false;
+                Set("EnableMes");
+            }
+
+            //try
+            //{
+            //    value = ParameterManager.GetCommonParameterByName("EnableNGPause");
+            //    _enableNGPause = bool.Parse(value.Value);
+            //    Set("EnableNGPause");
+            //}
+            //catch
+            //{
+            //    _enableNGPause = false;
+            //    Set("EnableNGPause");
+            //}
+
+            //try
+            //{
+            //    value = ParameterManager.GetCommonParameterByName("EnableCanLog");
+            //    _enableCanLog = bool.Parse(value.Value);
+            //    Set("EnableCanLog");
+            //}
+            //catch
+            //{
+            //    _enableCanLog = false;
+            //    Set("EnableCanLog");
+            //}
+
+            //try
+            //{
+            //    value = ParameterManager.GetCommonParameterByName("EnableDebugMode");
+            //    _enableDebugMode = bool.Parse(value.Value);
+            //    Set("EnableDebugMode");
+            //}
+            //catch
+            //{
+            //    _enableDebugMode = false;
+            //    Set("EnableDebugMode");
+            //}
+
+            //try
+            //{
+            //    value = ParameterManager.GetCommonParameterByName("EnableSimpleMode");
+            //    EnableSimpleMode = bool.Parse(value.Value);
+            //}
+            //catch
+            //{
+            //    EnableSimpleMode = false;
+            //}
+
+            //try
+            //{
+            //    value = ParameterManager.GetCommonParameterByName("UI_ShowInfoDock");
+            //    UI_ShowInfoDock = bool.Parse(value.Value);
+            //}
+            //catch
+            //{
+            //    UI_ShowInfoDock = false;
+            //}
+
+            //try
+            //{
+
+            //    value = ParameterManager.GetCommonParameterByName("TestReportFolder");
+            //    csvDir = value.Value;
+            //    if (!Directory.Exists(csvDir))
+            //    {
+            //        Directory.CreateDirectory(csvDir);
+            //    }
+            //}
+            //catch
+            //{
+            //    csvDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "TestReport");
+            //}
         }
 
         #region 鎵爜鏋浉鍏�
@@ -203,7 +329,10 @@
             catch (Exception ex)
             {
                 log.Error("Init Scan Gun Failed.", ex);
-                MsgBoxHelper.Error(this, $"{this.lang("Tr.InitGunFailed")}: {ex.StackTrace}");
+                Dispatcher.Invoke(() =>
+                {
+                    MsgBoxHelper.Error(this, $"{this.lang("Tr.InitGunFailed")}: {ex.StackTrace}");
+                });
             }
         }
         private string ReadDataFrom(SerialPort sp)
@@ -343,7 +472,10 @@
                     catch (Exception ex)
                     {
                         log.Error("TsExecution Loaded Failed.", ex);
-                        MsgBoxHelper.Error(this, $"{LangUtil.Get("Ts.UserControlLoadFailed")}: {ex.Message}");
+                        Dispatcher.Invoke(() =>
+                        {
+                            MsgBoxHelper.Error(this, $"{LangUtil.Get("Ts.UserControlLoadFailed")}: {ex.Message}");
+                        });
                     }
                     Dispatcher.Invoke(() =>
                     {
@@ -362,6 +494,8 @@
             try
             {
                 if (!isLoadOk) return;
+                engine?.Globals.SetValString("StationState.ProductModel", 1, SelectedProduct?.Name);
+                engine?.CommitGlobalsToDisk(promptOnSaveConflicts: false);
                 this.modeSeq = this.engine.GetSequenceFileEx(@"C:\Program Files\National Instruments\TestStand 2020\Components\Models\TestStandModels\ParallelModel.seq");
                 this.testSeq = this.engine.GetSequenceFileEx(this.testSeqFile, 0, TypeConflictHandlerTypes.ConflictHandler_UseGlobalType);
                 this.execution = this.engine.NewExecution(this.testSeq, "Test UUTs", this.modeSeq, false, 0, null, null, null);
diff --git a/Plugin.SeqEditor/AssemblyInfo.cs b/Plugin.SeqEditor/AssemblyInfo.cs
new file mode 100644
index 0000000..cc29e7f
--- /dev/null
+++ b/Plugin.SeqEditor/AssemblyInfo.cs
@@ -0,0 +1,10 @@
+using System.Windows;
+
+[assembly:ThemeInfo(
+    ResourceDictionaryLocation.None,            //where theme specific resource dictionaries are located
+                                                //(used if a resource is not found in the page,
+                                                // or application resource dictionaries)
+    ResourceDictionaryLocation.SourceAssembly   //where the generic resource dictionary is located
+                                                //(used if a resource is not found in the page,
+                                                // app, or any theme specific resource dictionaries)
+)]
diff --git a/Plugin.SeqEditor/Constant/DefaultStepName.cs b/Plugin.SeqEditor/Constant/DefaultStepName.cs
new file mode 100644
index 0000000..8c1c992
--- /dev/null
+++ b/Plugin.SeqEditor/Constant/DefaultStepName.cs
@@ -0,0 +1,12 @@
+锘縩amespace Plugin.SeqEditor.Constant
+{
+    public abstract class DefaultStepName
+    {
+        public const string PassFailTest = "Pass/Fail Test";
+        public const string NumericLimitTest = "Numeric Limit Test";
+        public const string NI_MultipleNumericLimitTest = "Multiple Numeric Limit Test";
+        public const string StringValueTest = "String Value Test";
+
+        public const string Action = "Action";
+    }
+}
diff --git a/Plugin.SeqEditor/Constant/StepTypeStr.cs b/Plugin.SeqEditor/Constant/StepTypeStr.cs
new file mode 100644
index 0000000..4b6b28b
--- /dev/null
+++ b/Plugin.SeqEditor/Constant/StepTypeStr.cs
@@ -0,0 +1,24 @@
+锘縰sing System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Plugin.SeqEditor.Constant
+{
+    public abstract class StepTypeStr
+    {
+        public const string PassFailTest = "PassFailTest";
+        public const string NumericLimitTest = "NumericLimitTest";
+        public const string NI_MultipleNumericLimitTest = "NI_MultipleNumericLimitTest";
+        public const string StringValueTest = "StringValueTest";
+
+        public const string NI_Wait = "NI_Wait";
+
+
+        public const string Action = "Action";
+        public const string SequenceCall = "SequenceCall";
+
+
+    }
+}
diff --git a/Plugin.SeqEditor/Entity/EditablePO/IEPO.cs b/Plugin.SeqEditor/Entity/EditablePO/IEPO.cs
new file mode 100644
index 0000000..dc06dfd
--- /dev/null
+++ b/Plugin.SeqEditor/Entity/EditablePO/IEPO.cs
@@ -0,0 +1,46 @@
+锘縰sing CommunityToolkit.Mvvm.ComponentModel;
+
+namespace Plugin.SeqEditor.Entity.EditablePO
+{
+    public class BaseEPO : ObservableObject
+    {
+        public StepUI StepUI { get; protected set; }
+
+
+        public string StepName
+        {
+            get => StepUI?.Name;
+            set
+            {
+                if (StepUI != null)
+                {
+                    StepUI.Name = value;
+                    OnPropertyChanged(nameof(StepName));
+                }
+            }
+        }
+        public string StepType
+        {
+            get => StepUI?.Type;
+        }
+
+        public string RunMode
+        {
+            get => StepUI?.RunMode;
+            set
+            {
+                if (StepUI != null)
+                {
+                    StepUI.RunMode = value;
+                    OnPropertyChanged(nameof(RunMode));
+                }
+            }
+        }
+
+        public virtual void CustomPropertyChanged(string name)
+        {
+            OnPropertyChanged(name);
+            //StepUI?.UpdatePropertyChanged("Description");
+        }
+    }
+}
diff --git a/Plugin.SeqEditor/Entity/EditablePO/NumValTestEPO.cs b/Plugin.SeqEditor/Entity/EditablePO/NumValTestEPO.cs
new file mode 100644
index 0000000..69d3272
--- /dev/null
+++ b/Plugin.SeqEditor/Entity/EditablePO/NumValTestEPO.cs
@@ -0,0 +1,57 @@
+锘縰sing CommunityToolkit.Mvvm.ComponentModel;
+using NationalInstruments.TestStand.Interop.API;
+
+namespace Plugin.SeqEditor.Entity.EditablePO
+{
+    public class NumValTestEPO : BaseEPO
+    {
+        public Step Step { get; private set; }
+        public PropertyObject PropertyObject { get; private set; }
+
+        public NumValTestEPO(StepUI stepUI)
+        {
+            StepUI = stepUI;
+            Step = stepUI?.step;
+            PropertyObject = Step?.AsPropertyObject();
+        }
+
+        public string ComparisonType
+        {
+            get => PropertyObject?.GetValString("Comp", 0);
+            set
+            {
+                if (PropertyObject != null)
+                {
+                    PropertyObject.SetValString("Comp", 0, value);
+                    CustomPropertyChanged(nameof(ComparisonType));
+                }
+            }
+        }
+
+        public double? Low
+        {
+            get => PropertyObject?.GetValNumber("Limits.Low", 0);
+            set
+            {
+                if (PropertyObject != null)
+                {
+                    PropertyObject.SetValNumber("Limits.Low", 0, value ?? 0);
+                    CustomPropertyChanged(nameof(Low));
+                }
+            }
+        }
+
+        public double? High
+        {
+            get => PropertyObject?.GetValNumber("Limits.High", 0);
+            set
+            {
+                if (PropertyObject != null)
+                {
+                    PropertyObject.SetValNumber("Limits.High", 0, value ?? 0);
+                    CustomPropertyChanged(nameof(Low));
+                }
+            }
+        }
+    }
+}
diff --git a/Plugin.SeqEditor/Entity/EditablePO/StringValTestEPO.cs b/Plugin.SeqEditor/Entity/EditablePO/StringValTestEPO.cs
new file mode 100644
index 0000000..352448b
--- /dev/null
+++ b/Plugin.SeqEditor/Entity/EditablePO/StringValTestEPO.cs
@@ -0,0 +1,66 @@
+锘縰sing CommunityToolkit.Mvvm.ComponentModel;
+using NationalInstruments.TestStand.Interop.API;
+
+namespace Plugin.SeqEditor.Entity.EditablePO
+{
+
+    public class StringValTestEPO : BaseEPO
+    {
+        public Step Step { get; private set; }
+        public PropertyObject PropertyObject { get; private set; }
+
+        public StringValTestEPO(StepUI stepUI)
+        {
+            StepUI = stepUI;
+            Step = StepUI?.step;
+            PropertyObject = Step?.AsPropertyObject();
+        }
+
+        public string ComparisonType
+        {
+            get => PropertyObject?.GetValString("Comp", 0);
+            set
+            {
+                if (PropertyObject != null)
+                {
+                    PropertyObject.SetValString("Comp", 0, value);
+                    CustomPropertyChanged(nameof(ComparisonType));
+                }
+            }
+        }
+
+        public bool IsReadOnly
+        {
+            get
+            {
+                if (PropertyObject == null) return false;
+                bool flag = PropertyObject.GetValBoolean("Limits.UseStringExpr", 0);
+                return flag == true;
+            }
+        }
+
+        public string StringValue
+        {
+            get
+            {
+                if (PropertyObject == null) return null;
+                var value = PropertyObject.GetValString("Limits.String", 0);
+                if (string.IsNullOrEmpty(value))
+                {
+                    value = PropertyObject.GetValString("Limits.StringExpr", 0);
+                }
+                return value;
+            }
+            set
+            {
+                if (PropertyObject != null)
+                {
+                    PropertyObject.SetValString("Limits.String", 0, value);
+                    PropertyObject.SetValString("Limits.StringExpr", 0, null);
+                    PropertyObject.SetValBoolean("Limits.UseStringExpr", 0, false);
+                    CustomPropertyChanged(nameof(StringValue));
+                }
+            }
+        }
+    }
+}
diff --git a/Plugin.SeqEditor/Entity/EditablePO/WaitEPO.cs b/Plugin.SeqEditor/Entity/EditablePO/WaitEPO.cs
new file mode 100644
index 0000000..0bb84e7
--- /dev/null
+++ b/Plugin.SeqEditor/Entity/EditablePO/WaitEPO.cs
@@ -0,0 +1,37 @@
+锘縰sing NationalInstruments.TestStand.Interop.API;
+
+namespace Plugin.SeqEditor.Entity.EditablePO
+{
+    public class WaitEPO : BaseEPO
+    {
+        public Step Step { get; private set; }
+        public PropertyObject PropertyObject { get; private set; }
+
+        public WaitEPO(StepUI stepUI)
+        {
+            StepUI = stepUI;
+            Step = StepUI?.step;
+            PropertyObject = Step?.AsPropertyObject();
+        }
+
+        public string TimeInterval
+        {
+            get
+            {
+                //var str = PropertyObject?.GetValString("TimeExpr", 0);
+                //if (string.IsNullOrEmpty(str)) return null;
+                //double.TryParse(str, out double value);
+                //return value;
+                return PropertyObject?.GetValString("TimeExpr", 0);
+            }
+            set
+            {
+                if (PropertyObject != null)
+                {
+                    PropertyObject.SetValString("TimeExpr", 0, value?.ToString());
+                    CustomPropertyChanged(nameof(TimeInterval));
+                }
+            }
+        }
+    }
+}
diff --git a/Plugin.SeqEditor/Entity/SeqUI.cs b/Plugin.SeqEditor/Entity/SeqUI.cs
new file mode 100644
index 0000000..c008960
--- /dev/null
+++ b/Plugin.SeqEditor/Entity/SeqUI.cs
@@ -0,0 +1,27 @@
+锘縰sing CommunityToolkit.Mvvm.ComponentModel;
+using NationalInstruments.TestStand.Interop.API;
+
+namespace Plugin.SeqEditor.Entity
+{
+    public partial class SeqUI : ObservableObject
+    {
+        public Sequence sequence { get; private set; }
+        public SeqUI(Sequence sequence)
+        {
+            this.sequence = sequence;
+        }
+
+        public string Name
+        {
+            get => sequence?.Name;
+            set
+            {
+                if (sequence != null)
+                {
+                    sequence.Name = value;
+                    OnPropertyChanged(Name);
+                }
+            }
+        }
+    }
+}
diff --git a/Plugin.SeqEditor/Entity/StepUI.cs b/Plugin.SeqEditor/Entity/StepUI.cs
new file mode 100644
index 0000000..79f71b0
--- /dev/null
+++ b/Plugin.SeqEditor/Entity/StepUI.cs
@@ -0,0 +1,91 @@
+锘縰sing CommunityToolkit.Mvvm.ComponentModel;
+using NationalInstruments.TestStand.Interop.API;
+using System.Collections.ObjectModel;
+
+namespace Plugin.SeqEditor.Entity
+{
+    public partial class StepUI : ObservableObject
+    {
+        public Step step { get; private set; }
+
+        public StepUI(Step step)
+        {
+            this.step = step;
+        }
+
+        public void UpdatePropertyChanged(string name)
+        {
+            OnPropertyChanged(name);
+        }
+
+        public string Name
+        {
+            get => step?.Name;
+            set
+            {
+                if (step != null)
+                {
+                    step.Name = value;
+                    OnPropertyChanged(nameof(Name));
+                }
+            }
+        }
+
+        public string Description
+        {
+            get => step?.Description;
+        }
+
+        public string RunMode
+        {
+            get => step?.GetRunModeEx();
+            set
+            {
+                if (step != null)
+                {
+                    step.SetRunModeEx(value);
+                    OnPropertyChanged(nameof(RunMode));
+                }
+            }
+        }
+
+        public string Type
+        {
+            get => step?.StepType?.Name;
+        }
+
+        public bool CanChangeAdapter
+        {
+            get => step?.CanChangeAdapter() ?? true;
+        }
+
+        public string Adapter
+        {
+            get => step?.AdapterKeyName;
+            set
+            {
+                if (step != null)
+                {
+                    step.ChangeAdapter(value);
+                    OnPropertyChanged(nameof(RunMode));
+                }
+            }
+        }
+
+
+
+        public StepGroups? StepGroup
+        {
+            get
+            {
+                return step?.StepGroup;
+            }
+        }
+
+        [ObservableProperty]
+        private string group;
+
+        [ObservableProperty]
+        public ObservableCollection<StepUI> children = new();
+    }
+}
diff --git a/Plugin.SeqEditor/Entity/VarUI.cs b/Plugin.SeqEditor/Entity/VarUI.cs
new file mode 100644
index 0000000..c050503
--- /dev/null
+++ b/Plugin.SeqEditor/Entity/VarUI.cs
@@ -0,0 +1,58 @@
+锘縰sing CommunityToolkit.Mvvm.ComponentModel;
+using NationalInstruments.TestStand.Interop.API;
+using Plugin.SeqEditor.Util;
+using System.Collections.ObjectModel;
+
+namespace Plugin.SeqEditor.Entity
+{
+    public partial class VarUI : ObservableObject
+    {
+        private string prefix;
+        private PropertyObject propertyObject;
+
+        public VarUI(PropertyObject po)
+        {
+            this.propertyObject = po;
+        }
+
+        [ObservableProperty]
+        private ObservableCollection<VarUI> subProperties = new ObservableCollection<VarUI>();
+
+        public string Name
+        {
+            get => propertyObject?.Name;
+            set
+            {
+                if (propertyObject != null)
+                {
+                    propertyObject.Name = value;
+                    OnPropertyChanged(Name);
+                }
+            }
+        }
+
+        public string Value
+        {
+            get => propertyObject?.GetValueString();
+            set
+            {
+                propertyObject?.SetValueString(value);
+            }
+        }
+
+        public PropertyValueTypes? Type
+        {
+            get => propertyObject?.Type?.ValueType;
+        }
+
+        public PropertyValueTypes? ElementType
+        {
+            get => propertyObject?.Type?.ElementType?.ValueType;
+        }
+
+        public string DisplayString
+        {
+            get => propertyObject?.Type.DisplayString;
+        }
+    }
+}
diff --git a/Plugin.SeqEditor/Plugin.SeqEditor.csproj b/Plugin.SeqEditor/Plugin.SeqEditor.csproj
new file mode 100644
index 0000000..87fbf68
--- /dev/null
+++ b/Plugin.SeqEditor/Plugin.SeqEditor.csproj
@@ -0,0 +1,33 @@
+锘�<Project Sdk="Microsoft.NET.Sdk">
+	<PropertyGroup>
+		<TargetFramework>net472</TargetFramework>
+		<Nullable>enable</Nullable>
+		<LangVersion>10.0</LangVersion>
+		<ImplicitUsings>enable</ImplicitUsings>
+		<UseWPF>true</UseWPF>
+		<Platforms>AnyCPU;x64</Platforms>
+	</PropertyGroup>
+	<ItemGroup>
+	  <PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
+	  <PackageReference Include="log4net" Version="3.0.4" />
+	  <PackageReference Include="Microsoft.Xaml.Behaviors.Wpf" Version="1.1.135" />
+	  <PackageReference Include="Panuon.WPF.UI" Version="1.3.0.2" />
+	</ItemGroup>
+	<ItemGroup>
+	  <ProjectReference Include="..\HMI.Base\HMI.Base.csproj" />
+	  <ProjectReference Include="..\HMI.Core\HMI.Core.csproj" />
+	  <ProjectReference Include="..\HMI.UI\HMI.UI.csproj" />
+	  <ProjectReference Include="..\HMI.Util\HMI.Util.csproj" />
+	</ItemGroup>
+	<ItemGroup>
+	  <Reference Include="NationalInstruments.TestStand.Interop.AdapterAPI">
+	    <HintPath>..\..\..\..\Program Files\National Instruments\TestStand 2020\API\DotNET\Assemblies\CurrentVersion\NationalInstruments.TestStand.Interop.AdapterAPI.dll</HintPath>
+	  </Reference>
+	  <Reference Include="NationalInstruments.TestStand.Interop.API">
+	    <HintPath>..\..\..\..\Program Files\National Instruments\TestStand 2020\API\DotNET\Assemblies\CurrentVersion\NationalInstruments.TestStand.Interop.API.dll</HintPath>
+	  </Reference>
+	  <Reference Include="NationalInstruments.TestStand.Interop.UI.Support">
+	    <HintPath>..\..\..\..\Program Files\National Instruments\TestStand 2020\API\DotNET\Assemblies\CurrentVersion\NationalInstruments.TestStand.Interop.UI.Support.dll</HintPath>
+	  </Reference>
+	</ItemGroup>
+</Project>
diff --git a/Plugin.SeqEditor/SeqEditor.xaml b/Plugin.SeqEditor/SeqEditor.xaml
new file mode 100644
index 0000000..173f331
--- /dev/null
+++ b/Plugin.SeqEditor/SeqEditor.xaml
@@ -0,0 +1,254 @@
+锘�<hb:PluginControl x:Class="Plugin.SeqEditor.SeqEditor"
+             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
+             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
+             xmlns:local="clr-namespace:Plugin.SeqEditor"
+             mc:Ignorable="d" 
+             xmlns:hb="clr-namespace:HMI.Base;assembly=HMI.Base"
+             xmlns:uic="clr-namespace:HMI.UI.Converter;assembly=HMI.UI"
+xmlns:ui="clr-namespace:HMI.UI;assembly=HMI.UI"
+xmlns:pw="https://opensource.panuon.com/wpf"
+xmlns:pu="https://opensource.panuon.com/wpf-ui"
+xmlns:dp="clr-namespace:HMI.UI.DpUtil;assembly=HMI.UI"
+xmlns:util="clr-namespace:Plugin.SeqEditor.Util"
+        xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
+             d:DesignHeight="450" d:DesignWidth="800">
+    <i:Interaction.Triggers>
+        <i:EventTrigger EventName="Loaded">
+            <i:InvokeCommandAction Command="{Binding LoadedCommand}" />
+        </i:EventTrigger>
+    </i:Interaction.Triggers>
+    <UserControl.Resources>
+        <ResourceDictionary>
+            <ResourceDictionary.MergedDictionaries>
+                <ResourceDictionary Source="/HMI.UI;component/Controls/TreeGrid/Themes/TreeItemContentControl.xaml"/>
+                <ResourceDictionary Source="/HMI.UI;component/Controls/TreeGrid/Themes/TreeGrid.xaml"/>
+            </ResourceDictionary.MergedDictionaries>
+
+            <Style TargetType="pu:FormGroup">
+                <Setter Property="Padding" Value="0,10"/>
+                <Setter Property="HeaderFontSize" Value="14"/>
+                <Setter Property="HeaderFontWeight" Value="Bold"/>
+            </Style>
+
+            <!-- 鏍慓rid缁勪欢 -->
+            <ui:TreeItemDataConverter x:Key="TreeItemDataConverter"/>
+
+            <CollectionViewSource x:Key="TreeGridFilter"
+                                  Source="{Binding AllSteps,Converter={StaticResource TreeItemDataConverter},ConverterParameter=Children}">
+                <CollectionViewSource.GroupDescriptions>
+                    <PropertyGroupDescription PropertyName="Data.Group" />
+                </CollectionViewSource.GroupDescriptions>
+            </CollectionViewSource>
+
+        </ResourceDictionary>
+    </UserControl.Resources>
+    <Border Style="{StaticResource CardBoarder}" Padding="0">
+        <Grid dp:GridHelper.Space="10" Cursor="">
+            <Grid.RowDefinitions>
+                <RowDefinition Height="Auto"/>
+                <RowDefinition Height="Auto"/>
+                <RowDefinition/>
+            </Grid.RowDefinitions>
+            <Grid.ColumnDefinitions>
+                <ColumnDefinition Width="0"/>
+                <ColumnDefinition Width="8*"/>
+                <ColumnDefinition Width="2*"/>
+            </Grid.ColumnDefinitions>
+
+            <DockPanel Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="3" Margin="0,0,0,10">
+                <StackPanel DockPanel.Dock="Right"  Orientation="Horizontal"
+        dp:StackPanelHelper.ChildSpace="10">
+                    <Button Style="{StaticResource AddBtn}" Content="{DynamicResource se.open}" Command="{Binding OpenSeqFileCommand}"/>
+                    <Button Style="{StaticResource SaveBtn}" Content="{DynamicResource se.save}" Command="{Binding SaveSeqFileCommand}"/>
+                    <Button Style="{StaticResource SaveBtn}" pu:ButtonHelper.Icon="&#xe60b;"
+                            Content="{DynamicResource se.saveTo}" 
+                            Command="{Binding SaveSeqFileCommand}"/>
+                    <Button Style="{StaticResource DelBtn}" Content="{DynamicResource se.close}"
+                            pu:ButtonHelper.Icon="&#xe609;"
+                            Command="{Binding CloseSeqFileCommand}"/>
+                </StackPanel>
+
+                <TextBlock DockPanel.Dock="Left" FontSize="{StaticResource TitleFontSize}" 
+           VerticalAlignment="Center" HorizontalAlignment="Left" Text="{DynamicResource se.title}"/>
+
+                <TextBlock DockPanel.Dock="Left" FontSize="{StaticResource ContentFontSize2}" 
+                           Margin="20,0,0,0"
+VerticalAlignment="Bottom" LineHeight="{StaticResource TitleFontSize}" HorizontalAlignment="Left" Text="{Binding CurrentFileName}"/>
+            </DockPanel>
+
+            <!--<pu:FormGroup Header="{DynamicResource se.seq}" Orientation="Vertical" Grid.Row="1" Grid.Column="0">
+                <DataGrid ItemsSource="{Binding Sequences}" SelectedItem="{Binding CurrentSeqUI}">
+                    <DataGrid.Columns>
+                        <DataGridTextColumn Width="*" Binding="{Binding Name}">
+                            <DataGridTextColumn.Header>
+                                <TextBlock Text="{DynamicResource se.name}"/>
+                            </DataGridTextColumn.Header>
+                        </DataGridTextColumn>
+                    </DataGrid.Columns>
+                </DataGrid>
+            </pu:FormGroup>-->
+            <pu:FormGroup Grid.Row="1" Grid.Column="1" Header="姝ラ绫诲瀷" Style="{StaticResource InputFormGroupStyle}">
+                <StackPanel Orientation="Horizontal">
+
+                    <!--<ComboBox Text="{Binding TargetType}" 
+                              MinWidth="180" Margin="0,0,10,0" x:Name="CbFilter"
+                        SelectedValuePath="DataValue" IsEditable="True" >
+                        <ComboBox.ItemTemplate>
+                            <DataTemplate DataType="util:ItemData">
+                                <StackPanel Orientation="Horizontal">
+                                    <CheckBox IsChecked="{Binding IsChecked}" />
+                                    <TextBlock Text="{Binding DisplayName}" />
+                                </StackPanel>
+                            </DataTemplate>
+                        </ComboBox.ItemTemplate>
+                    </ComboBox>-->
+
+                    <ui:MultiComboBox SelectedValues="{Binding TargetType}"
+                                      Style="{StaticResource FormComboxStyle}"
+                                      pu:ComboBoxHelper.ClearButtonVisibility="Collapsed"
+                                      MinWidth="180" Margin="0,0,10,0" x:Name="CbFilter" IsEditable="True" IsReadOnly="True" >
+                        <ui:MultiComboBox.ItemTemplate>
+                            <DataTemplate DataType="util:ItemData">
+                                <StackPanel Orientation="Horizontal">
+                                    <CheckBox IsChecked="{Binding IsChecked,UpdateSourceTrigger=PropertyChanged}" />
+                                    <TextBlock Text="{Binding DisplayName}" />
+                                </StackPanel>
+                            </DataTemplate>
+                        </ui:MultiComboBox.ItemTemplate>
+                    </ui:MultiComboBox>
+
+                    <!--<ComboBox Style="{StaticResource FormComboxStyle}" MinWidth="180" Margin="0,0,10,0" x:Name="CbFilter"/>-->
+
+                    <Button Style="{StaticResource RfhBtn}" Command="{Binding RefreshCommand}"/>
+                </StackPanel>
+            </pu:FormGroup>
+
+
+            <pu:FormGroup Header="{Binding CurrentSeqTitle}" Orientation="Vertical" Grid.Row="2" Grid.Column="1">
+                <!--<DataGrid ItemsSource="{Binding Source={StaticResource cvs}}"
+                          RowStyle="{StaticResource StepRow}"
+                          SelectedItem="{Binding CurrentStepUI}">
+                    <DataGrid.GroupStyle>
+                        <GroupStyle>
+                            <GroupStyle.Panel>
+                                <ItemsPanelTemplate>
+                                    <DataGridRowsPresenter/>
+                                </ItemsPanelTemplate>
+                            </GroupStyle.Panel>
+                            <GroupStyle.ContainerStyle>
+                                <Style TargetType="{x:Type GroupItem}" BasedOn="{StaticResource DataGridGroupStyle}"/>
+                            </GroupStyle.ContainerStyle>
+                        </GroupStyle>
+                    </DataGrid.GroupStyle>
+
+                    <DataGrid.Columns>
+                        <DataGridTextColumn Width="2*" Binding="{Binding Name}">
+                            <DataGridTextColumn.Header>
+                                <TextBlock Text="{DynamicResource se.name}"/>
+                            </DataGridTextColumn.Header>
+                        </DataGridTextColumn>
+
+                        <DataGridTextColumn Width="1*" Binding="{Binding Type}">
+                            <DataGridTextColumn.Header>
+                                <TextBlock Text="{DynamicResource se.name}"/>
+                            </DataGridTextColumn.Header>
+                        </DataGridTextColumn>
+
+                        <DataGridTextColumn Width="6*" Binding="{Binding Description}">
+                            <DataGridTextColumn.Header>
+                                <TextBlock Text="{DynamicResource se.description}"/>
+                            </DataGridTextColumn.Header>
+                        </DataGridTextColumn>
+
+                        <DataGridTemplateColumn Width="1*">
+                            <DataGridTemplateColumn.Header>
+                                <TextBlock Text="{DynamicResource se.runMode}"/>
+                            </DataGridTemplateColumn.Header>
+                            <DataGridTemplateColumn.CellTemplate>
+                                <DataTemplate>
+                                    <Grid x:Name="border" Margin="0">
+                                        <TextBlock x:Name="text" Margin="-1" Padding="0" VerticalAlignment="Center"/>
+                                    </Grid>
+                                    <DataTemplate.Triggers>
+                                        <DataTrigger Binding="{Binding RunMode}" Value="Normal">
+                                            <Setter TargetName="text" Property="Text" Value="{DynamicResource se.normal}"/>
+                                        </DataTrigger>
+                                        <DataTrigger Binding="{Binding RunMode}" Value="Skip">
+                                            <Setter TargetName="text" Property="Text" Value="{DynamicResource se.skip}"/>
+                                        </DataTrigger>
+                                        <DataTrigger Binding="{Binding RunMode}" Value="Pass">
+                                            <Setter TargetName="text" Property="Text" Value="{DynamicResource se.pass}"/>
+                                        </DataTrigger>
+                                        <DataTrigger Binding="{Binding RunMode}" Value="Fail">
+                                            <Setter TargetName="text" Property="Text" Value="{DynamicResource se.fail}"/>
+                                        </DataTrigger>
+                                    </DataTemplate.Triggers>
+                                </DataTemplate>
+                            </DataGridTemplateColumn.CellTemplate>
+                        </DataGridTemplateColumn>
+                    </DataGrid.Columns>
+                                 ItemsSource="{Binding AllSteps,Converter={StaticResource treeC}}"
+                </DataGrid>-->
+
+                <ui:TreeDataGrid Grid.Row="1" x:Name="treeDataGrid"
+                                 VirtualizingPanel.IsContainerVirtualizable="True"
+                                VirtualizingPanel.IsVirtualizing="True"
+                                VirtualizingPanel.VirtualizationMode="Recycling"
+                                ScrollViewer.CanContentScroll="False"
+                                 VirtualizingPanel.IsVirtualizingWhenGrouping="True"
+                                EnableColumnVirtualization="True"
+                                EnableRowVirtualization="True"
+                                 PreviewMouseWheel="treeDataGrid_PreviewMouseWheel"
+                                 AutoGenerateColumns="False"
+                                 IsReadOnly="True"
+                                 Style="{StaticResource DefaultDataGrid}"
+                ItemsSource="{Binding Source={StaticResource TreeGridFilter}}"
+                                 SelectedItem="{Binding TreeItemData}">
+
+                    <!--<DataGrid.GroupStyle>
+                        <GroupStyle>
+                            <GroupStyle.ContainerStyle>
+                                <Style TargetType="{x:Type GroupItem}">
+                                    <Setter Property="Margin" Value="0,0,0,5" />
+                                    <Setter Property="Template">
+                                        <Setter.Value>
+                                            <ControlTemplate TargetType="{x:Type GroupItem}">
+                                                <GroupBox Header="{Binding Name}">
+                                                    <ItemsPresenter />
+                                                </GroupBox>
+                                            </ControlTemplate>
+                                        </Setter.Value>
+                                    </Setter>
+                                </Style>
+                            </GroupStyle.ContainerStyle>
+                        </GroupStyle>
+                    </DataGrid.GroupStyle>-->
+
+                    <ui:TreeDataGrid.Columns>
+                        <DataGridTemplateColumn IsReadOnly="True" Width="Auto">
+                            <DataGridTemplateColumn.CellTemplate>
+                                <DataTemplate>
+                                    <ui:TreeItemContentControl IconLineStroke="Black" TreeData="{Binding }"/>
+                                </DataTemplate>
+                            </DataGridTemplateColumn.CellTemplate>
+                        </DataGridTemplateColumn>
+
+                        <DataGridTextColumn Header="鍚嶇О" Width="1*" Binding="{Binding Data.Name}"></DataGridTextColumn>
+                        <DataGridTextColumn Header="绫诲瀷" Width="1*" Binding="{Binding Data.Type}"></DataGridTextColumn>
+                        <DataGridTextColumn Header="鎻忚堪" Width="7*" Binding="{Binding  Data.Description}"></DataGridTextColumn>
+                        <DataGridTextColumn Header="RunMode" Width="*" Binding="{Binding Data.RunMode}"></DataGridTextColumn>
+                    </ui:TreeDataGrid.Columns>
+                </ui:TreeDataGrid>
+
+            </pu:FormGroup>
+
+            <pu:FormGroup Header="{DynamicResource se.propertySetting}" Orientation="Vertical" 
+                          Grid.Row="2" Padding="0,10" Grid.Column="2">
+                <Border x:Name="editPanelContainer" Background="#FFF">
+                </Border>
+            </pu:FormGroup>
+        </Grid>
+    </Border>
+</hb:PluginControl>
diff --git a/Plugin.SeqEditor/SeqEditor.xaml.cs b/Plugin.SeqEditor/SeqEditor.xaml.cs
new file mode 100644
index 0000000..94f8f87
--- /dev/null
+++ b/Plugin.SeqEditor/SeqEditor.xaml.cs
@@ -0,0 +1,70 @@
+using HMI.Base;
+using HMI.UI;
+using Panuon.WPF.UI;
+using Plugin.SeqEditor.Util;
+using Plugin.SeqEditor.ViewModel;
+using System.Windows;
+using System.Windows.Data;
+using System.Windows.Input;
+
+namespace Plugin.SeqEditor;
+
+/// <summary>
+/// Interaction logic for UserControl1.xaml
+/// </summary>
+public partial class SeqEditor : PluginControl
+{
+    public SeqEditor()
+    {
+        InitializeComponent();
+        CbFilter.ItemsSource = (IEnumerable<MultiComboBoxData>)
+            new MultiComboBoxDataConverter().Convert(FilterTypeData.ItemSource,
+            null,null,null
+            );
+    }
+
+    public override void InitFinish()
+    {
+        this.DataContext = new SeqEditorViewModel(this, log);
+    }
+
+    public void BackgroundAction(Action action, string message)
+    {
+        new MaskWindow((WindowX)Window.GetWindow(this),
+                  (win) =>
+                  {
+                      action?.Invoke();
+                  }
+                  ).Info(message);
+    }
+
+    /// <summary>
+    /// 树数据展示过滤
+    /// </summary>
+    /// <param name="sender"></param>
+    /// <param name="e"></param>
+    private void CollectionViewSource_Filter(object sender, FilterEventArgs e)
+    {
+        if (e.Item is TreeItemData data)
+        {
+            e.Accepted = data.IsVisible;
+        }
+    }
+
+    // <summary>
+    /// 树展开/关闭事件
+    /// </summary>
+    /// <param name="sender"></param>
+    /// <param name="e"></param>
+    private void treeDataGrid_ExpandedChange(object sender, bool e)
+    {
+        //CollectionViewSource cvs = this.Resources["TreeGridFilter"] as CollectionViewSource;
+        //cvs?.View.Refresh();
+    }
+
+    private void treeDataGrid_PreviewMouseWheel(object sender, MouseWheelEventArgs e)
+    {
+        e.Handled = false;
+    }
+}
+
diff --git a/Plugin.SeqEditor/SettingPanel/ActionPanel.xaml b/Plugin.SeqEditor/SettingPanel/ActionPanel.xaml
new file mode 100644
index 0000000..0d51042
--- /dev/null
+++ b/Plugin.SeqEditor/SettingPanel/ActionPanel.xaml
@@ -0,0 +1,45 @@
+锘�<UserControl x:Class="Plugin.SeqEditor.SettingPanel.ActionPanel"
+             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
+             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
+             xmlns:local="clr-namespace:Plugin.SeqEditor.SettingPanel"
+             mc:Ignorable="d" 
+             xmlns:hb="clr-namespace:HMI.Base;assembly=HMI.Base"
+             xmlns:uic="clr-namespace:HMI.UI.Converter;assembly=HMI.UI"
+             xmlns:ui="clr-namespace:HMI.UI;assembly=HMI.UI"
+             xmlns:pw="https://opensource.panuon.com/wpf"
+             xmlns:pu="https://opensource.panuon.com/wpf-ui"
+             xmlns:dp="clr-namespace:HMI.UI.DpUtil;assembly=HMI.UI"
+             Padding="5"
+             d:DesignHeight="450" d:DesignWidth="800">
+    <Grid>
+        <StackPanel>
+            <pu:FormGroup Header="{DynamicResource se.stepName}" Orientation="Vertical" 
+                          Style="{StaticResource InputFormGroupStyle}">
+                <TextBox IsReadOnly="True" Style="{StaticResource EditorTextBoxStyle}" Text="{Binding Name}" />
+            </pu:FormGroup>
+
+            <pu:FormGroup Header="{DynamicResource se.stepType}" Orientation="Vertical" 
+                          Style="{StaticResource InputFormGroupStyle}">
+                <TextBox IsReadOnly="True" Style="{StaticResource EditorTextBoxStyle}" Text="{Binding Type,Mode=OneWay}" />
+            </pu:FormGroup>
+
+            <pu:FormGroup Header="{DynamicResource se.runMode}" Orientation="Vertical" 
+                          Style="{StaticResource InputFormGroupStyle}">
+
+                <ComboBox x:Name="CbRunMode" Style="{StaticResource FormComboxStyle}"
+                          VirtualizingPanel.IsVirtualizing="True"
+                SelectedValue="{Binding RunMode}" 
+            DisplayMemberPath="DisplayName" SelectedValuePath="DataValue"/>
+            </pu:FormGroup>
+
+            <!--<pu:FormGroup Header="{DynamicResource se.adapter}" 
+        Orientation="Vertical"
+        Style="{StaticResource InputFormGroupStyle}">
+                <ComboBox x:Name="CbAdapter" Style="{StaticResource FormComboxStyle}" IsEnabled="False" SelectedValue="{Binding Adapter}" 
+                          DisplayMemberPath="DisplayName" SelectedValuePath="DataValue"/>
+            </pu:FormGroup>-->
+        </StackPanel>
+    </Grid>
+</UserControl>
diff --git a/Plugin.SeqEditor/SettingPanel/ActionPanel.xaml.cs b/Plugin.SeqEditor/SettingPanel/ActionPanel.xaml.cs
new file mode 100644
index 0000000..2fd2076
--- /dev/null
+++ b/Plugin.SeqEditor/SettingPanel/ActionPanel.xaml.cs
@@ -0,0 +1,39 @@
+锘縰sing CommunityToolkit.Mvvm.ComponentModel;
+using NationalInstruments.TestStand.Interop.API;
+using Plugin.SeqEditor.Entity;
+using Plugin.SeqEditor.Util;
+using System.ComponentModel;
+using System.Windows.Controls;
+
+namespace Plugin.SeqEditor.SettingPanel
+{
+    /// <summary>
+    /// CommonProperty.xaml 鐨勪氦浜掗�昏緫
+    /// </summary>
+    [INotifyPropertyChanged]
+    public partial class ActionPanel : UserControl
+    {
+        public event EventHandler SettingChanged;
+
+        public StepUI stepUI;
+        public Step step;
+        public PropertyObject propertyObject;
+
+        protected void CustomPropertyChanged(string name)
+        {
+            OnPropertyChanged(name);
+            SettingChanged?.Invoke(this, EventArgs.Empty);
+        }
+
+        public ActionPanel(StepUI stepUI)
+        {
+            this.stepUI = stepUI;
+            this.step = stepUI.step;
+            propertyObject = this.step?.AsPropertyObject();
+            this.DataContext = stepUI;
+            InitializeComponent();
+            CbRunMode.ItemsSource = RunModeData.ItemSource;
+            //CbAdapter.ItemsSource = StepAdapterData.ItemSource;
+        }
+    }
+}
diff --git a/Plugin.SeqEditor/SettingPanel/CommonProperty.xaml b/Plugin.SeqEditor/SettingPanel/CommonProperty.xaml
new file mode 100644
index 0000000..2c44659
--- /dev/null
+++ b/Plugin.SeqEditor/SettingPanel/CommonProperty.xaml
@@ -0,0 +1,25 @@
+锘�<UserControl x:Class="Plugin.SeqEditor.SettingPanel.CommonProperty"
+             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
+             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
+             xmlns:local="clr-namespace:Plugin.SeqEditor.SettingPanel"
+             mc:Ignorable="d" 
+             d:DesignHeight="450" d:DesignWidth="800">
+    <Grid>
+        <StackPanel>
+            <Label>Name</Label>
+            <TextBox Text="{Binding Name}"/>
+
+            <Label>Type</Label>
+            <TextBox IsEnabled="False" Text="{Binding Type,Mode=OneWay}"/>
+
+            <Label>Adapter</Label>
+            <ComboBox x:Name="CbAdapter" 
+                      IsEnabled="{Binding CanChangeAdapter}"
+                      SelectedValue="{Binding Adapter}"
+                      DisplayMemberPath="DisplayName" SelectedValuePath="DataValue"/>
+        </StackPanel>
+
+    </Grid>
+</UserControl>
diff --git a/Plugin.SeqEditor/SettingPanel/CommonProperty.xaml.cs b/Plugin.SeqEditor/SettingPanel/CommonProperty.xaml.cs
new file mode 100644
index 0000000..deaed82
--- /dev/null
+++ b/Plugin.SeqEditor/SettingPanel/CommonProperty.xaml.cs
@@ -0,0 +1,45 @@
+锘縰sing CommunityToolkit.Mvvm.ComponentModel;
+using NationalInstruments.TestStand.Interop.API;
+using System.ComponentModel;
+using System.Windows.Controls;
+using Plugin.SeqEditor.Constant;
+using Plugin.SeqEditor.Entity;
+using Plugin.SeqEditor.Util;
+
+namespace Plugin.SeqEditor.SettingPanel
+{
+    /// <summary>
+    /// CommonProperty.xaml 鐨勪氦浜掗�昏緫
+    /// </summary>
+    [INotifyPropertyChanged]
+    public partial class CommonProperty : UserControl
+    {
+        public event EventHandler SettingChanged;
+
+        public StepUI stepUI;
+        public Step step;
+        public PropertyObject propertyObject;
+
+        protected void CustomPropertyChanged(string name)
+        {
+            OnPropertyChanged(name);
+            SettingChanged?.Invoke(this, EventArgs.Empty);
+        }
+
+        public CommonProperty()
+        {
+            DataContext = this;
+            InitializeComponent();
+            CbAdapter.ItemsSource = StepAdapterData.ItemSource;
+        }
+
+        public void RefreshStep(StepUI stepUI)
+        {
+            this.stepUI = stepUI;
+            this.step = stepUI.step;
+            propertyObject = this.step?.AsPropertyObject();
+            this.DataContext = stepUI;
+        }
+
+    }
+}
diff --git a/Plugin.SeqEditor/SettingPanel/NumValTestPanel.xaml b/Plugin.SeqEditor/SettingPanel/NumValTestPanel.xaml
new file mode 100644
index 0000000..24016dd
--- /dev/null
+++ b/Plugin.SeqEditor/SettingPanel/NumValTestPanel.xaml
@@ -0,0 +1,50 @@
+锘�<UserControl x:Class="Plugin.SeqEditor.SettingPanel.NumValTestPanel"
+             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
+             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
+             xmlns:local="clr-namespace:Plugin.SeqEditor.SettingPanel"
+             mc:Ignorable="d" 
+             
+             Padding="5"
+             xmlns:hb="clr-namespace:HMI.Base;assembly=HMI.Base"
+             xmlns:uic="clr-namespace:HMI.UI.Converter;assembly=HMI.UI"
+             xmlns:ui="clr-namespace:HMI.UI;assembly=HMI.UI"
+             xmlns:pw="https://opensource.panuon.com/wpf"
+             xmlns:pu="https://opensource.panuon.com/wpf-ui"
+             xmlns:dp="clr-namespace:HMI.UI.DpUtil;assembly=HMI.UI"
+             d:DesignHeight="450" d:DesignWidth="800">
+    <StackPanel>
+        <pu:FormGroup Header="{DynamicResource se.runMode}" 
+       Orientation="Vertical"
+       Style="{StaticResource InputFormGroupStyle}">
+
+            <ComboBox x:Name="CbRunMode" 
+       Style="{StaticResource FormComboxStyle}"
+       SelectedValue="{Binding RunMode}" 
+   DisplayMemberPath="DisplayName" SelectedValuePath="DataValue"/>
+        </pu:FormGroup>
+
+        <pu:FormGroup Header="{DynamicResource se.compType}" 
+       Orientation="Vertical"
+       Style="{StaticResource InputFormGroupStyle}">
+
+            <ComboBox x:Name="CbType" 
+       Style="{StaticResource FormComboxStyle}" SelectedValue="{Binding ComparisonType}"
+          DisplayMemberPath="DisplayName"
+          SelectedValuePath="DataValue"/>
+        </pu:FormGroup>
+
+        <pu:FormGroup Header="Low" 
+Orientation="Vertical"
+Style="{StaticResource InputFormGroupStyle}">
+            <ui:NumberInput Style="{StaticResource EditorTextBoxStyle}" Text="{Binding Low}"/>
+        </pu:FormGroup>
+
+        <pu:FormGroup Header="High" 
+Orientation="Vertical"
+Style="{StaticResource InputFormGroupStyle}">
+            <ui:NumberInput Style="{StaticResource EditorTextBoxStyle}" Text="{Binding High}"/>
+        </pu:FormGroup>
+    </StackPanel>
+</UserControl>
diff --git a/Plugin.SeqEditor/SettingPanel/NumValTestPanel.xaml.cs b/Plugin.SeqEditor/SettingPanel/NumValTestPanel.xaml.cs
new file mode 100644
index 0000000..e6dc157
--- /dev/null
+++ b/Plugin.SeqEditor/SettingPanel/NumValTestPanel.xaml.cs
@@ -0,0 +1,20 @@
+锘縰sing Plugin.SeqEditor.Entity.EditablePO;
+using Plugin.SeqEditor.Util;
+using System.Windows.Controls;
+
+namespace Plugin.SeqEditor.SettingPanel
+{
+    /// <summary>
+    /// NumValTestPanel.xaml 鐨勪氦浜掗�昏緫
+    /// </summary>
+    public partial class NumValTestPanel : UserControl
+    {
+        public NumValTestPanel(NumValTestEPO epo)
+        {
+            DataContext = epo;
+            InitializeComponent();
+            CbRunMode.ItemsSource = RunModeData.ItemSource;
+            CbType.ItemsSource = NumCompTypeData.ItemSource;
+        }
+    }
+}
diff --git a/Plugin.SeqEditor/SettingPanel/StringValTestPanel.xaml b/Plugin.SeqEditor/SettingPanel/StringValTestPanel.xaml
new file mode 100644
index 0000000..7a8c260
--- /dev/null
+++ b/Plugin.SeqEditor/SettingPanel/StringValTestPanel.xaml
@@ -0,0 +1,57 @@
+锘�<UserControl x:Class="Plugin.SeqEditor.SettingPanel.StringValTestPanel"
+             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
+             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
+             xmlns:local="clr-namespace:Plugin.SeqEditor.SettingPanel"
+             mc:Ignorable="d" 
+             Padding="5"
+             xmlns:hb="clr-namespace:HMI.Base;assembly=HMI.Base"
+             xmlns:uic="clr-namespace:HMI.UI.Converter;assembly=HMI.UI"
+             xmlns:ui="clr-namespace:HMI.UI;assembly=HMI.UI"
+             xmlns:pw="https://opensource.panuon.com/wpf"
+             xmlns:pu="https://opensource.panuon.com/wpf-ui"
+             xmlns:dp="clr-namespace:HMI.UI.DpUtil;assembly=HMI.UI"
+             d:DesignHeight="450" d:DesignWidth="800">
+    <StackPanel>
+        <pu:FormGroup Header="{DynamicResource se.stepName}" 
+        Orientation="Vertical"
+        Style="{StaticResource InputFormGroupStyle}">
+
+            <TextBox IsReadOnly="True" Style="{StaticResource EditorTextBoxStyle}" Text="{Binding StepName}" />
+        </pu:FormGroup>
+
+        <pu:FormGroup Header="{DynamicResource se.stepType}" 
+Orientation="Vertical"
+Style="{StaticResource InputFormGroupStyle}">
+
+            <TextBox IsReadOnly="True" Style="{StaticResource EditorTextBoxStyle}" Text="{Binding StepType,Mode=OneWay}" />
+        </pu:FormGroup>
+
+        <pu:FormGroup Header="{DynamicResource se.runMode}" 
+               Orientation="Vertical"
+               Style="{StaticResource InputFormGroupStyle}">
+
+            <ComboBox x:Name="CbRunMode" 
+               Style="{StaticResource FormComboxStyle}"
+               SelectedValue="{Binding RunMode}" 
+           DisplayMemberPath="DisplayName" SelectedValuePath="DataValue"/>
+        </pu:FormGroup>
+
+        <pu:FormGroup Header="{DynamicResource se.compType}" 
+       Orientation="Vertical"
+       Style="{StaticResource InputFormGroupStyle}">
+
+            <ComboBox x:Name="CbType"
+                      Style="{StaticResource FormComboxStyle}" SelectedValue="{Binding ComparisonType}"
+          DisplayMemberPath="DisplayName" SelectedValuePath="DataValue"/>
+        </pu:FormGroup>
+
+        <pu:FormGroup Header="{DynamicResource se.targetVal}" 
+Orientation="Vertical"
+Style="{StaticResource InputFormGroupStyle}">
+
+            <TextBox IsReadOnly="{Binding IsReadOnly}" Style="{StaticResource EditorTextBoxStyle}" Text="{Binding StringValue}"/>
+        </pu:FormGroup>
+    </StackPanel>
+</UserControl>
diff --git a/Plugin.SeqEditor/SettingPanel/StringValTestPanel.xaml.cs b/Plugin.SeqEditor/SettingPanel/StringValTestPanel.xaml.cs
new file mode 100644
index 0000000..00c015c
--- /dev/null
+++ b/Plugin.SeqEditor/SettingPanel/StringValTestPanel.xaml.cs
@@ -0,0 +1,21 @@
+锘縰sing System.Windows.Controls;
+using Plugin.SeqEditor.Entity.EditablePO;
+using Plugin.SeqEditor.Util;
+
+namespace Plugin.SeqEditor.SettingPanel
+{
+    /// <summary>
+    /// StringValueTest.xaml 鐨勪氦浜掗�昏緫
+    /// </summary>
+    public partial class StringValTestPanel : UserControl
+    {
+        public StringValTestPanel(StringValTestEPO data)
+        {
+            this.DataContext = data;
+            InitializeComponent();
+
+            CbRunMode.ItemsSource = RunModeData.ItemSource;
+            CbType.ItemsSource = StringCompTypeData.ItemSource;
+        }
+    }
+}
diff --git a/Plugin.SeqEditor/SettingPanel/WaitPanel.xaml b/Plugin.SeqEditor/SettingPanel/WaitPanel.xaml
new file mode 100644
index 0000000..b7f9340
--- /dev/null
+++ b/Plugin.SeqEditor/SettingPanel/WaitPanel.xaml
@@ -0,0 +1,48 @@
+锘�<UserControl x:Class="Plugin.SeqEditor.SettingPanel.WaitPanel"
+             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
+             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
+             xmlns:local="clr-namespace:Plugin.SeqEditor.SettingPanel"
+             mc:Ignorable="d" 
+             xmlns:hb="clr-namespace:HMI.Base;assembly=HMI.Base"
+             xmlns:uic="clr-namespace:HMI.UI.Converter;assembly=HMI.UI"
+             xmlns:ui="clr-namespace:HMI.UI;assembly=HMI.UI"
+             xmlns:pw="https://opensource.panuon.com/wpf"
+             xmlns:pu="https://opensource.panuon.com/wpf-ui"
+             xmlns:dp="clr-namespace:HMI.UI.DpUtil;assembly=HMI.UI"
+             Padding="5"
+             d:DesignHeight="450" d:DesignWidth="800">
+    <StackPanel>
+        <pu:FormGroup Header="{DynamicResource se.stepName}" 
+              Orientation="Vertical"
+              Style="{StaticResource InputFormGroupStyle}">
+
+            <TextBox IsReadOnly="True" Style="{StaticResource EditorTextBoxStyle}" Text="{Binding StepName}" />
+        </pu:FormGroup>
+
+        <pu:FormGroup Header="{DynamicResource se.stepType}" 
+      Orientation="Vertical"
+      Style="{StaticResource InputFormGroupStyle}">
+
+            <TextBox IsReadOnly="True" Style="{StaticResource EditorTextBoxStyle}" Text="{Binding StepType,Mode=OneWay}" />
+        </pu:FormGroup>
+
+        <pu:FormGroup Header="{DynamicResource se.runMode}" 
+                      Orientation="Vertical"
+                      Style="{StaticResource InputFormGroupStyle}">
+
+            <ComboBox x:Name="CbRunMode" 
+                      Style="{StaticResource FormComboxStyle}"
+                      SelectedValue="{Binding RunMode}" 
+                  DisplayMemberPath="DisplayName" SelectedValuePath="DataValue"/>
+        </pu:FormGroup>
+
+        <pu:FormGroup Header="{DynamicResource se.waitInterval}" 
+              Orientation="Vertical"
+              Style="{StaticResource InputFormGroupStyle}">
+            <ui:NumberInput Style="{StaticResource EditorTextBoxStyle}" 
+                            Text="{Binding TimeInterval,UpdateSourceTrigger=PropertyChanged}"/>
+        </pu:FormGroup>
+    </StackPanel>
+</UserControl>
diff --git a/Plugin.SeqEditor/SettingPanel/WaitPanel.xaml.cs b/Plugin.SeqEditor/SettingPanel/WaitPanel.xaml.cs
new file mode 100644
index 0000000..31abae1
--- /dev/null
+++ b/Plugin.SeqEditor/SettingPanel/WaitPanel.xaml.cs
@@ -0,0 +1,19 @@
+锘縰sing Plugin.SeqEditor.Entity.EditablePO;
+using Plugin.SeqEditor.Util;
+using System.Windows.Controls;
+
+namespace Plugin.SeqEditor.SettingPanel
+{
+    /// <summary>
+    /// WaitPanel.xaml 鐨勪氦浜掗�昏緫
+    /// </summary>
+    public partial class WaitPanel : UserControl
+    {
+        public WaitPanel(WaitEPO epo)
+        {
+            this.DataContext = epo;
+            InitializeComponent();
+            CbRunMode.ItemsSource = RunModeData.ItemSource;
+        }
+    }
+}
diff --git a/Plugin.SeqEditor/Util/ItemData.cs b/Plugin.SeqEditor/Util/ItemData.cs
new file mode 100644
index 0000000..3b81e9c
--- /dev/null
+++ b/Plugin.SeqEditor/Util/ItemData.cs
@@ -0,0 +1,147 @@
+锘縰sing HMI.Core.Util;
+using NationalInstruments.TestStand.Interop.API;
+
+namespace Plugin.SeqEditor.Util
+{
+    public class ItemData
+    {
+        public string DisplayName { get; private set; }
+        public string DataValue { get; private set; }
+
+        public ItemData(string displayName, string dataValue)
+        {
+            this.DisplayName = displayName;
+            this.DataValue = dataValue;
+        }
+    }
+
+
+    public abstract class StringCompTypeData
+    {
+        public static readonly ItemData None =
+            new(LangUtil.Get("se.data.none"), "LOG");
+
+        public static readonly ItemData SCCS =
+            new(LangUtil.Get("se.strComp.SCCS"), "CaseSensitive");
+        public static readonly ItemData SCIC =
+            new(LangUtil.Get("se.strComp.SCIC"), "IgnoreCase");
+
+        public static readonly ItemData RECS =
+            new(LangUtil.Get("se.strComp.RECS"), "RegularExpressionCaseSensitive");
+        public static readonly ItemData REIC =
+            new(LangUtil.Get("se.strComp.REIC"), "IgnoreCase");
+
+        public static readonly List<ItemData> ItemSource =
+            new List<ItemData> {
+                None,SCCS,SCIC,//RECS,REIC
+            };
+    }
+
+    public abstract class FilterTypeData
+    {
+        public static readonly ItemData Wait =
+            new(LangUtil.Get("Wait"), "NI_Wait");
+
+        public static readonly ItemData Action =
+            new(LangUtil.Get("Action"), "Action");
+        public static readonly ItemData StrValTest =
+            new(LangUtil.Get("String Val Test"), "StringValueTest");
+
+        public static readonly ItemData NumValTest =
+            new(LangUtil.Get("Numeric Limit Test"), "NumericLimitTest");
+
+        public static readonly ItemData RECS =
+            new(LangUtil.Get("SequenceCall"), "SequenceCall");
+
+        public static readonly List<ItemData> ItemSource =
+            new List<ItemData> {
+                Wait,Action,StrValTest,//RECS,REIC
+            };
+    }
+
+    public abstract class NumCompTypeData
+    {
+
+        public static readonly ItemData EQ =
+            new("= Low", "EQ");
+        public static readonly ItemData NE =
+            new("!= Low", "NE");
+
+        public static readonly ItemData GT =
+            new("> Low", "GT");
+
+        public static readonly ItemData LT =
+            new("< Low", "LT");
+
+        public static readonly ItemData GE =
+            new(">= Low", "GE");
+
+        public static readonly ItemData LE =
+            new("<= Low", "LE");
+
+        public static readonly ItemData GTLT =
+            new("> Low锛�< High", "GTLT");
+
+        public static readonly ItemData GELE =
+            new(">=Low锛�<= High", "GELE");
+        public static readonly ItemData GELT =
+            new(">= Low锛�< High", "GELT");
+        public static readonly ItemData GTLE =
+            new("> Low锛�<= High", "GTLE");
+
+
+        public static readonly ItemData LTGT =
+            new("< Low OR > High", "LTGT");
+        public static readonly ItemData LEGE =
+            new("<= Low OR >= High", "LEGE");
+        public static readonly ItemData LEGT =
+            new("<= Low OR > High", "LEGT");
+        public static readonly ItemData LTGE =
+            new("< Low OR >= High", "LTGE");
+
+        public static readonly List<ItemData> ItemSource =
+            new List<ItemData> {
+                EQ,NE,GT,LT,GE,LE,GTLT,GELE,GELT,GTLE,LTGT,LEGE,LEGT,LTGE
+            };
+    }
+
+    public abstract class RunModeData
+    {
+        public static readonly ItemData Normal =
+            new(LangUtil.Get("se.normal"), "Normal");
+
+        public static readonly ItemData Skip =
+            new(LangUtil.Get("se.skip"), "Skip");
+
+        public static readonly ItemData ForcePass =
+            new(LangUtil.Get("se.pass"), "Pass");
+
+        public static readonly ItemData ForceFail =
+            new(LangUtil.Get("se.fail"), "Fail");
+
+        public static readonly List<ItemData> ItemSource =
+            new List<ItemData> {
+                Normal,Skip,ForcePass,ForceFail
+            };
+    }
+
+    public abstract class StepAdapterData
+    {
+        public static readonly ItemData LabView = new("LabVIEW", AdapterKeyNames.FlexLVAdapterKeyName);
+        public static readonly ItemData LabWindows = new("LabWindows/CVI",
+            AdapterKeyNames.FlexCVIAdapterKeyName);
+        public static readonly ItemData CCPlus = new("C/C++ DLL", AdapterKeyNames.FlexCAdapterKeyName);
+        public static readonly ItemData DotNet = new(".NET", AdapterKeyNames.DotNetAdapterKeyname);
+        public static readonly ItemData ActiveX = new("ActiveX/COM", AdapterKeyNames.AutomationAdapterKeyName);
+        public static readonly ItemData None = new("None", AdapterKeyNames.NoneAdapterKeyName);
+        public static readonly ItemData LabViewNGX = new("LabVIEW NXG", AdapterKeyNames.LabVIEWNXGAdapterKeyName);
+        public static readonly ItemData Python = new("Python", AdapterKeyNames.PythonAdapterKeyName);
+        public static readonly ItemData SequenceCall =
+            new("SequenceCall", AdapterKeyNames.SequenceAdapterKeyName);
+
+        public static readonly List<ItemData> ItemSource =
+            new List<ItemData> {
+                LabView,LabWindows,CCPlus,DotNet,ActiveX,None,LabViewNGX,Python,SequenceCall
+            };
+    }
+}
diff --git a/Plugin.SeqEditor/Util/PoExplain.cs b/Plugin.SeqEditor/Util/PoExplain.cs
new file mode 100644
index 0000000..3d828ff
--- /dev/null
+++ b/Plugin.SeqEditor/Util/PoExplain.cs
@@ -0,0 +1,44 @@
+锘縰sing NationalInstruments.TestStand.Interop.API;
+
+namespace Plugin.SeqEditor.Util
+{
+    public static class PoExtensions
+    {
+        public static string GetValueString(this PropertyObject po)
+        {
+            var lookUpStr = string.Empty;
+            switch (po.Type.ValueType)
+            {
+                case PropertyValueTypes.PropValType_String:
+                    return po.GetValString(lookUpStr, 0);
+                case PropertyValueTypes.PropValType_Boolean:
+                    return po.GetValBoolean(lookUpStr, 0).ToString();
+                case PropertyValueTypes.PropValType_Number:
+                    return po.GetValNumber(lookUpStr, 0).ToString();
+                default:
+                    return string.Empty;
+            }
+        }
+
+        public static void SetValueString(this PropertyObject po, string value)
+        {
+            var lookUpStr = string.Empty;
+            switch (po.Type.ValueType)
+            {
+                case PropertyValueTypes.PropValType_String:
+                    po.SetValString(lookUpStr, 0, value);
+                    break;
+                case PropertyValueTypes.PropValType_Boolean:
+                    bool boolVal = bool.Parse(value);
+                    po.SetValBoolean(lookUpStr, 0, boolVal);
+                    break;
+                case PropertyValueTypes.PropValType_Number:
+                    double doubleVal = double.Parse(value);
+                    po.SetValNumber(lookUpStr, 0, doubleVal);
+                    break;
+                default:
+                    return;
+            }
+        }
+    }
+}
diff --git a/Plugin.SeqEditor/Util/Step2EPO.cs b/Plugin.SeqEditor/Util/Step2EPO.cs
new file mode 100644
index 0000000..7f3bff8
--- /dev/null
+++ b/Plugin.SeqEditor/Util/Step2EPO.cs
@@ -0,0 +1,44 @@
+锘縰sing System.Windows.Controls;
+using Plugin.SeqEditor.Constant;
+using Plugin.SeqEditor.Entity;
+using Plugin.SeqEditor.Entity.EditablePO;
+using Plugin.SeqEditor.SettingPanel;
+
+namespace Plugin.SeqEditor.Util
+{
+    public class StepUI2EPO
+    {
+        public static object Convert2EPO(StepUI step)
+        {
+            switch (step.Type)
+            {
+                case StepTypeStr.StringValueTest:
+                    return new StringValTestEPO(step);
+                default:
+                    return null;
+            }
+        }
+
+        public static UserControl Convert2EditPanel(StepUI step)
+        {
+            if (step == null) return null;
+            object epo;
+            switch (step.Type)
+            {
+                case StepTypeStr.StringValueTest:
+                    epo = new StringValTestEPO(step);
+                    return new StringValTestPanel((StringValTestEPO)epo);
+                case StepTypeStr.NI_Wait:
+                    epo = new WaitEPO(step);
+                    return new WaitPanel((WaitEPO)epo);
+                case StepTypeStr.NumericLimitTest:
+                    epo = new NumValTestEPO(step);
+                    return new NumValTestPanel((NumValTestEPO)epo);
+                case StepTypeStr.Action:
+                    return new ActionPanel(step);
+                default:
+                    return null;
+            }
+        }
+    }
+}
diff --git a/Plugin.SeqEditor/ViewModel/SeqEditorViewModel.cs b/Plugin.SeqEditor/ViewModel/SeqEditorViewModel.cs
new file mode 100644
index 0000000..b369169
--- /dev/null
+++ b/Plugin.SeqEditor/ViewModel/SeqEditorViewModel.cs
@@ -0,0 +1,454 @@
+锘縰sing CommunityToolkit.Mvvm.ComponentModel;
+using CommunityToolkit.Mvvm.Input;
+using HMI.Core.Util;
+using HMI.UI;
+using HMI.UI.Util;
+using log4net;
+using Microsoft.Win32;
+using NationalInstruments.TestStand.Interop.API;
+using Plugin.SeqEditor.Constant;
+using Plugin.SeqEditor.Entity;
+using Plugin.SeqEditor.Util;
+using System.Collections.ObjectModel;
+using System.IO;
+
+namespace Plugin.SeqEditor.ViewModel
+{
+
+    public partial class SeqEditorViewModel : ObservableObject
+    {
+        private ObservableCollection<StepUI> setupSteps = new ObservableCollection<StepUI>();
+
+        private ObservableCollection<StepUI> mainSteps = new ObservableCollection<StepUI>();
+
+        private ObservableCollection<StepUI> cleanupSteps = new ObservableCollection<StepUI>();
+
+        [ObservableProperty]
+        private ObservableCollection<StepUI> allSteps = new ObservableCollection<StepUI>();
+
+        [ObservableProperty]
+        private ObservableCollection<SeqUI> sequences = new ObservableCollection<SeqUI>();
+
+        [ObservableProperty]
+        private SeqUI currentSeqUI;
+
+        [ObservableProperty]
+        private StepUI currentStepUI;
+
+        [ObservableProperty]
+        private string currentFileName;
+
+        [ObservableProperty]
+        private string currentSeqTitle = LangUtil.Get("se.steps");
+
+        [ObservableProperty]
+        private string targetType = null;
+
+
+        private string currentFilePath;
+        private Engine engine;
+        private SequenceFile sequenceFile;
+        private bool isLoaded;
+        private ILog log;
+        public SeqEditor control { get; private set; }
+
+        public List<ItemData> Types = FilterTypeData.ItemSource;
+
+        private HashSet<string> edits = new HashSet<string> {
+            StepTypeStr.PassFailTest,
+            StepTypeStr.NumericLimitTest,
+            StepTypeStr.NI_MultipleNumericLimitTest,
+            StepTypeStr.StringValueTest,
+            StepTypeStr.Action,
+            StepTypeStr.NI_Wait,
+            StepTypeStr.SequenceCall,
+        };
+
+        public SeqEditorViewModel(SeqEditor control, ILog log)
+        {
+            this.log = log;
+            this.control = control;
+            engine = new Engine();
+            engine.LoadTypePaletteFilesEx();
+        }
+
+        ~SeqEditorViewModel()
+        {
+            if (engine != null)
+            {
+                if (sequenceFile != null)
+                {
+                    engine.ReleaseSequenceFileEx(sequenceFile);
+                }
+                engine.ShutDown(true);
+                // 涓嶅姞寤舵椂浼氬脊绐梞d
+                System.Threading.Thread.Sleep(2500);
+            }
+        }
+
+        private ObservableCollection<StepUI> LoadSteps(Sequence seq, string group = null)
+        {
+            //setupSteps.Clear();
+            //var steps = new ObservableCollection<StepUI>();
+            //int num = seq.GetNumSteps(StepGroups.StepGroup_Setup);
+            //for (int i = 0; i < num; i++)
+            //{
+            //    Step step = seq.GetStep(i, StepGroups.StepGroup_Setup);
+            //    var ui = new StepUI(step);
+            //    if (edits.Contains(ui.Type))
+            //    {
+            //        setupSteps.Add(ui);
+            //        steps.Add(ui);
+            //    }
+            //}
+            ////SetupSteps = steps;
+            ////steps = new ObservableCollection<StepUI>();
+            //mainSteps.Clear();
+            //num = seq.GetNumSteps(StepGroups.StepGroup_Main);
+            //for (int i = 0; i < num; i++)
+            //{
+            //    Step step = seq.GetStep(i, StepGroups.StepGroup_Main);
+            //    var ui = new StepUI(step);
+            //    if (edits.Contains(ui.Type))
+            //    {
+            //        mainSteps.Add(ui);
+            //        steps.Add(ui);
+            //    }
+            //}
+            ////MainSteps = steps;
+            ////steps = new ObservableCollection<StepUI>();
+            //cleanupSteps.Clear();
+            //num = seq.GetNumSteps(StepGroups.StepGroup_Cleanup);
+            //for (int i = 0; i < num; i++)
+            //{
+            //    Step step = seq.GetStep(i, StepGroups.StepGroup_Cleanup);
+            //    var ui = new StepUI(step);
+            //    if (edits.Contains(ui.Type))
+            //    {
+            //        cleanupSteps.Add(ui);
+            //        steps.Add(ui);
+            //    }
+            //}
+            //AllSteps = steps;
+            //CurrentStepUI = null;
+
+            var steps = new ObservableCollection<StepUI>();
+
+            int num = seq.GetNumSteps(StepGroups.StepGroup_Setup);
+            for (int i = 0; i < num; i++)
+            {
+                Step step = seq.GetStep(i, StepGroups.StepGroup_Setup);
+                var ui = Convert2StepUI(step, group);
+                if (ui != null)
+                {
+                    steps.Add(ui);
+                }
+            }
+
+            num = seq.GetNumSteps(StepGroups.StepGroup_Main);
+            for (int i = 0; i < num; i++)
+            {
+                Step step = seq.GetStep(i, StepGroups.StepGroup_Main);
+                var ui = Convert2StepUI(step, group);
+                if (ui != null)
+                {
+                    steps.Add(ui);
+                }
+            }
+
+            num = seq.GetNumSteps(StepGroups.StepGroup_Cleanup);
+            for (int i = 0; i < num; i++)
+            {
+                Step step = seq.GetStep(i, StepGroups.StepGroup_Cleanup);
+                var ui = Convert2StepUI(step, group);
+                if (ui != null)
+                {
+                    steps.Add(ui);
+                }
+            }
+            return steps;
+        }
+
+        private StepUI Convert2StepUI(Step step, string group)
+        {
+            var ui = new StepUI(step);
+            if (!edits.Contains(ui.Type))
+            {
+                return null;
+            }
+
+            if (string.IsNullOrEmpty(group))
+            {
+                if (ui.StepGroup == StepGroups.StepGroup_Setup)
+                {
+                    group = "Setup";
+                }
+                else if (ui.StepGroup == StepGroups.StepGroup_Main)
+                {
+                    group = "Main";
+                }
+                else if (ui.StepGroup == StepGroups.StepGroup_Cleanup)
+                {
+                    group = "Cleanup";
+                }
+                else
+                {
+                    group = "Unkown";
+                }
+            }
+            ui.Group = group;
+
+            if (ui.Type == StepTypeStr.SequenceCall)
+            {
+                var po = step.Module.AsPropertyObject();
+                bool useCurFile = po.GetValBoolean("UseCurFile", 0);
+                if (useCurFile)
+                {
+                    string seqName = po.GetValString("SeqName", 0);
+                    Sequence sequence = sequenceFile.GetSequenceByName(seqName);
+                    if (sequence == null)
+                    {
+                        return ui;
+                    }
+                    ui.Children = LoadSteps(sequence, group);
+                }
+            }
+            return ui;
+        }
+
+
+        [RelayCommand]
+        private void Loaded()
+        {
+            if (!isLoaded)
+            {
+            }
+        }
+
+        [RelayCommand]
+        private void OpenSeqFile()
+        {
+            OpenFileDialog ofd = new OpenFileDialog()
+            {
+                Filter = $"{LangUtil.Get("se.seq")}|*.seq;"
+            };
+            if (ofd.ShowDialog() == true)
+            {
+                control.BackgroundAction(() =>
+                {
+                    try
+                    {
+                        var start = DateTime.Now;
+                        currentFilePath = ofd.FileName;
+                        CurrentFileName = $"{LangUtil.Get("se.currentFile")}{Path.GetFileName(currentFilePath)}";
+                        if (sequenceFile != null)
+                        {
+                            engine.ReleaseSequenceFileEx(sequenceFile);
+                        }
+                        sequenceFile = engine.GetSequenceFileEx(currentFilePath);
+                        Sequence main = sequenceFile.GetSequenceByName("MainSequence");
+                        CurrentSeqUI = new SeqUI(main);
+
+                        //var seqs = new ObservableCollection<SeqUI>();
+                        //for (int i = 0; i < sequenceFile.NumSequences; i++)
+                        //{
+                        //    var tsSequence = sequenceFile.GetSequence(i);
+                        //    seqs.Add(new SeqUI(tsSequence));
+                        //}
+                        //Sequences = seqs;
+
+                        if (Sequences != null && Sequences.Count > 0)
+                        {
+                            CurrentSeqUI = Sequences[0];
+                        }
+                        log.Info($"Open File [{currentFilePath}] In {(DateTime.Now - start).TotalMilliseconds}ms");
+                    }
+                    catch (Exception ex)
+                    {
+
+                    }
+                }, LangUtil.Get("se.seqIsLoading"));
+            }
+        }
+
+        [RelayCommand]
+        private void SaveSeqFile()
+        {
+            sequenceFile?.Save(currentFilePath);
+        }
+
+        [RelayCommand]
+        private void Refresh()
+        {
+            if (CurrentSeqUI == null) return;
+            if (string.IsNullOrEmpty(TargetType))
+            {
+                edits = new HashSet<string> {
+                    StepTypeStr.PassFailTest,
+                    StepTypeStr.NumericLimitTest,
+                    StepTypeStr.NI_MultipleNumericLimitTest,
+                    StepTypeStr.StringValueTest,
+                    StepTypeStr.Action,
+                    StepTypeStr.NI_Wait,
+                    StepTypeStr.SequenceCall,
+                };
+            }
+            else
+            {
+                edits = new HashSet<string>(TargetType.Split(','))
+                {
+                    StepTypeStr.SequenceCall
+                };
+            }
+            AllSteps = LoadSteps(CurrentSeqUI.sequence);
+        }
+
+        [RelayCommand]
+        private void CloseSeqFile()
+        {
+            //AllSteps = new ObservableCollection<StepUI>();
+            if (engine != null && sequenceFile != null)
+            {
+                engine.ReleaseSequenceFileEx(sequenceFile);
+                sequenceFile = null;
+            }
+            setupSteps.Clear();
+            mainSteps.Clear();
+            cleanupSteps.Clear();
+            AllSteps = new();
+            Sequences.Clear();
+            currentFilePath = string.Empty;
+            CurrentFileName = string.Empty;
+            CurrentSeqUI = null;
+            CurrentStepUI = null;
+            CurrentSeqTitle = LangUtil.Get("se.steps");
+        }
+
+        [RelayCommand]
+        private void AddStep(string type)
+        {
+            try
+            {
+                var group = CurrentStepUI.step.StepGroup;
+
+                ObservableCollection<StepUI> source;
+                if (group == StepGroups.StepGroup_Setup)
+                {
+                    source = setupSteps;
+                }
+                else if (group == StepGroups.StepGroup_Main)
+                {
+                    source = mainSteps;
+                }
+                else
+                {
+                    source = cleanupSteps;
+                }
+                int index = source.IndexOf(CurrentStepUI);
+                if (index < 0)
+                {
+                    return;
+                }
+                Step newStep;
+                switch (type)
+                {
+                    case "Tests.PassFail":
+                        newStep = engine.NewStep(AdapterKeyNames.FlexLVAdapterKeyName, StepTypeStr.PassFailTest);
+                        newStep.Name = DefaultStepName.PassFailTest;
+                        var p = newStep.AsPropertyObject();
+                        string name = p.Name;
+                        var ps = p.GetSubProperties("", 0);
+                        foreach (var pp in ps)
+                        {
+                            string sname = pp.Name;
+                        }
+
+                        break;
+                    case "Tests.NumericLimitTest":
+                        newStep = engine.NewStep(AdapterKeyNames.FlexLVAdapterKeyName, StepTypeStr.NumericLimitTest);
+                        newStep.Name = DefaultStepName.NumericLimitTest;
+                        break;
+                    case "Tests.NI_MultipleNumericLimitTest":
+                        newStep = engine.NewStep(AdapterKeyNames.FlexLVAdapterKeyName, StepTypeStr.NI_MultipleNumericLimitTest);
+                        newStep.Name = DefaultStepName.NI_MultipleNumericLimitTest;
+                        break;
+                    case "Tests.StringValueTest":
+                        newStep = engine.NewStep(AdapterKeyNames.FlexLVAdapterKeyName, StepTypeStr.StringValueTest);
+                        newStep.Name = DefaultStepName.StringValueTest;
+                        break;
+                    case "Actions.LabVIEW":
+                        newStep = engine.NewStep(AdapterKeyNames.FlexLVAdapterKeyName, StepTypeStr.Action);
+                        newStep.Name = DefaultStepName.Action;
+                        break;
+                    case "Actions.LabWindows/CVI":
+                        newStep = engine.NewStep(AdapterKeyNames.FlexCVIAdapterKeyName, StepTypeStr.Action);
+                        newStep.Name = DefaultStepName.Action;
+                        break;
+                    case "Actions.C/C++ DLL":
+                        newStep = engine.NewStep(AdapterKeyNames.FlexCAdapterKeyName, StepTypeStr.Action);
+                        newStep.Name = DefaultStepName.Action;
+                        break;
+                    case "Actions..NET":
+                        newStep = engine.NewStep(AdapterKeyNames.DotNetAdapterKeyname, StepTypeStr.Action);
+                        newStep.Name = DefaultStepName.Action;
+                        break;
+                    case "Actions.ActiveX/COM":
+                        newStep = engine.NewStep(AdapterKeyNames.AutomationAdapterKeyName, StepTypeStr.Action);
+                        newStep.Name = DefaultStepName.Action;
+                        break;
+                    case "Actions.None":
+                        newStep = engine.NewStep(AdapterKeyNames.NoneAdapterKeyName, StepTypeStr.Action);
+                        newStep.Name = DefaultStepName.Action;
+                        break;
+                    case "Actions.LabVIEW NXG":
+                        newStep = engine.NewStep(AdapterKeyNames.LabVIEWNXGAdapterKeyName, StepTypeStr.Action);
+                        newStep.Name = DefaultStepName.Action;
+                        break;
+                    case "Actions.Python":
+                        newStep = engine.NewStep(AdapterKeyNames.PythonAdapterKeyName, StepTypeStr.Action);
+                        newStep.Name = DefaultStepName.Action;
+                        break;
+
+                    default:
+                        return;
+                }
+                CurrentSeqUI.sequence.InsertStep(newStep, index + 1, group);
+                AllSteps = LoadSteps(CurrentSeqUI.sequence);
+            }
+            catch (Exception ex)
+            {
+                log.Error("Insert Step Error.", ex);
+                MsgBoxHelper2.Error(ex.Message);
+            }
+        }
+
+        [ObservableProperty]
+        private TreeItemData treeItemData;
+
+        partial void OnTreeItemDataChanged(TreeItemData data)
+        {
+            CurrentStepUI = data?.Data as StepUI;
+        }
+
+        partial void OnCurrentStepUIChanged(StepUI stepUI)
+        {
+            var panel = StepUI2EPO.Convert2EditPanel(stepUI);
+            control.editPanelContainer.Child = panel;
+            if (stepUI != null)
+            {
+
+                //window.commonP.RefreshStep(stepUI);
+            }
+        }
+
+        partial void OnCurrentSeqUIChanged(SeqUI seqUI)
+        {
+            if (seqUI != null)
+            {
+                CurrentStepUI = null;
+                AllSteps = LoadSteps(seqUI.sequence);
+            }
+            CurrentSeqTitle = $"{seqUI?.Name} {LangUtil.Get("se.steps")}";
+        }
+    }
+}
diff --git a/Plugin.TestResult/SingleCell.xaml b/Plugin.TestResult/SingleCell.xaml
index c374a6e..57c824f 100644
--- a/Plugin.TestResult/SingleCell.xaml
+++ b/Plugin.TestResult/SingleCell.xaml
@@ -5,6 +5,8 @@
              xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
              xmlns:local="clr-namespace:Plugin.TestResult_WPF"
              mc:Ignorable="d" 
+             xmlns:pw="https://opensource.panuon.com/wpf"
+xmlns:pu="https://opensource.panuon.com/wpf-ui"
              x:Name="uc"
              Padding="5"
              MouseDoubleClick="UserControl_MouseDoubleClick"
@@ -17,6 +19,12 @@
         <ToolTip Padding="5" BorderThickness="1" BorderBrush="Black" Background="Yellow" Content="{Binding tooltipContent}"/>
     </UserControl.ToolTip>-->
 
+    <UserControl.ContextMenu>
+        <ContextMenu>
+            <MenuItem IsCheckable="True" IsChecked="{Binding CellIsDisabled,Mode=TwoWay}"  Header="绂佺敤"/>
+        </ContextMenu>
+    </UserControl.ContextMenu>
+
     <UserControl.Style>
         <Style TargetType="UserControl">
             <Style.Triggers>
diff --git a/Plugin.TestResult/SingleCell.xaml.cs b/Plugin.TestResult/SingleCell.xaml.cs
index 08fb6b7..b8b62a7 100644
--- a/Plugin.TestResult/SingleCell.xaml.cs
+++ b/Plugin.TestResult/SingleCell.xaml.cs
@@ -78,6 +78,18 @@
             }
         }
 
+        private bool cellIsDisabled;
+
+        public bool CellIsDisabled
+        {
+            get { return cellIsDisabled; }
+            set
+            {
+                RaiseAndSetIfchanged(ref cellIsDisabled, value);
+            }
+        }
+
+
         public string title => $"{parentName}-{index + 1}";
 
         private string _status = CommonStatus.WAITING;
diff --git a/Plugin.TestResult/TestResultView.xaml.cs b/Plugin.TestResult/TestResultView.xaml.cs
index d8fa337..5f56462 100644
--- a/Plugin.TestResult/TestResultView.xaml.cs
+++ b/Plugin.TestResult/TestResultView.xaml.cs
@@ -1,5 +1,6 @@
 锘縰sing HMI.Core.Util;
 using HMI.UI.Util;
+using NationalInstruments.TestStand.Interop.AdapterAPI;
 using NationalInstruments.TestStand.Interop.API;
 using NationalInstruments.TestStand.Interop.UI.Ax;
 using NationalInstruments.TestStand.Utility;
diff --git a/Plugin.TsExecution/SingleControl.xaml b/Plugin.TsExecution/SingleControl.xaml
index 0aa2f1e..5cfe37e 100644
--- a/Plugin.TsExecution/SingleControl.xaml
+++ b/Plugin.TsExecution/SingleControl.xaml
@@ -8,6 +8,7 @@
              xmlns:pu="https://opensource.panuon.com/wpf-ui"
              xmlns:local="clr-namespace:Plugin.TsExecution"
              mc:Ignorable="d" 
+             xmlns:sys="clr-namespace:System;assembly=mscorlib"
              Loaded="UserControl_Loaded"
              Background="#ccc6c7"
              FontSize="{Binding ActualHeight,RelativeSource={RelativeSource AncestorType=UserControl}, 
@@ -15,6 +16,8 @@
              x:Name="control"
              d:DesignHeight="450" d:DesignWidth="800">
     <UserControl.Resources>
+        <sys:Double x:Key="nanD">NaN</sys:Double>
+
         <Style TargetType="Label" x:Key="baseLabel">
             <Setter Property="HorizontalContentAlignment" Value="Left"/>
             <Setter Property="VerticalContentAlignment" Value="Center"/>
@@ -207,8 +210,7 @@
                   VirtualizingPanel.IsContainerVirtualizable="True"
                   VirtualizingPanel.IsVirtualizing="True" 
                   VirtualizingPanel.VirtualizationMode="Recycling"
-                  RowHeight="{Binding ActualWidth,ElementName=control, 
-Converter={StaticResource fc}, ConverterParameter='35,1093,-1,45'}"
+                  RowHeight="{StaticResource nanD}"
                   ColumnHeaderHeight="{Binding ActualWidth,ElementName=control,
 Converter={StaticResource fc}, ConverterParameter='30,1093,-1,45'}"
                   ItemsSource="{Binding Datasource}"
@@ -249,7 +251,7 @@
                             <TextBlock Text="{DynamicResource Test.Name}"/>
                         </DataGridTextColumn.Header>
                     </DataGridTextColumn>
-                    <DataGridTextColumn Width="*" Binding="{Binding Unit}" 
+                    <DataGridTextColumn Width="100" Binding="{Binding Unit}" 
                                         ElementStyle="{StaticResource TableTextBlock}">
                         <DataGridTextColumn.Header>
                             <TextBlock Text="{DynamicResource Test.Unit}"/>
@@ -273,7 +275,7 @@
                             <TextBlock Text="{DynamicResource Test.LimitUp}"/>
                         </DataGridTextColumn.Header>
                     </DataGridTextColumn>
-                    <DataGridTemplateColumn Width="*">
+                    <DataGridTemplateColumn Width="120">
                         <DataGridTemplateColumn.Header>
                             <TextBlock Text="{DynamicResource Test.Result}"/>
                         </DataGridTemplateColumn.Header>
diff --git a/Plugin.TsExecution/SingleControl.xaml.cs b/Plugin.TsExecution/SingleControl.xaml.cs
index 8b0cfff..c82ecb6 100644
--- a/Plugin.TsExecution/SingleControl.xaml.cs
+++ b/Plugin.TsExecution/SingleControl.xaml.cs
@@ -408,34 +408,37 @@
 
         public void WriteToCSV(string path)
         {
-            try
+            Dispatcher.BeginInvoke(new Action(() =>
             {
-
-                FileInfo fi = new FileInfo(path);
-                if (!fi.Directory.Exists)
+                try
                 {
-                    fi.Directory.Create();  //鏂囦欢涓嶅瓨鍦紝鍒涘缓鏂囦欢
-                }
 
-                using (FileStream fs = new FileStream(path, FileMode.Create, FileAccess.Write))
-                using (StreamWriter sw = new StreamWriter(fs, System.Text.Encoding.UTF8))
-                {
-                    string title = "椤圭洰鍚嶇О,娴嬭瘯鍙傛暟,娴嬭瘯鍊�,涓婇檺,涓嬮檺,鍗曚綅,缁撴灉";
-                    sw.WriteLine(title);
-
-                    if (Datasource != null && Datasource.Count > 0)
+                    FileInfo fi = new FileInfo(path);
+                    if (!fi.Directory.Exists)
                     {
-                        foreach (var data in Datasource)
+                        fi.Directory.Create();  //鏂囦欢涓嶅瓨鍦紝鍒涘缓鏂囦欢
+                    }
+
+                    using (FileStream fs = new FileStream(path, FileMode.Create, FileAccess.Write))
+                    using (StreamWriter sw = new StreamWriter(fs, System.Text.Encoding.UTF8))
+                    {
+                        string title = "椤圭洰鍚嶇О,娴嬭瘯鍙傛暟,娴嬭瘯鍊�,涓婇檺,涓嬮檺,鍗曚綅,缁撴灉";
+                        sw.WriteLine(title);
+
+                        if (Datasource != null && Datasource.Count > 0)
                         {
-                            sw.WriteLine($"{data.Name},{data.Step},{data.Value},{data.LimitUp},{data.LimitDown},{data.Unit},{data.Result}");
+                            foreach (var data in Datasource)
+                            {
+                                sw.WriteLine($"{data.Name},{data.Step},{data.Value},{data.LimitUp},{data.LimitDown},{data.Unit},{data.Result}");
+                            }
                         }
                     }
                 }
-            }
-            catch (Exception ex)
-            {
-                TsExecution.logger.Error("Write CSV File Failed.", ex);
-            }
+                catch (Exception ex)
+                {
+                    TsExecution.logger.Error("Write CSV File Failed.", ex);
+                }
+            }));
         }
 
         public void ShowPausePanel()
diff --git a/Plugin.TsExecution/TsExecution.xaml.cs b/Plugin.TsExecution/TsExecution.xaml.cs
index fb1f427..528df03 100644
--- a/Plugin.TsExecution/TsExecution.xaml.cs
+++ b/Plugin.TsExecution/TsExecution.xaml.cs
@@ -212,7 +212,6 @@
             }
         }
 
-
         public TsExecution()
         {
             DataContext = this;
@@ -283,15 +282,14 @@
                 if (!string.IsNullOrEmpty(lastModel))
                 {
                     var product = Products.FirstOrDefault(p => p.Name == lastModel);
-                    if (product != null) SelectedProduct = product;
-                    goto Write;
+                    if (product != null)
+                    {
+                        SelectedProduct = product;
+                        RefershUI();
+                        return;
+                    }
                 }
-                goto Write;
-
-            Write:
                 SelectedProduct = Products[0];
-                engine?.Globals.SetValString("StationState.ProductModel", 1, Products[0].Name);
-
                 RefershUI();
             }
             SampleStatus = false;
@@ -461,6 +459,8 @@
             fail = Convert.ToInt32(IniFileHelper.ReadIniData("Count", "Fail", "0", CountIniPath));
         }
 
+        private string csvDir;
+
         /// <summary>
         /// 璇诲彇閰嶇疆鏂囦欢閲岀殑鍑犱釜灞炴��
         /// </summary>
@@ -535,6 +535,21 @@
             catch
             {
                 UI_ShowInfoDock = false;
+            }
+
+            try
+            {
+
+                value = ParameterManager.GetCommonParameterByName("TestReportFolder");
+                csvDir = value.Value;
+                if (!Directory.Exists(csvDir))
+                {
+                    Directory.CreateDirectory(csvDir);
+                }
+            }
+            catch
+            {
+                csvDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "TestReport");
             }
 
             //Convert.ToBoolean(IniFileHelper.ReadIniData("Config", "EnableMes", "true", CountIniPath));
@@ -647,8 +662,14 @@
             //MaskWindow mask = new MaskWindow();
             try
             {
+                engine?.Globals.SetValString("StationState.ProductModel", 1, SelectedProduct?.Name);
+                engine?.CommitGlobalsToDisk(promptOnSaveConflicts: false);
                 this.modeSeq = this.engine.GetSequenceFileEx(@"C:\Program Files\National Instruments\TestStand 2020\Components\Models\TestStandModels\ParallelModel.seq");
                 this.testSeq = this.engine.GetSequenceFileEx(this.testSeqFile, 0, TypeConflictHandlerTypes.ConflictHandler_UseGlobalType);
+
+                //var sb = testSeq.GetSequenceByName("MainSequence");
+                //sb.
+
                 this.execution = this.engine.NewExecution(this.testSeq, "Test UUTs", this.modeSeq, false, 0, null, null, null);
                 IsTesting = true;
             }
@@ -831,6 +852,15 @@
             this.tsSockets[index].Barcode = barcode;
         }
 
+        public void SavePositionCSV(int index)
+        {
+            var time = DateTime.Now;
+            string status = this.tsSockets[index].Status == "Passed" ? "PASS" : "FAIL";
+            string csvPath = Path.Combine(csvDir, SelectedProduct?.Name ?? "鏈煡浜у搧", time.ToString("yyyyMMdd"), status,
+                $"{this.tsSockets[index].Barcode}-{time.ToString("HHmmss")}.csv");
+            this.tsSockets[index].WriteToCSV(csvPath);
+        }
+
         /// <summary>
         /// TestStand鐢ㄦ埛浜や簰浜嬩欢
         /// </summary>
@@ -939,7 +969,7 @@
                         this.tsSockets[Index].SetTestProcess(ProcessVal);
                         break;
                     case 10031://瀵煎嚭CSV
-                        this.tsSockets[Index].WriteToCSV(e.uiMsg.StringData);
+                        SavePositionCSV(Index);
                         break;
                     case 10032://鎸変笅鏍囨殏鍋滄祴璇�
                         this.tsSockets[Index].ShowPausePanel();
diff --git a/WPFProject/App.config b/WPFProject/App.config
index d1cf364..603cbae 100644
--- a/WPFProject/App.config
+++ b/WPFProject/App.config
@@ -3,7 +3,7 @@
 	<configSections>
 		<section name="TestResultConfig" type="System.Configuration.SingleTagSectionHandler" />
 	</configSections>
-	<TestResultConfig Row="2" Column="3" PortStart="6660" PositionColumn="3" PositionCount="18" NameStr="A1,A2,A3,B1,B2,B3"/>
+	<TestResultConfig Row="2" Column="3" PortStart="6660" PositionColumn="3" PositionCount="18" NameStr="A1,A2,A3,B1,B2,B3" />
 	<startup>
 		<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
 	</startup>
diff --git a/WPFProject/BuildCmd.bat b/WPFProject/BuildCmd.bat
index c912389..1936efa 100644
--- a/WPFProject/BuildCmd.bat
+++ b/WPFProject/BuildCmd.bat
@@ -1,8 +1,15 @@
-锘縨ove /y "$(TargeDir)HMI.Core.dll" $(TargeDir)Libs\HMI.Core.dll"
+锘縄F "$(ConfigurationName)"=="Release" Confuser.CLI.exe $(TargeDir)..\ConfuserEx\$(TargetFileName).crproj
+
+move /y "$(TargeDir)HMI.Core.dll" $(TargeDir)Libs\HMI.Core.dll"
 move /y "$(TargeDir)HMI.Base.dll" $(TargeDir)Libs\HMI.Base.dll"
 move /y "$(TargeDir)HMI.UI.dll" $(TargeDir)Libs\HMI.UI.dll"
 move /y "$(TargeDir)HMI.Util.dll" $(TargeDir)Libs\HMI.Util.dll"
 move /y "$(TargeDir)ConnectionUtil.dll" $(TargeDir)Libs\ConnectionUtil.dll"
+
+move /y "$(TargeDir)HMI.LogUtil.dll" $(TargeDir)Api\HMI.LogUtil.dll"
+move /y "$(TargeDir)HMI.SerialData.Util.dll" $(TargeDir)Api\HMI.SerialData.Util.dll"
+move /y "$(TargeDir)HMI.Simple.Util.dll" $(TargeDir)Api\HMI.Simple.Util.dll"
+move /y "$(TargeDir)HMI.Util.Base.dll" $(TargeDir)Api\HMI.Util.Base.dll"
 
 move /y "$(TargeDir)HMI.AlarmInfoManage.dll" $(TargeDir)Plugin\HMI.AlarmInfoManage.dll"
 move /y "$(TargeDir)HMI.DataViewer.dll" $(TargeDir)Plugin\HMI.DataViewer.dll"
@@ -18,6 +25,7 @@
 move /y "$(TargeDir)Plugin.TestBuilder.dll" $(TargeDir)Plugin\Plugin.TestBuilder.dll"
 move /y "$(TargeDir)Plugin.DataBoard.dll" $(TargeDir)Plugin\Plugin.DataBoard.dll"
 move /y "$(TargeDir)Plugin.JarUI.dll" $(TargeDir)Plugin\Plugin.JarUI.dll"
+move /y "$(TargeDir)Plugin.SeqEditor.dll" $(TargeDir)Plugin\Plugin.SeqEditor.dll"
 
 setlocal enabledelayedexpansion
 
diff --git a/WPFProject/Lang/en-US/SeqEditor.json b/WPFProject/Lang/en-US/SeqEditor.json
new file mode 100644
index 0000000..e2643bf
--- /dev/null
+++ b/WPFProject/Lang/en-US/SeqEditor.json
@@ -0,0 +1,34 @@
+{
+  "se.title": "Seq Config",
+  "se.file": "File",
+  "se.open": "Open",
+  "se.save": "Save",
+  "se.saveTo": "Save To",
+  "se.close": "Close",
+  "se.stepName": "Step",
+  "se.stepType": "Type",
+  "se.runMode": "Run Mode",
+  "se.waitInterval": "Interval (s)",
+  "se.adapter": "Adapter",
+  "se.seq": "Sequence",
+  "se.name": "Name",
+  "se.type": "Type",
+  "se.description": "Description",
+  "se.propertySetting": "Setting",
+  "se.normal": "Normal",
+  "se.skip": "Skip",
+  "se.pass": "Force to PASS",
+  "se.fail": "Force to FAIL",
+  "se.steps": "Steps",
+  "se.currentFile": "Current File: ",
+  "se.seqIsLoading": "Sequence is loading",
+  "se.compType": "Comparison Type",
+  "se.targetVal": "Target Val",
+
+  "se.data.none": "None",
+  "se.strComp.SCCS": "String Comparsion - Case Sensitive",
+  "se.strComp.SCIC": "String Comparsion - Ignore Case",
+  "se.strComp.RECS": "Regular Expression - Case Sensitive",
+  "se.strComp.REIC": "Regular Expression - Ignore Case"
+
+}
\ No newline at end of file
diff --git a/WPFProject/Lang/zh-CN/SeqEditor.json b/WPFProject/Lang/zh-CN/SeqEditor.json
new file mode 100644
index 0000000..018e449
--- /dev/null
+++ b/WPFProject/Lang/zh-CN/SeqEditor.json
@@ -0,0 +1,34 @@
+{
+  "se.title": "搴忓垪閰嶇疆",
+  "se.file": "鏂囦欢",
+  "se.open": "鎵撳紑",
+  "se.save": "淇濆瓨",
+  "se.saveTo": "鍙﹀瓨",
+  "se.close": "鍏抽棴",
+  "se.stepName": "姝ラ鍚嶇О",
+  "se.stepType": "姝ラ绫诲瀷",
+  "se.runMode": "杩愯璁剧疆",
+  "se.waitInterval": "绛夊緟鏃堕棿锛坰锛�",
+  "se.adapter": "閫傞厤鍣�",
+  "se.seq": "娴嬭瘯搴忓垪",
+  "se.name": "鍚嶇О",
+  "se.type": "绫诲瀷",
+  "se.description": "鎻忚堪",
+  "se.propertySetting": "灞炴�ц缃�",
+  "se.normal": "姝e父",
+  "se.skip": "璺宠繃",
+  "se.pass": "寮哄埗PASS",
+  "se.fail": "寮哄埗FAIL",
+  "se.steps": "娴嬭瘯姝ラ",
+  "se.currentFile": "褰撳墠鏂囦欢锛�",
+  "se.seqIsLoading": "姝e湪鍔犺浇娴嬭瘯搴忓垪",
+  "se.compType": "姣旇緝鏂瑰紡",
+  "se.targetVal": "鏈熸湜鍊�",
+
+  "se.data.none": "鏃�",
+  "se.strComp.SCCS": "姣旇緝瀛楃涓�-澶у皬鍐欐晱鎰�",
+  "se.strComp.SCIC": "姣旇緝瀛楃涓�-蹇界暐澶у皬鍐�",
+  "se.strComp.RECS": "姝e垯琛ㄨ揪寮�-澶у皬鍐欐晱鎰�",
+  "se.strComp.REIC": "姝e垯琛ㄨ揪寮�-蹇界暐澶у皬鍐�"
+
+}
\ No newline at end of file
diff --git a/WPFProject/Lang/zh-CN/app.json b/WPFProject/Lang/zh-CN/app.json
index 79111c2..6b4b85f 100644
--- a/WPFProject/Lang/zh-CN/app.json
+++ b/WPFProject/Lang/zh-CN/app.json
@@ -5,7 +5,7 @@
   "App.Password": "瀵嗙爜",
   "App.UsernameHolder": "璇疯緭鍏ョ敤鎴峰悕",
   "App.PasswordHolder": "璇疯緭鍏ュ瘑鐮�",
-  "App.PleaseWait": "璇风瓑寰�",
+  "App.PleaseWait": "璇风◢鍊�",
   "App.LoginFailed": "鐧婚檰澶辫触",
   "App.ErrorUsernameOrPassword": "鐢ㄦ埛鍚嶆垨瀵嗙爜閿欒",
   "App.IconMustInFolder": "璇峰皢鍥炬爣绉诲姩鑷崇▼搴廔con鐩綍涓�",
@@ -156,5 +156,5 @@
   "App.ScannerSetting": "鎵爜鏋缃�",
   "App.SequenceIsStarting": "姝e湪鍚姩娴嬭瘯搴忓垪锛岃绋嶅悗...",
   "App.Status": "鐘舵��",
-  "App.Info": "淇℃伅"
+  "App.Info": "淇℃伅",
 }
\ No newline at end of file
diff --git a/WPFProject/LoadingWindow.xaml.cs b/WPFProject/LoadingWindow.xaml.cs
index e0c4fc1..4e1ff79 100644
--- a/WPFProject/LoadingWindow.xaml.cs
+++ b/WPFProject/LoadingWindow.xaml.cs
@@ -1,5 +1,4 @@
 锘縰sing HMI.Base;
-using HMI.Core.Api;
 using HMI.UI.Util;
 using Panuon.WPF.UI;
 using System;
@@ -85,9 +84,11 @@
 
         private void Button_Click(object sender, RoutedEventArgs e)
         {
+            this.Closed -= ShutDownWhen_Closed;
             window.Show();
             this.Close();
         }
+
         private PanuonWindow window;
         private double total;
         private double finished;
@@ -116,9 +117,15 @@
                 ShowConsole = true;
                 BtnNext.Visibility = Visibility.Visible;
                 MsgBoxHelper.Error(this, "鍔犺浇鍑洪敊锛岃纭");
+                this.Closed += ShutDownWhen_Closed; ;
             }
         }
 
+        private void ShutDownWhen_Closed(object sender, EventArgs e)
+        {
+            Application.Current.Shutdown();
+        }
+
         public void Add(string text, bool? result)
         {
             Message = text;
diff --git a/WPFProject/WPFProject.csproj b/WPFProject/WPFProject.csproj
index 89e92ba..55d3de6 100644
--- a/WPFProject/WPFProject.csproj
+++ b/WPFProject/WPFProject.csproj
@@ -107,6 +107,9 @@
     <Reference Include="Microsoft.DwayneNeed.Win32">
       <HintPath>..\Libs\Microsoft.DwayneNeed.Win32.dll</HintPath>
     </Reference>
+    <Reference Include="Microsoft.Xaml.Behaviors, Version=1.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
+      <HintPath>packages\Microsoft.Xaml.Behaviors.Wpf.1.1.135\lib\net462\Microsoft.Xaml.Behaviors.dll</HintPath>
+    </Reference>
     <Reference Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
       <HintPath>packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
     </Reference>
@@ -395,6 +398,12 @@
     <Content Include="Lang\en-US\DataBoard.json">
       <CopyToOutputDirectory>Always</CopyToOutputDirectory>
     </Content>
+    <Content Include="Lang\zh-CN\SeqEditor.json">
+      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+    </Content>
+    <Content Include="Lang\en-US\SeqEditor.json">
+      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+    </Content>
     <None Include="packages.config" />
     <None Include="Properties\app.manifest" />
     <None Include="Properties\Settings.settings">
@@ -458,6 +467,10 @@
       <Project>{968fe602-22d6-42bd-981c-601ad518a896}</Project>
       <Name>Plugin.JarUI</Name>
     </ProjectReference>
+    <ProjectReference Include="..\Plugin.SeqEditor\Plugin.SeqEditor.csproj">
+      <Project>{717fd815-ff95-4f30-b4d9-9cea978ca40e}</Project>
+      <Name>Plugin.SeqEditor</Name>
+    </ProjectReference>
     <ProjectReference Include="..\Plugin.ServoManage\Plugin.ServoManage.csproj">
       <Project>{1853e00f-6865-4bc6-974b-6a5e1c00bafe}</Project>
       <Name>Plugin.ServoManage</Name>
@@ -507,11 +520,17 @@
   <ItemGroup />
   <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
   <PropertyGroup>
-    <PostBuildEvent>move /y "$(TargeDir)HMI.Core.dll" $(TargeDir)Libs\HMI.Core.dll"
+    <PostBuildEvent>
+move /y "$(TargeDir)HMI.Core.dll" $(TargeDir)Libs\HMI.Core.dll"
 move /y "$(TargeDir)HMI.Base.dll" $(TargeDir)Libs\HMI.Base.dll"
 move /y "$(TargeDir)HMI.UI.dll" $(TargeDir)Libs\HMI.UI.dll"
 move /y "$(TargeDir)HMI.Util.dll" $(TargeDir)Libs\HMI.Util.dll"
 move /y "$(TargeDir)ConnectionUtil.dll" $(TargeDir)Libs\ConnectionUtil.dll"
+
+move /y "$(TargeDir)HMI.LogUtil.dll" $(TargeDir)Api\HMI.LogUtil.dll"
+move /y "$(TargeDir)HMI.SerialData.Util.dll" $(TargeDir)Api\HMI.SerialData.Util.dll"
+move /y "$(TargeDir)HMI.Simple.Util.dll" $(TargeDir)Api\HMI.Simple.Util.dll"
+move /y "$(TargeDir)HMI.Util.Base.dll" $(TargeDir)Api\HMI.Util.Base.dll"
 
 move /y "$(TargeDir)HMI.AlarmInfoManage.dll" $(TargeDir)Plugin\HMI.AlarmInfoManage.dll"
 move /y "$(TargeDir)HMI.DataViewer.dll" $(TargeDir)Plugin\HMI.DataViewer.dll"
@@ -527,6 +546,7 @@
 move /y "$(TargeDir)Plugin.TestBuilder.dll" $(TargeDir)Plugin\Plugin.TestBuilder.dll"
 move /y "$(TargeDir)Plugin.DataBoard.dll" $(TargeDir)Plugin\Plugin.DataBoard.dll"
 move /y "$(TargeDir)Plugin.JarUI.dll" $(TargeDir)Plugin\Plugin.JarUI.dll"
+move /y "$(TargeDir)Plugin.SeqEditor.dll" $(TargeDir)Plugin\Plugin.SeqEditor.dll"
 
 setlocal enabledelayedexpansion
 
diff --git a/WPFProject/WPFProject.sln b/WPFProject/WPFProject.sln
index 3c6d196..ded9cb2 100644
--- a/WPFProject/WPFProject.sln
+++ b/WPFProject/WPFProject.sln
@@ -63,6 +63,8 @@
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Plugin.JarUI", "..\Plugin.JarUI\Plugin.JarUI.csproj", "{968FE602-22D6-42BD-981C-601AD518A896}"
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Plugin.SeqEditor", "..\Plugin.SeqEditor\Plugin.SeqEditor.csproj", "{717FD815-FF95-4F30-B4D9-9CEA978CA40E}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
@@ -303,6 +305,14 @@
 		{968FE602-22D6-42BD-981C-601AD518A896}.Release|Any CPU.Build.0 = Release|Any CPU
 		{968FE602-22D6-42BD-981C-601AD518A896}.Release|x64.ActiveCfg = Release|Any CPU
 		{968FE602-22D6-42BD-981C-601AD518A896}.Release|x64.Build.0 = Release|Any CPU
+		{717FD815-FF95-4F30-B4D9-9CEA978CA40E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{717FD815-FF95-4F30-B4D9-9CEA978CA40E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{717FD815-FF95-4F30-B4D9-9CEA978CA40E}.Debug|x64.ActiveCfg = Debug|x64
+		{717FD815-FF95-4F30-B4D9-9CEA978CA40E}.Debug|x64.Build.0 = Debug|x64
+		{717FD815-FF95-4F30-B4D9-9CEA978CA40E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{717FD815-FF95-4F30-B4D9-9CEA978CA40E}.Release|Any CPU.Build.0 = Release|Any CPU
+		{717FD815-FF95-4F30-B4D9-9CEA978CA40E}.Release|x64.ActiveCfg = Release|Any CPU
+		{717FD815-FF95-4F30-B4D9-9CEA978CA40E}.Release|x64.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
diff --git a/WPFProject/packages.config b/WPFProject/packages.config
index 80bed4e..b62d154 100644
--- a/WPFProject/packages.config
+++ b/WPFProject/packages.config
@@ -1,6 +1,7 @@
 锘�<?xml version="1.0" encoding="utf-8"?>
 <packages>
   <package id="log4net" version="3.0.4" targetFramework="net472" />
+  <package id="Microsoft.Xaml.Behaviors.Wpf" version="1.1.135" targetFramework="net472" />
   <package id="Newtonsoft.Json" version="13.0.3" targetFramework="net472" />
   <package id="Panuon.WPF" version="1.1.3" targetFramework="net472" />
   <package id="Panuon.WPF.UI" version="1.3.0.2" targetFramework="net472" />

--
Gitblit v1.9.1