From 99df058a665ea04065a23e729ec79a2f23f79881 Mon Sep 17 00:00:00 2001
From: alielie <alielie@git.com>
Date: 星期二, 20 五月 2025 16:59:43 +0800
Subject: [PATCH] 优化

---
 Plugin.DataBoard/Plugin.DataBoard.csproj                  |    1 
 WPFProject/BuildCmd.bat                                   |    9 
 Plugin.JarUI/JarCell.xaml.cs                              |   84 +
 Plugin.JarUI/Properties/Settings.Designer.cs              |   30 
 WPFProject/Api/Api.txt                                    |    1 
 HMI.UI/ScannerSetting.xaml.cs                             |  153 ++
 Plugin.JarUI/Properties/Resources.resx                    |  117 +
 Plugin.JarUI/packages.config                              |    6 
 HMI.Core/Util/MSMQHelper.cs                               |    2 
 HMI.MenuManage/Properties/AssemblyInfo.cs                 |    6 
 HMI.UI/ScannerSetting.xaml                                |   65 
 SimpleUtil/HMI.SerialData.Util/HMI.SerialData.Util.csproj |   26 
 SimpleUtil/HMI.Simple.Util/ProductUtil.cs                 |   70 +
 WPFProject/Lang/zh-CN/app.json                            |   14 
 HMI.MenuManage/Modal/UserModal.xaml.cs                    |   21 
 WPFProject/Lang/en-US/TestExecution.json                  |    5 
 SimpleUtil/HMI.Util.Base/RegUtil.cs                       |   31 
 Plugin.SNGenerateTool/UserControl1.xaml.cs                |    1 
 WPFProject/PanuonWindow.xaml.cs                           |   51 
 HMI.Core/Util/LangUtil.cs                                 |    4 
 HMI.UI/MaskWindow.xaml.cs                                 |   16 
 Plugin.TestResult/ViewControl.xaml.cs                     |    2 
 WPFProject/Window1.xaml.cs                                |    1 
 Plugin.JarUI/Plugin.JarUI.csproj                          |  127 +
 Plugin.TsExecution/TsExecution.xaml.cs                    |  395 ++++-
 HMI.UI/Entity/ScannerConfig.cs                            |   34 
 HMI.UnitTest/MainWindow.xaml.cs                           |   37 
 Plugin.JarUI/JarCell.xaml                                 |   80 +
 HMI.Core/Annotation/ExcelColumn.cs                        |    6 
 WPFProject/SuperSocket.SocketEngine.dll                   |    0 
 Plugin.TsExecution/TsExecution.xaml                       |  132 +
 WPFProject/App.xaml                                       |    2 
 VisionBuilder.Test/PluginOne.cs                           |    6 
 HMI.MenuManage/Model/UserManageModel.cs                   |   35 
 Plugin.DataBoard/DataBoard.xaml                           |    2 
 WPFProject/Properties/app.manifest                        |   64 
 HMI.MenuManage/Entity/UserData.cs                         |   21 
 HMI.UI/Util/MsgBoxHelper.cs                               |   15 
 HMI.UI/DpUtil/UniformGridHelper.cs                        |  108 +
 Plugin.JarUI/Properties/Settings.settings                 |    7 
 HMI.MenuManage/UserManage.xaml.cs                         |   64 
 HMI.Core/Entity/UserInfo.cs                               |    4 
 HMI.Core/Util/PathHelper.cs                               |    2 
 SimpleUtil/HMI.SerialData.Util/SerialDataHelper.cs        |   59 
 HMI.UI/Controls/NumberPanel.xaml.cs                       |    3 
 Plugin.DataBoard/DataBoard.xaml.cs                        |    3 
 SimpleUtil/HMI.Simple.Util/HMI.Simple.Util.csproj         |   12 
 HMI.Core/Manager/ParameterManager.cs                      |   50 
 HMI.UI/Style/FormStyle.xaml                               |    3 
 HMI.Base/PluginLoader.cs                                  |    2 
 SimpleUtil/HMI.LogUtil/Properties/AssemblyInfo.cs         |   33 
 WPFProject/WPFProject.csproj                              |   35 
 WPFProject/Login.xaml.cs                                  |    5 
 HMI.UI/Style/PanuonResource.xaml                          |   13 
 WPFProject/PanuonWindow.xaml                              |   28 
 SimpleUtil/HMI.Simple.Util/ParameterUtil.cs               |  185 ++
 Plugin.TsExecution/SingleControl.xaml                     |  407 ++++-
 WPFProject/WPFProject.sln                                 |   46 
 HMI.UnitTest/App.xaml                                     |    2 
 Plugin.JarUI/JarUI.xaml.cs                                |  498 +++++++
 HMI.MenuManage/HMI.MenuManage.csproj                      |    5 
 Plugin.TsExecution/SingleControl.xaml.cs                  |   68 
 SimpleUtil/HMI.LogUtil/HMI.LogUtil.csproj                 |   62 
 SimpleUtil/HMI.Util.Base/HMI.Util.Base.csproj             |   48 
 HMI.Util/ExcelHelper.cs                                   |    8 
 WPFProject/Lang/zh-CN/TestExecution.json                  |   17 
 HMI.LogViewer/LogViewer.xaml.cs                           |   53 
 HMI.MenuManage/Modal/UserModal.xaml                       |    7 
 HMI.MenuManage/UserManage.xaml                            |   12 
 /dev/null                                                 |   97 -
 Plugin.JarUI/JarUI.xaml                                   |   47 
 Plugin.SNGenerateTool/Form1.cs                            |    2 
 Plugin.JarUI/Properties/Resources.Designer.cs             |   62 
 WPFProject/App.xaml.cs                                    |   46 
 HMI.UnitTest/HMI.UnitTest.csproj                          |    4 
 Plugin.JarUI/Properties/AssemblyInfo.cs                   |   52 
 SimpleUtil/HMI.Util.Base/Properties/AssemblyInfo.cs       |   33 
 HMI.Core/Util/BeanUtil.cs                                 |    1 
 WPFProject/Lang/en-US/app.json                            |   15 
 HMI.Core/FontSizeConverter.cs                             |   64 
 HMI.UI/HMI.UI.csproj                                      |   13 
 SimpleUtil/HMI.LogUtil/LogHelper.cs                       |  112 +
 82 files changed, 3,491 insertions(+), 578 deletions(-)

diff --git a/HMI.Base/PluginLoader.cs b/HMI.Base/PluginLoader.cs
index 6b4ba0b..e63503a 100644
--- a/HMI.Base/PluginLoader.cs
+++ b/HMI.Base/PluginLoader.cs
@@ -166,7 +166,7 @@
             try
             {
                 UserInfo user = UserInfo.User;
-                string sql = $@"SELECT m.* FROM menu AS m INNER JOIN role_menu_rel AS rrel ON m.code = rrel.menuCode
+                string sql = $@"SELECT DISTINCT m.* FROM menu AS m INNER JOIN role_menu_rel AS rrel ON m.code = rrel.menuCode
 INNER JOIN role AS r ON rrel.roleCode = r.code
 INNER JOIN user_role_rel AS urel ON r.code = urel.roleCode
 AND urel.username = '{user.Username}' ORDER BY m.level,m.sort, m.id;";
diff --git a/HMI.Core/Annotation/ExcelColumn.cs b/HMI.Core/Annotation/ExcelColumn.cs
index c6ac1c8..29ae81d 100644
--- a/HMI.Core/Annotation/ExcelColumn.cs
+++ b/HMI.Core/Annotation/ExcelColumn.cs
@@ -6,10 +6,14 @@
     public class ExcelColumnAttribute : Attribute
     {
         public string Name { get; private set; }
+        public bool IgnoreExport { get; private set; }
+        public bool IgnoreImport { get; private set; }
 
-        public ExcelColumnAttribute(string name)
+        public ExcelColumnAttribute(string name, bool ignoreExport = false, bool ignoreImport = false)
         {
             this.Name = name;
+            this.IgnoreExport = ignoreExport;
+            this.IgnoreImport = ignoreImport;
         }
     }
 }
diff --git a/HMI.Core/Entity/UserInfo.cs b/HMI.Core/Entity/UserInfo.cs
index 6357850..c9b644d 100644
--- a/HMI.Core/Entity/UserInfo.cs
+++ b/HMI.Core/Entity/UserInfo.cs
@@ -3,15 +3,17 @@
     public class UserInfo
     {
         public static UserInfo User { get; private set; }
-        public UserInfo(string id, string username, string name, string password = null)
+        public UserInfo(string id, string code, string username, string name, string password = null)
         {
             Id = id;
+            Code = code;
             Username = username;
             Name = name;
             Password = password;
         }
 
         public string Id { get; private set; }
+        public string Code { get; private set; }
         public string Username { get; private set; }
         public string Name { get; private set; }
         public string Password { get; private set; }
diff --git a/HMI.Core/FontSizeConverter.cs b/HMI.Core/FontSizeConverter.cs
index 5ff7876..ecadb25 100644
--- a/HMI.Core/FontSizeConverter.cs
+++ b/HMI.Core/FontSizeConverter.cs
@@ -1,14 +1,51 @@
 锘縰sing System;
-using System.Collections.Generic;
 using System.Globalization;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
 using System.Windows.Data;
 
 namespace HMI.Core
 {
-    public class CustomFontSizeConverter : IValueConverter
+    public class WidthSizeConverter : IValueConverter
+    {
+        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+        {
+            if (parameter == null) return 15;
+
+            string[] ps = parameter.ToString().Split(',');
+            double size = double.Parse(ps[0]);
+            double baseSize = double.Parse(ps[1]);
+            double min = -1, max = -1;
+
+            if (ps.Length >= 3)
+            {
+                min = double.Parse(ps[2]);
+            }
+            if (ps.Length >= 4)
+            {
+                max = double.Parse(ps[3]);
+            }
+
+            if (value is double width)
+            {
+                double res = CountFontSize(size, width, baseSize);
+                res = min > 0 && res < min ? min : res;
+                res = max > 0 && res > max ? max : res;
+                return res;
+            }
+            return 15;
+        }
+
+        public static double CountFontSize(double size, double limit, double baseSize)
+        {
+            return size * limit / baseSize;
+        }
+
+        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+        {
+            throw new NotImplementedException();
+        }
+    }
+
+    public class HeightSizeConverter : IValueConverter
     {
         public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
         {
@@ -17,9 +54,22 @@
             string[] ps = parameter.ToString().Split(',');
             int size = int.Parse(ps[0]);
             int baseSize = int.Parse(ps[1]);
-            if (value is double width)
+            double min = -1, max = -1;
+
+            if (ps.Length >= 3)
             {
-                return CountFontSize(size, width, baseSize);
+                min = double.Parse(ps[2]);
+            }
+            if (ps.Length >= 4)
+            {
+                max = double.Parse(ps[3]);
+            }
+            if (value is double height)
+            {
+                double res = CountFontSize(size, height, baseSize);
+                res = min > 0 && res < min ? min : res;
+                res = max > 0 && res > max ? max : res;
+                return res;
             }
             return 15;
         }
diff --git a/HMI.Core/Manager/ParameterManager.cs b/HMI.Core/Manager/ParameterManager.cs
index 188b08c..77f2d1f 100644
--- a/HMI.Core/Manager/ParameterManager.cs
+++ b/HMI.Core/Manager/ParameterManager.cs
@@ -62,5 +62,55 @@
             if (data == null) return null;
             return data.Value;
         }
+
+        public static bool UpdateCommonParameter(string name, object value, out string errMsg)
+        {
+            errMsg = string.Empty;
+            try
+            {
+                string path = PathHelper.ParameterDataPath;
+                if (!File.Exists(path))
+                {
+                    errMsg = "閰嶇疆鏂囦欢涓嶅瓨鍦�";
+                    return false;
+                }
+                List<ParameterData> datas = XmlHelper.DeserializeFromXml<List<ParameterData>>(path);
+                ParameterData data = datas.FirstOrDefault(d => d.Name == name);
+                if (data != null)
+                {
+                    data.Value = value.ToString();
+                    XmlHelper.SerializeToXml(path, datas);
+                    return true;
+                }
+                string type = "String";
+                if (value is bool)
+                {
+                    type = "Bool";
+                }
+                else if (value is int)
+                {
+                    type = "Int";
+                }
+                else if (value is double)
+                {
+                    type = "Double";
+                }
+
+                data = new ParameterData
+                {
+                    Name = name,
+                    Type = type,
+                    Value = value.ToString()
+                };
+                datas.Add(data);
+                XmlHelper.SerializeToXml(path, datas);
+                return true;
+            }
+            catch (Exception ex)
+            {
+                errMsg = ex.Message;
+                return false;
+            }
+        }
     }
 }
diff --git a/HMI.Core/Util/BeanUtil.cs b/HMI.Core/Util/BeanUtil.cs
index 2c6371d..3e7e5ef 100644
--- a/HMI.Core/Util/BeanUtil.cs
+++ b/HMI.Core/Util/BeanUtil.cs
@@ -2,7 +2,6 @@
 using System.Collections.Generic;
 using System.Data;
 using System.Reflection;
-using System.Xml;
 
 namespace HMI.Core.Util
 {
diff --git a/HMI.Core/Util/LangUtil.cs b/HMI.Core/Util/LangUtil.cs
index 64c004c..abda81f 100644
--- a/HMI.Core/Util/LangUtil.cs
+++ b/HMI.Core/Util/LangUtil.cs
@@ -112,7 +112,7 @@
                         }
                     }
                 }
-                catch (Exception ex)
+                catch
                 {
                 }
             }
@@ -160,7 +160,7 @@
                         }
                     }
                 }
-                catch (Exception ex)
+                catch
                 {
                 }
             }
diff --git a/HMI.Core/Util/MSMQHelper.cs b/HMI.Core/Util/MSMQHelper.cs
index 14c8125..3e331c6 100644
--- a/HMI.Core/Util/MSMQHelper.cs
+++ b/HMI.Core/Util/MSMQHelper.cs
@@ -93,7 +93,7 @@
                 {
                     MsgReceived = this.msgQueue.Receive(Time);
                 }
-                catch(Exception ex)
+                catch
                 {
                     // do nothing
                 }
diff --git a/HMI.Core/Util/PathHelper.cs b/HMI.Core/Util/PathHelper.cs
index ad80093..577dc2e 100644
--- a/HMI.Core/Util/PathHelper.cs
+++ b/HMI.Core/Util/PathHelper.cs
@@ -174,7 +174,7 @@
                     Directory.CreateDirectory(path);
                 }
             }
-            catch (Exception ex)
+            catch
             {
 
             }
diff --git a/HMI.LogViewer/LogViewer.xaml.cs b/HMI.LogViewer/LogViewer.xaml.cs
index 508aecf..bc54659 100644
--- a/HMI.LogViewer/LogViewer.xaml.cs
+++ b/HMI.LogViewer/LogViewer.xaml.cs
@@ -1,6 +1,7 @@
 锘縰sing HMI.Base;
 using HMI.Core.Util;
 using HMI.UI.Util;
+using log4net.Core;
 using System;
 using System.Collections.Generic;
 using System.Data;
@@ -9,6 +10,7 @@
 using System.Timers;
 using System.Windows;
 using System.Windows.Controls;
+using static System.Net.Mime.MediaTypeNames;
 
 namespace HMI.LogViewer
 {
@@ -36,6 +38,13 @@
         public LogViewer()
         {
             DataContext = this;
+            autoRfhTimer = new Timer
+            {
+                Enabled = false,
+                Interval = 1000,
+                AutoReset = false
+            };
+            autoRfhTimer.Elapsed += AutoRfhTimer_Elapsed;
             InitializeComponent();
         }
 
@@ -68,12 +77,6 @@
 
         private void UserControl_Loaded(object sender, RoutedEventArgs e)
         {
-            autoRfhTimer = new Timer
-            {
-                Enabled = false,
-                Interval = 1000
-            };
-            autoRfhTimer.Elapsed += AutoRfhTimer_Elapsed;
             Loaded -= UserControl_Loaded;
         }
 
@@ -222,22 +225,28 @@
         /// </summary>
         private void Refresh100items()
         {
-            //try
-            //{
-            //    if (!File.Exists(todayLogFile)) return;
-            //    string str = "select type,time,source,text from log order by time desc limit 100";
-            //    SqliteHelper sql = new SqliteHelper(todayLogFile);
-            //    var dt = sql.GetDataTable(str);
-            //    this.Dispatcher.BeginInvoke((Action)delegate ()
-            //    {
-            //        table.ItemsSource = dt.DefaultView;
-            //    });
-            //}
-            //catch (Exception ex)
-            //{
-            //    log.Error("Log Auto Refresh Failed.", ex);
-            //    this.Error($"鑷姩鍒锋柊澶辫触锛亄ex.Message}");
-            //}
+            try
+            {
+                autoRfhTimer.Enabled = false;
+                string todayLogFile = Path.Combine(PathHelper.LogViewerDir, $"log_{DateTime.Today:yyyy-MM-dd}.db");
+                if (!File.Exists(todayLogFile)) return;
+                string str = "select level,time,source,text from log order by time desc limit 100";
+                SqliteHelper sql = new SqliteHelper(todayLogFile);
+                var dt = sql.GetDataTable(str);
+                this.Dispatcher.BeginInvoke((Action)delegate ()
+                {
+                    table.ItemsSource = dt.DefaultView;
+                });
+            }
+            catch (Exception ex)
+            {
+                log.Error("Log Auto Refresh Failed.", ex);
+                this.Error($"鑷姩鍒锋柊澶辫触锛亄ex.Message}");
+            }
+            finally
+            {
+                autoRfhTimer.Enabled = AutoRfh;
+            }
         }
     }
 }
diff --git a/HMI.MenuManage/Entity/UserData.cs b/HMI.MenuManage/Entity/UserData.cs
index a89d19f..280346d 100644
--- a/HMI.MenuManage/Entity/UserData.cs
+++ b/HMI.MenuManage/Entity/UserData.cs
@@ -1,8 +1,4 @@
-锘縰sing System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
+锘縰sing HMI.Core;
 using HMI.Core.Entity;
 
 namespace HMI.MenuManage.Entity
@@ -10,9 +6,22 @@
     public class UserData : NotifyBase
     {
         public string Id { get; set; }
-        public string Name { get; set; }
+
+        [ExcelColumn(name: "宸ュ彿")]
+        public string Code { get; set; }
+
+        [ExcelColumn(name: "璐﹀彿")]
         public string Username { get; set; }
+
+        [ExcelColumn(name: "鍚嶇О")]
+        public string Name { get; set; }
+
+        [ExcelColumn(name: "瑙掕壊缂栫爜")]
+        public string RoleCodes { get; set; }
+
+        [ExcelColumn(name: "瀵嗙爜", ignoreExport: true)]
         public string Password { get; set; }
+
         public string RoleCode { get; set; }
 
         private bool _Selected;
diff --git a/HMI.MenuManage/HMI.MenuManage.csproj b/HMI.MenuManage/HMI.MenuManage.csproj
index e0e6184..30106b5 100644
--- a/HMI.MenuManage/HMI.MenuManage.csproj
+++ b/HMI.MenuManage/HMI.MenuManage.csproj
@@ -78,6 +78,7 @@
       <HintPath>..\Libs\System.Data.SQLite.Linq.dll</HintPath>
     </Reference>
     <Reference Include="System.Web" />
+    <Reference Include="System.Windows.Forms" />
     <Reference Include="System.Xml" />
     <Reference Include="Microsoft.CSharp" />
     <Reference Include="System.Core" />
@@ -193,6 +194,10 @@
       <Project>{f17de6b5-af1d-48c6-94a0-6b763a6546a6}</Project>
       <Name>HMI.UI</Name>
     </ProjectReference>
+    <ProjectReference Include="..\HMI.Util\HMI.Util.csproj">
+      <Project>{afea100a-9223-448d-ab10-3015d93dd98c}</Project>
+      <Name>HMI.Util</Name>
+    </ProjectReference>
   </ItemGroup>
   <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
   <PropertyGroup>
diff --git a/HMI.MenuManage/Modal/UserModal.xaml b/HMI.MenuManage/Modal/UserModal.xaml
index 26a30f7..e7cd7f4 100644
--- a/HMI.MenuManage/Modal/UserModal.xaml
+++ b/HMI.MenuManage/Modal/UserModal.xaml
@@ -16,6 +16,13 @@
             Style="{StaticResource ModalWindow}">
     <Grid Margin="10">
         <StackPanel x:Name="formPanel">
+            <pu:FormGroup x:Name="FgCode" Style="{StaticResource InputFormGroupStyle}"
+                          dp:FormGroupHelper.ValidateMethod="ValidateCode"
+              Header="{DynamicResource HMI.User.Code}"
+              dp:FormGroupHelper.IsRequired="True" GroupName="MyFormGroup">
+                <TextBox x:Name="TbCode" Style="{StaticResource EditorTextBoxStyle}" />
+            </pu:FormGroup>
+
             <pu:FormGroup x:Name="FgName" Style="{StaticResource InputFormGroupStyle}" 
                           Header="{DynamicResource HMI.User.RealName}"
                           dp:FormGroupHelper.IsRequired="True" GroupName="MyFormGroup">
diff --git a/HMI.MenuManage/Modal/UserModal.xaml.cs b/HMI.MenuManage/Modal/UserModal.xaml.cs
index d88c6c6..749b2a1 100644
--- a/HMI.MenuManage/Modal/UserModal.xaml.cs
+++ b/HMI.MenuManage/Modal/UserModal.xaml.cs
@@ -83,11 +83,25 @@
             return true;
         }
 
+        private bool ValidateCode(object value, out string err)
+        {
+            err = null;
+            string segment = string.IsNullOrEmpty(Data.Id) ? "" : $" AND id!='{Data.Id}'";
+            string sql = $"SELECT 1 FROM user WHERE code='{TbCode.Text}'{segment};";
+            if (sqliteHelper.HasData(sql))
+            {
+                err = string.Format(LangUtil.Get("App.MustBeUnique"), LangUtil.Get("HMI.User.Code"));
+                return false;
+            }
+            return true;
+        }
+
         private bool ValidateForm()
         {
             bool flag = FormGroupHelper.ValidateForm(formPanel);
             if (flag)
             {
+                Data.Code = TbCode.Text;
                 Data.Username = TbUsername.Text;
                 Data.Name = TbName.Text;
                 Data.Password = TbPassword.Text;
@@ -99,6 +113,7 @@
         public bool? Open(UserData data)
         {
             Data = data;
+            TbCode.Text = Data.Code;
             TbUsername.Text = Data.Username;
             TbName.Text = Data.Name;
             TbPassword.Text = Data.Password;
@@ -122,15 +137,15 @@
 
         public void InsertData(UserData data)
         {
-            string sql = $@"INSERT INTO user (username,password,name) 
+            string sql = $@"INSERT INTO user (code, username,password,name) 
                 VALUES
-                ('{data.Username}','{data.Password}','{data.Name}');";
+                ('{data.Code}', '{data.Username}','{data.Password}','{data.Name}');";
             UserRoleRelHelper.SaveUserRoleRel(sqliteHelper, Roles, data, sql, null);
         }
 
         public void UpdateData(UserData data)
         {
-            string sql = $@"UPDATE user SET username='{data.Username}',password='{data.Password}',name='{data.Name}' 
+            string sql = $@"UPDATE user SET code='{data.Code}', username='{data.Username}',password='{data.Password}',name='{data.Name}' 
 WHERE id='{data.Id}';";
             UserRoleRelHelper.SaveUserRoleRel(sqliteHelper, Roles, data, sql, null);
         }
diff --git a/HMI.MenuManage/Model/UserManageModel.cs b/HMI.MenuManage/Model/UserManageModel.cs
index 42fafc2..2fbca78 100644
--- a/HMI.MenuManage/Model/UserManageModel.cs
+++ b/HMI.MenuManage/Model/UserManageModel.cs
@@ -49,6 +49,7 @@
             sqliteHelper = new SqliteHelper(PathHelper.DatabasePath);
             sqliteHelper.ExecuteSql(@"CREATE TABLE IF NOT EXISTS user (
                     id INTEGER PRIMARY KEY AUTOINCREMENT,
+                    code TEXT NOT NULL,
                     username TEXT NOT NULL,
                     password TEXT NOT NULL,
                     name TEXT,
@@ -63,6 +64,28 @@
 ");
         }
 
+        public void ImportUser(List<UserData> users)
+        {
+            List<string> batch = new List<string>();
+            foreach (var user in users)
+            {
+                string sql = $"SELECT 1 FROM user WHERE username='{user.Username}' OR code='{user.Code}' OR name='{user.Name}'";
+                if (sqliteHelper.HasData(sql))
+                {
+                    throw new Exception($"User {user.Username} already exists.");
+                }
+                string codes = "''";
+                string roleCodes = user.RoleCodes?.Trim();
+                if (!string.IsNullOrEmpty(roleCodes))
+                {
+                    codes = $"'{roleCodes.Replace(",", "','")}'";
+                }
+                batch.Add($"INSERT INTO user (code,username,name,password) VALUES ('{user.Code}', '{user.Username}', '{user.Name}', '{user.Password}');");
+                batch.Add($"INSERT INTO user_role_rel (username, roleCode) SELECT '{user.Username}', code FROM role WHERE code IN ({codes});");
+            }
+            sqliteHelper.ExecuteBatchSql(batch);
+        }
+
         public void LoadData()
         {
             ObservableCollection<UserData> data = new ObservableCollection<UserData>();
@@ -74,10 +97,22 @@
                     var menu = new UserData
                     {
                         Id = dr["id"].ToString(),
+                        Code = dr["code"].ToString(),
                         Username = dr["username"].ToString(),
                         Password = dr["password"].ToString(),
                         Name = dr["name"].ToString()
                     };
+                    DataTable roles = sqliteHelper.GetDataTable($@"SELECT r.code AS code FROM user_role_rel AS rel 
+INNER JOIN role AS r ON rel.roleCode = r.code AND rel.username = '{menu.Username}' Order By r.code;");
+                    List<string> codes = new List<string>();
+                    foreach (DataRow row in roles.Rows)
+                    {
+                        if (row["code"] != DBNull.Value)
+                        {
+                            codes.Add(row["code"].ToString());
+                        }
+                    }
+                    menu.RoleCodes = string.Join(",", codes);
                     data.Add(menu);
                 }
             }
diff --git a/HMI.MenuManage/Properties/AssemblyInfo.cs b/HMI.MenuManage/Properties/AssemblyInfo.cs
index 8f148a8..0b2a64c 100644
--- a/HMI.MenuManage/Properties/AssemblyInfo.cs
+++ b/HMI.MenuManage/Properties/AssemblyInfo.cs
@@ -1,9 +1,7 @@
-锘縰sing System.Reflection;
-using System.Resources;
-using System.Runtime.CompilerServices;
+锘縰sing HMI.Core.Annotation;
+using System.Reflection;
 using System.Runtime.InteropServices;
 using System.Windows;
-using HMI.Core.Annotation;
 
 // 鏈夊叧绋嬪簭闆嗙殑涓�鑸俊鎭敱浠ヤ笅
 // 鎺у埗銆傛洿鏀硅繖浜涚壒鎬у�煎彲淇敼
diff --git a/HMI.MenuManage/UserManage.xaml b/HMI.MenuManage/UserManage.xaml
index 90085ce..ace9978 100644
--- a/HMI.MenuManage/UserManage.xaml
+++ b/HMI.MenuManage/UserManage.xaml
@@ -22,6 +22,13 @@
                 <DockPanel Margin="0,0,0,10">
                     <Button DockPanel.Dock="Right" Style="{StaticResource DelBtn}"
                             x:Name="deleteBatchBtn" Click="Button_Click"/>
+
+                    <Button DockPanel.Dock="Right" Style="{StaticResource ExportBtn}" 
+Margin="0,0,10,0" x:Name="exportBtn" Click="Button_Click"/>
+
+                    <Button DockPanel.Dock="Right" Style="{StaticResource ImportBtn}" 
+        Margin="0,0,10,0" x:Name="importBtn" Click="Button_Click"/>
+                    
                     <Button DockPanel.Dock="Right" Style="{StaticResource RfhBtn}" 
                             Margin="0,0,10,0" x:Name="refreshBtn" Click="Button_Click"/>
                     <Button DockPanel.Dock="Right" Style="{StaticResource AddBtn}"
@@ -49,6 +56,11 @@
                                 </DataTemplate>
                             </DataGridTemplateColumn.CellTemplate>
                         </DataGridTemplateColumn>
+                        <DataGridTextColumn Width="*"  Binding="{Binding Code}">
+                            <DataGridTextColumn.Header>
+                                <TextBlock Text="{DynamicResource HMI.User.Code}"/>
+                            </DataGridTextColumn.Header>
+                        </DataGridTextColumn>
                         <DataGridTextColumn Width="*"  Binding="{Binding Username}">
                             <DataGridTextColumn.Header>
                                 <TextBlock Text="{DynamicResource HMI.User.Account}"/>
diff --git a/HMI.MenuManage/UserManage.xaml.cs b/HMI.MenuManage/UserManage.xaml.cs
index 44a6dde..dcf1c22 100644
--- a/HMI.MenuManage/UserManage.xaml.cs
+++ b/HMI.MenuManage/UserManage.xaml.cs
@@ -3,6 +3,7 @@
 using HMI.MenuManage.Entity;
 using HMI.MenuManage.Model;
 using HMI.UI.Util;
+using HMI.Util;
 using System;
 using System.Collections.Generic;
 using System.Linq;
@@ -159,6 +160,69 @@
                     ToastHelper.Error(Window.GetWindow(this), $"{LangUtil.Get("App.SaveFailed")}: {ex.Message}");
                 }
             }
+            else if (btn.Name == "exportBtn")
+            {
+                ExportToExcel();
+            }
+            else if (btn.Name == "importBtn")
+            {
+                ImportFromExcel();
+            }
+        }
+
+        public void ExportToExcel()
+        {
+            try
+            {
+                if (Model.DataSource == null || Model.DataSource.Count <= 0)
+                {
+                    ToastHelper.Warning(this.lang("App.NoExportData"));
+                    return;
+                }
+                System.Windows.Forms.SaveFileDialog sfd = new System.Windows.Forms.SaveFileDialog();
+
+                string fname = string.Empty;
+                sfd.Filter = "";
+                sfd.DefaultExt = "xls";
+                sfd.FileName = this.lang("HMI.User.Title");
+                if (sfd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
+                {
+                    fname = sfd.FileName;
+                    ExcelHelper.ExportExcel(fname, Model.DataSource);
+                    ToastHelper.Success(this.lang("App.ExportSuccess"));
+                }
+            }
+            catch (Exception ex)
+            {
+                log.Error("Export User Data Failed.", ex);
+                ToastHelper.Error(ex.Message);
+            }
+        }
+
+        public void ImportFromExcel()
+        {
+            try
+            {
+                System.Windows.Forms.OpenFileDialog openDialog = new System.Windows.Forms.OpenFileDialog();
+                openDialog.Filter = "execl(*.xlsx;*.xls)|*.xlsx;*.xls";
+                if (openDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
+                {
+                    List<UserData> users = ExcelHelper.ReadFromExcel<UserData>(openDialog.FileName);
+                    if (users == null || users.Count <= 0)
+                    {
+                        ToastHelper.Warning(this.lang("App.NoImportData"));
+                        return;
+                    }
+                    Model.ImportUser(users);
+                    Model.LoadData();
+                    ToastHelper.Success(this.lang("App.ImportSuccess"));
+                }
+            }
+            catch (Exception ex)
+            {
+                log.Error("AlarmText Import Failed.", ex);
+                ToastHelper.Error(Window.GetWindow(this), $"{this.lang("App.ImportFailed")}: {ex.Message}");
+            }
         }
     }
 }
diff --git a/HMI.UI/Controls/NumberPanel.xaml.cs b/HMI.UI/Controls/NumberPanel.xaml.cs
index 5bddb7a..e3eee9d 100644
--- a/HMI.UI/Controls/NumberPanel.xaml.cs
+++ b/HMI.UI/Controls/NumberPanel.xaml.cs
@@ -46,9 +46,8 @@
                 {
 
                 }
-                catch (Exception ex)
+                catch
                 {
-
                     return;
                 }
                 finally
diff --git a/HMI.UI/DpUtil/UniformGridHelper.cs b/HMI.UI/DpUtil/UniformGridHelper.cs
index bcd3ebb..6f98327 100644
--- a/HMI.UI/DpUtil/UniformGridHelper.cs
+++ b/HMI.UI/DpUtil/UniformGridHelper.cs
@@ -92,7 +92,7 @@
 
                 if (child is Control control)
                 {
-                    
+
                     int col = i % columnCount;
                     int row = i / columnCount;
 
@@ -110,5 +110,111 @@
 
             }
         }
+
+        public static void RefreshLineSize(UniformGrid grid)
+        {
+            Thickness parentThickness = default;
+            Brush brush = default;
+            var parent = grid.Parent;
+            if (parent is System.Windows.Controls.Control parentControl)
+            {
+                parentThickness = parentControl.BorderThickness;
+                brush = parentControl.BorderBrush;
+            }
+            else if (parent is Border border)
+            {
+                parentThickness = border.BorderThickness;
+                brush = border.BorderBrush;
+            }
+
+            //var value = GetLineSize(grid);
+            //if (value == null) return;
+            //double size = (double)value;
+
+            int totalCount = grid.Children.Count;
+            var columnCount = grid.Columns;
+            int rowCount = grid.Rows;
+
+            for (int i = 0; i < totalCount; i++)
+            {
+                int row = i / columnCount;
+                int rest = i % columnCount;
+
+
+                var cell = grid.Children[i];
+
+                var bt = new Thickness(rest == 0 ? 0 : parentThickness.Left,
+                    row == 0 ? 0 : parentThickness.Top,
+                    rest == columnCount - 1 ? 0 : parentThickness.Right,
+                    row == rowCount - 1 ? 0 : parentThickness.Bottom);
+
+                if (cell is Control child)
+                {
+                    child.BorderThickness = bt;
+                    child.BorderBrush = brush;
+                }
+            }
+        }
+
+        public static void RefreshDefault(UniformGrid grid, ControlTemplate template)
+        {
+            int totalCount = grid.Columns * grid.Rows;
+
+            int rest = totalCount - grid.Children.Count;
+            if (rest <= 0) return;
+
+            for (int i = 0; i < rest; i++)
+            {
+                var control = new Control
+                {
+                    Template = template
+                };
+                grid.Children.Add(control);
+            }
+        }
+
+        public static ControlTemplate GetDefaultTemplate(DependencyObject obj)
+        {
+            return (ControlTemplate)obj.GetValue(DefaultTemplateProperty);
+        }
+        public static void SetDefaultTemplate(DependencyObject obj, ControlTemplate value)
+        {
+            obj.SetValue(DefaultTemplateProperty, value);
+        }
+        public static readonly DependencyProperty DefaultTemplateProperty =
+            DependencyProperty.RegisterAttached("DefaultTemplate", typeof(ControlTemplate), typeof(UniformGrid),
+                new PropertyMetadata(null, OnDefaultTemplateChanged));
+
+        private static void OnDefaultTemplateChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+        {
+            if (d is UniformGrid grid)
+            {
+                if (e.Property == DefaultTemplateProperty)
+                {
+                    var tem = GetDefaultTemplate(grid);
+                    if (tem == null) return;
+
+                    //var rowCount = grid.Rows;
+                    int columnCount = grid.Columns;
+                    int totalCount = grid.Children.Count;
+                    int rowCount = totalCount / columnCount;
+                    int last = totalCount % columnCount;
+                    if (last == 0)
+                    {
+                        return;
+                    }
+                    int addCount = columnCount - last;
+
+                    for (int i = 0; i < addCount; i++)
+                    {
+                        Button u = new Button
+                        {
+                            Template = tem
+                        };
+                        grid.Children.Add(u);
+                    }
+                }
+            }
+        }
     }
 }
diff --git a/HMI.UI/Entity/NotifyBase.cs b/HMI.UI/Entity/NotifyBase.cs
deleted file mode 100644
index e401957..0000000
--- a/HMI.UI/Entity/NotifyBase.cs
+++ /dev/null
@@ -1,97 +0,0 @@
-锘縰sing System;
-using System.ComponentModel;
-using System.Runtime.CompilerServices;
-using System.Windows.Input;
-
-namespace HMI.UI.Entity
-{
-
-    public class RelayCommand<T> : ICommand
-    {
-        /// <summary>
-        /// 鍛戒护鑳藉惁鎵ц
-        /// </summary>
-        readonly Func<bool> _canExecute;
-        /// <summary>
-        /// 鍛戒护鎵ц鐨勬柟娉�
-        /// </summary>
-        readonly Action<T> _execute;
-
-        /// <summary>
-        /// 鍛戒护鐨勬瀯閫犲嚱鏁�
-        /// </summary>
-        /// <param name="action">鍛戒护闇�鎵ц鐨勬柟娉�</param>
-        /// <param name="canExecute">鍛戒护鏄惁鍙互鎵ц鐨勬柟娉�</param>
-        public RelayCommand(Action<T> action, Func<bool> canExecute)
-        {
-            _execute = action;
-            _canExecute = canExecute;
-        }
-
-        public RelayCommand(Action<T> action)
-        {
-            _execute = action;
-        }
-
-        /// <summary>
-        /// 鍒ゆ柇鍛戒护鏄惁鍙互鎵ц
-        /// </summary>
-        /// <param name="parameter"></param>
-        /// <returns></returns>
-        public bool CanExecute(Object parameter)
-        {
-            if (_canExecute == null)
-                return true;
-            return _canExecute();
-        }
-
-        /// <summary>
-        /// 鎵ц鍛戒护
-        /// </summary>
-        /// <param name="parameter"></param>
-        public void Execute(Object parameter)
-        {
-            _execute((T)parameter);
-        }
-
-        /// <summary>
-        /// 浜嬩欢杩藉姞銆佺Щ闄�
-        /// </summary>
-        public event EventHandler CanExecuteChanged
-        {
-            add
-            {
-                if (_canExecute != null)
-                    CommandManager.RequerySuggested += value;
-            }
-            remove
-            {
-                if (_canExecute != null)
-                    CommandManager.RequerySuggested -= value;
-            }
-        }
-
-    }
-
-    public class NotifyBase : INotifyPropertyChanged
-    {
-        #region ...
-        public virtual event PropertyChangedEventHandler PropertyChanged;
-
-        protected virtual void RaisePropertyChanged(string name)
-        {
-
-            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
-        }
-
-        protected void RaiseAndSetIfChanged<T>(ref T old, T @new, [CallerMemberName] string propertyName = null)
-        {
-            old = @new;
-            if (propertyName != null)
-            {
-                RaisePropertyChanged(propertyName);
-            }
-        }
-        #endregion
-    }
-}
diff --git a/HMI.UI/Entity/ScannerConfig.cs b/HMI.UI/Entity/ScannerConfig.cs
new file mode 100644
index 0000000..a39a673
--- /dev/null
+++ b/HMI.UI/Entity/ScannerConfig.cs
@@ -0,0 +1,34 @@
+锘縰sing HMI.Util;
+using System;
+using System.IO.Ports;
+
+namespace HMI.UI.Entity
+{
+    public class ScannerConfig
+    {
+        public string PortName { get; set; }
+
+        public int BaudRate { get; set; }
+        public Parity Parity { get; set; } = Parity.None;
+        public int DataBits { get; set; }
+        public StopBits StopBits { get; set; } = StopBits.One;
+        public Handshake Handshake { get; set; } = Handshake.None;
+        public bool DtrEnable { get; set; } = true;
+        public bool RtsEnable { get; set; } = true;
+
+
+        public static ScannerConfig ReadFromIni(string iniFile, string section)
+        {
+            var res = new ScannerConfig();
+            res.PortName = IniFileHelper.ReadIniData(section, "PortName", "COM1", iniFile);
+            res.BaudRate = int.Parse(IniFileHelper.ReadIniData(section, "BaudRate", "9600", iniFile));
+            res.Parity = (Parity)Enum.Parse(typeof(Parity), IniFileHelper.ReadIniData(section, "Parity", "None", iniFile));
+            res.DataBits = int.Parse(IniFileHelper.ReadIniData(section, "DataBits", "8", iniFile));
+            res.StopBits = (StopBits)Enum.Parse(typeof(StopBits), IniFileHelper.ReadIniData(section, "StopBits", "One", iniFile));
+            res.Handshake = (Handshake)Enum.Parse(typeof(Handshake), IniFileHelper.ReadIniData(section, "Handshake", "None", iniFile));
+            res.DtrEnable = bool.Parse(IniFileHelper.ReadIniData(section, "DtrEnable", "true", iniFile));
+            res.RtsEnable = bool.Parse(IniFileHelper.ReadIniData(section, "RtsEnable", "true", iniFile));
+            return res;
+        }
+    }
+}
diff --git a/HMI.UI/HMI.UI.csproj b/HMI.UI/HMI.UI.csproj
index 72f50a8..1e9ec04 100644
--- a/HMI.UI/HMI.UI.csproj
+++ b/HMI.UI/HMI.UI.csproj
@@ -103,8 +103,12 @@
     <Compile Include="DpUtil\StackPanelHelper.cs" />
     <Compile Include="DpUtil\TreeViewHelper.cs" />
     <Compile Include="DpUtil\UniformGridHelper.cs" />
+    <Compile Include="Entity\ScannerConfig.cs" />
     <Compile Include="MaskWindow.xaml.cs">
       <DependentUpon>MaskWindow.xaml</DependentUpon>
+    </Compile>
+    <Compile Include="ScannerSetting.xaml.cs">
+      <DependentUpon>ScannerSetting.xaml</DependentUpon>
     </Compile>
     <Compile Include="Style\BaseWindow.xaml.cs">
       <DependentUpon>BaseWindow.xaml</DependentUpon>
@@ -138,6 +142,10 @@
     <Page Include="MaskWindow.xaml">
       <SubType>Designer</SubType>
       <Generator>MSBuild:Compile</Generator>
+    </Page>
+    <Page Include="ScannerSetting.xaml">
+      <Generator>MSBuild:Compile</Generator>
+      <SubType>Designer</SubType>
     </Page>
     <Page Include="Style\MsgBoxStyle.xaml">
       <SubType>Designer</SubType>
@@ -202,6 +210,11 @@
       <Project>{3e3bf44a-0b69-45c5-9512-528cb48a7106}</Project>
       <Name>HMI.Core</Name>
     </ProjectReference>
+    <ProjectReference Include="..\HMI.Util\HMI.Util.csproj">
+      <Project>{afea100a-9223-448d-ab10-3015d93dd98c}</Project>
+      <Name>HMI.Util</Name>
+    </ProjectReference>
   </ItemGroup>
+  <ItemGroup />
   <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
 </Project>
\ No newline at end of file
diff --git a/HMI.UI/MaskWindow.xaml.cs b/HMI.UI/MaskWindow.xaml.cs
index 56fd10e..0298727 100644
--- a/HMI.UI/MaskWindow.xaml.cs
+++ b/HMI.UI/MaskWindow.xaml.cs
@@ -50,9 +50,19 @@
             //}));
             Task.Run(() =>
             {
-                // 鏆傚仠绾跨▼浠I瀹屽叏灞曠ず
-                if (delay > 0) Thread.Sleep(delay);
-                action(this);
+                try
+                {
+                    // 鏆傚仠绾跨▼浠I瀹屽叏灞曠ず
+                    if (delay > 0) Thread.Sleep(delay);
+                    action(this);
+                }
+                finally
+                {
+                    Dispatcher.Invoke(() =>
+                    {
+                        this.Close();
+                    });
+                }
             });
         }
 
diff --git a/HMI.UI/ScannerSetting.xaml b/HMI.UI/ScannerSetting.xaml
new file mode 100644
index 0000000..a3c4ad6
--- /dev/null
+++ b/HMI.UI/ScannerSetting.xaml
@@ -0,0 +1,65 @@
+锘�<pu:WindowX x:Class="HMI.UI.ScannerSetting"
+        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+        xmlns:local="clr-namespace:HMI.UI"
+        mc:Ignorable="d"
+        Style="{StaticResource ModalWindow}"
+        xmlns:uic="clr-namespace:HMI.UI.Converter"
+        xmlns:pw="https://opensource.panuon.com/wpf"
+        xmlns:pu="https://opensource.panuon.com/wpf-ui"
+        xmlns:dp="clr-namespace:HMI.UI.DpUtil"
+        xmlns:cc="clr-namespace:HMI.Core;assembly=HMI.Core"
+        xmlns:sys="clr-namespace:System;assembly=mscorlib"
+        xmlns:ports="clr-namespace:System.IO.Ports;assembly=System"
+        SizeToContent="Height"
+        WindowStartupLocation="CenterOwner"
+        Title="{Binding title}" Width="600">
+    <Window.Resources>
+        <ObjectDataProvider x:Key="stopBitsItems" 
+                  MethodName="GetValues" 
+                  ObjectType="{x:Type sys:Enum}">
+            <ObjectDataProvider.MethodParameters>
+                <x:Type TypeName="ports:StopBits"/>
+            </ObjectDataProvider.MethodParameters>
+        </ObjectDataProvider>
+    </Window.Resources>
+
+
+    <Grid Margin="10">
+        <StackPanel x:Name="formPanel">
+            <pu:FormGroup x:Name="FgPortName" Style="{StaticResource InputFormGroupStyle}" Header="{DynamicResource Tr.PortName}"
+              GroupName="MyFormGroup">
+                <ComboBox x:Name="CbPortName" Style="{StaticResource FormComboxStyle}" />
+            </pu:FormGroup>
+
+            <pu:FormGroup Style="{StaticResource InputFormGroupStyle}" Header="{DynamicResource Tr.BaudRate}"
+                          dp:FormGroupHelper.IsRequired="True"
+                          GroupName="MyFormGroup">
+                <pu:NumberInput x:Name="NiBaudRate" IsEnabled="{Binding SbIsEnabled}" Style="{StaticResource FormNumberInput}" />
+            </pu:FormGroup>
+
+            <pu:FormGroup Style="{StaticResource InputFormGroupStyle}" Header="{DynamicResource Tr.DataBits}"
+            dp:FormGroupHelper.IsRequired="True" GroupName="MyFormGroup">
+                <pu:NumberInput x:Name="NiDataBits" IsEnabled="{Binding SbIsEnabled}" Style="{StaticResource FormNumberInput}" />
+            </pu:FormGroup>
+
+            <pu:FormGroup Style="{StaticResource InputFormGroupStyle}" Header="{DynamicResource Tr.StopBits}" 
+                          dp:FormGroupHelper.IsRequired="True" GroupName="MyFormGroup">
+                <ComboBox x:Name="CbStopBits" IsEnabled="{Binding SbIsEnabled}" Style="{StaticResource FormComboxStyle}"
+                          ItemsSource="{Binding Source={StaticResource stopBitsItems}}"
+          SelectedValuePath="Content">
+                </ComboBox>
+            </pu:FormGroup>
+
+            <pu:FormGroup Style="{StaticResource InputFormGroupStyle}" Header="{DynamicResource Tr.DtrEnable}" GroupName="MyFormGroup">
+                <CheckBox x:Name="CxDtrEnable" IsEnabled="{Binding SbIsEnabled}"/>
+            </pu:FormGroup>
+
+            <pu:FormGroup Style="{StaticResource InputFormGroupStyle}" Header="{DynamicResource Tr.RtsEnable}"  GroupName="MyFormGroup">
+                <CheckBox x:Name="CxRtsEnable" IsEnabled="{Binding SbIsEnabled}"/>
+            </pu:FormGroup>
+        </StackPanel>
+    </Grid>
+</pu:WindowX>
diff --git a/HMI.UI/ScannerSetting.xaml.cs b/HMI.UI/ScannerSetting.xaml.cs
new file mode 100644
index 0000000..56d6792
--- /dev/null
+++ b/HMI.UI/ScannerSetting.xaml.cs
@@ -0,0 +1,153 @@
+锘縰sing HMI.Core.Util;
+using HMI.UI.DpUtil;
+using HMI.UI.Entity;
+using HMI.UI.Util;
+using HMI.Util;
+using Panuon.WPF.UI;
+using System;
+using System.ComponentModel;
+using System.IO.Ports;
+
+namespace HMI.UI
+{
+    /// <summary>
+    /// ScannerSetting.xaml 鐨勪氦浜掗�昏緫
+    /// </summary>
+    public partial class ScannerSetting : WindowX
+    {
+        public ScannerSetting()
+        {
+            DataContext = this;
+            InitializeComponent();
+        }
+
+        public string IniFile { get; set; }
+        public ScannerConfig Data { get; set; }
+
+        private bool _isProcessing;
+
+        protected override void OnClosing(CancelEventArgs e)
+        {
+            //Press Continue Button
+            if (DialogResult == true)
+            {
+                if (_isProcessing)
+                {
+                    e.Cancel = true;
+                    return;
+                }
+                var okButton = WindowXModalDialog.GetOKButton(this);
+                try
+                {
+                    _isProcessing = true;
+                    ButtonHelper.SetIsPending(okButton, true);
+                    if (!ValidateForm())
+                    {
+                        e.Cancel = true;
+                        return;
+                    }
+
+                    SaveOrUpdateData();
+                }
+                catch (Exception ex)
+                {
+                    ToastHelper.Error(this, $"Error锛亄ex.Message}");
+                    e.Cancel = true;
+                    return;
+                }
+                finally
+                {
+                    _isProcessing = false;
+                    ButtonHelper.SetIsPending(okButton, false);
+                }
+            }
+            base.OnClosing(e);
+        }
+
+
+        private bool ValidateForm()
+        {
+            bool flag = FormGroupHelper.ValidateForm(formPanel);
+            if (flag)
+            {
+                Data.PortName = CbPortName.SelectedItem?.ToString();
+                Data.BaudRate = (int)(NiBaudRate.Value ?? 0);
+                Data.DataBits = (int)(NiDataBits.Value ?? 0);
+                Data.StopBits = (StopBits)CbStopBits.SelectedItem;
+                Data.DtrEnable = CxDtrEnable.IsChecked ?? false;
+                Data.RtsEnable = CxRtsEnable.IsChecked ?? false;
+            }
+            return flag;
+        }
+
+        private bool _isEnabled = true;
+        public bool SbIsEnabled
+        {
+            get => _isEnabled;
+            set
+            {
+                Set(ref _isEnabled, value);
+            }
+        }
+
+        private string section;
+        public bool? Open(bool showPortName, string section, string iniFile)
+        {
+            IniFile = iniFile;
+            string[] ports = SerialPort.GetPortNames();
+            CbPortName.ItemsSource = ports;
+
+            this.section = section;
+            this.Title = section == "Config" ? this.lang("Tr.CommonSetting") : (section + this.lang("Tr.ScannerSetting"));
+            if (showPortName)
+            {
+                FgPortName.Visibility = System.Windows.Visibility.Visible;
+                FormGroupHelper.SetIsRequired(FgPortName, true);
+                SbIsEnabled = true;
+            }
+            else
+            {
+                FgPortName.Visibility = System.Windows.Visibility.Collapsed;
+                FormGroupHelper.SetIsRequired(FgPortName, false);
+                SbIsEnabled = true;
+            }
+
+            var data = ScannerConfig.ReadFromIni(IniFile, "Config");
+            string portName = IniFileHelper.ReadIniData(section, "PortName", "", IniFile);
+            if (string.IsNullOrEmpty(portName))
+            {
+                if (section.StartsWith("A"))
+                {
+                    portName = "COM1";
+                }
+                else
+                {
+                    portName = "COM2";
+                }
+            }
+            data.PortName = portName;
+
+            Data = data;
+            CbPortName.SelectedItem = Data.PortName;
+            NiBaudRate.Value = Data.BaudRate;
+            NiDataBits.Value = Data.DataBits;
+            CbStopBits.SelectedItem = Data.StopBits;
+            CxDtrEnable.IsChecked = Data.DtrEnable;
+            CxRtsEnable.IsChecked = Data.RtsEnable;
+            return this.ShowDialog();
+        }
+
+        private void SaveOrUpdateData()
+        {
+            IniFileHelper.WriteIniData(section, "PortName", Data.PortName, IniFile);
+            if (section == "Config")
+            {
+                IniFileHelper.WriteIniData(section, "BaudRate", Data.BaudRate.ToString(), IniFile);
+                IniFileHelper.WriteIniData(section, "DataBits", Data.DataBits.ToString(), IniFile);
+                IniFileHelper.WriteIniData(section, "StopBits", Data.StopBits.ToString(), IniFile);
+                IniFileHelper.WriteIniData(section, "DtrEnable", Data.DtrEnable.ToString(), IniFile);
+                IniFileHelper.WriteIniData(section, "RtsEnable", Data.RtsEnable.ToString(), IniFile);
+            }
+        }
+    }
+}
diff --git a/HMI.UI/Style/FormStyle.xaml b/HMI.UI/Style/FormStyle.xaml
index 1043030..5172944 100644
--- a/HMI.UI/Style/FormStyle.xaml
+++ b/HMI.UI/Style/FormStyle.xaml
@@ -106,14 +106,13 @@
     <Style x:Key="InputFormGroupStyle" TargetType="pu:FormGroup">
         <Style.Resources>
             <Style TargetType="TextBox" BasedOn="{StaticResource EditorTextBoxStyle}">
-
             </Style>
         </Style.Resources>
         <Setter Property="HeaderTemplate">
             <Setter.Value>
                 <DataTemplate>
                     <Border Margin="5,0,5,0">
-                        <ContentControl Content="{Binding}" />
+                        <ContentControl Content="{Binding}"/>
                     </Border>
                 </DataTemplate>
             </Setter.Value>
diff --git a/HMI.UI/Style/PanuonResource.xaml b/HMI.UI/Style/PanuonResource.xaml
index 5d6734a..4fdbc63 100644
--- a/HMI.UI/Style/PanuonResource.xaml
+++ b/HMI.UI/Style/PanuonResource.xaml
@@ -27,6 +27,7 @@
         <Setter Property="Padding" Value="8"/>
         <Setter Property="BorderThickness" Value="0"/>
         <Setter Property="Cursor" Value="Hand"/>
+        <Setter Property="Focusable" Value="False"/>
     </Style>
 
     <Style x:Key="CommonButton" TargetType="Button" BasedOn="{StaticResource PrimaryButton}">
@@ -170,20 +171,22 @@
         </Style.Triggers>
     </Style>
 
+    <Style x:Key="TableTextBlock" TargetType="TextBlock">
+        <Setter Property="Padding" Value="0"/>
+        <Setter Property="Margin" Value="0"/>
+        <Setter Property="VerticalAlignment" Value="Center"/>
+    </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"/>
-                <Setter Property="Background" Value="Transparent"/>
             </Style>
-            <!--<Style TargetType="TextBlock">
-                <Setter Property="VerticalAlignment" Value="Center"/>
-                <Setter Property="Padding" Value="5"/>
-            </Style>-->
             <Style TargetType="DataGridColumnHeader" 
        BasedOn="{StaticResource {x:Static pu:StyleKeys.DataGridColumnHeaderStyle}}">
                 <Setter Property="Padding" Value="10"/>
diff --git a/HMI.UI/Util/MsgBoxHelper.cs b/HMI.UI/Util/MsgBoxHelper.cs
index 6d5d49d..79174a2 100644
--- a/HMI.UI/Util/MsgBoxHelper.cs
+++ b/HMI.UI/Util/MsgBoxHelper.cs
@@ -71,12 +71,19 @@
             MessageBoxResult res = MessageBoxResult.None;
             if (!(owner is Window window))
             {
-                window = Window.GetWindow(owner) ?? Application.Current.MainWindow;
+                owner.Dispatcher.Invoke(() =>
+                {
+                    window = Window.GetWindow(owner) ?? Application.Current.MainWindow;
+                    res = MessageBoxX.Show(window, message, caption, button, icon, defaultButton, setting, countdown);
+                });
             }
-            window.Dispatcher.Invoke(() =>
+            else
             {
-                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);
+                });
+            }
             return res;
         }
     }
diff --git a/HMI.UnitTest/App.xaml b/HMI.UnitTest/App.xaml
index 293171a..6a7ec8d 100644
--- a/HMI.UnitTest/App.xaml
+++ b/HMI.UnitTest/App.xaml
@@ -23,6 +23,8 @@
             <uic:VisibilityBoolConverter x:Key="vbConverter"/>
             <uic:DataGridRowFlagConverter x:Key="rowFlag"/>
             <hc:LangConverter x:Key="lc"/>
+            <hc:WidthSizeConverter x:Key="fc"/>
+            <hc:HeightSizeConverter x:Key="hfc"/>
         </ResourceDictionary>
     </Application.Resources>
 </Application>
diff --git a/HMI.UnitTest/HMI.UnitTest.csproj b/HMI.UnitTest/HMI.UnitTest.csproj
index f3f4ff5..ba07951 100644
--- a/HMI.UnitTest/HMI.UnitTest.csproj
+++ b/HMI.UnitTest/HMI.UnitTest.csproj
@@ -155,10 +155,6 @@
       <Project>{be42ab26-3270-43e1-84b8-6929d862ff8d}</Project>
       <Name>Plugin.DataBoard</Name>
     </ProjectReference>
-    <ProjectReference Include="..\Plugin.SNGenerateTool\Plugin.SNGenerateTool.csproj">
-      <Project>{3e18af3f-8212-45a5-a6e3-f5b1cfa3afae}</Project>
-      <Name>Plugin.SNGenerateTool</Name>
-    </ProjectReference>
     <ProjectReference Include="..\Plugin.TestBuilder\Plugin.TestBuilder.csproj">
       <Project>{30201ec1-9a82-48db-94b9-bd5c1190228b}</Project>
       <Name>Plugin.TestBuilder</Name>
diff --git a/HMI.UnitTest/MainWindow.xaml.cs b/HMI.UnitTest/MainWindow.xaml.cs
index daa000d..2de0f1d 100644
--- a/HMI.UnitTest/MainWindow.xaml.cs
+++ b/HMI.UnitTest/MainWindow.xaml.cs
@@ -33,18 +33,29 @@
 
         private void Button_Click(object sender, RoutedEventArgs e)
         {
-            CountdownEvent c = new CountdownEvent(4);
-            while (c.CurrentCount > 0) {
-                Task.Run(() =>
+            Task.Run(() =>
+            {
+                for (int j = 0; j < 1000; j++)
                 {
-                    int i = c.CurrentCount;
-                    c.Signal();
-                    for (int j = 0; j < 1000; j++)
-                    {
-                        tsExecution.AddTestData(i, $"{j},2,3,4,5,6,7");
-                    }
-                });
-            }
+                    string status = (j % 2 == 0) ? "Passed" : "Failed";
+                    tsExecution.AddTestData(0, $"{j},2,3,4,5,6,{status}");
+                }
+            });
+
+            tsExecution.UpdatePositionStatus(0, "Passed");
+            tsExecution.UpdatePositionProcessValue(0, 12);
+            tsExecution.PausePosition(0);
+            tsExecution.SetPositionBarcode(0, "123456789");
+
+
+            //tsExecution.UpdatePositionStatus(1, "Failed");
+            //tsExecution.UpdatePositionStatus(2, "Ready");
+            //tsExecution.UpdatePositionStatus(3, "Testing");
+            //tsExecution.UpdatePositionProcessValue(1, 50);
+            //tsExecution.UpdatePositionProcessValue(2, 100);
+            //tsExecution.UpdatePositionProcessValue(3, 99);
+
+
             //client = new PipeClient2("DAService");
             ////client.OnData += Client_OnData1;
             //client.Connect();
@@ -151,10 +162,10 @@
 
         private void Window_Loaded(object sender, RoutedEventArgs e)
         {
-            var plugin = new DataBoard();
+            var plugin = new TsExecution();
             HandlePlugin(plugin);
             border.Child = plugin;
-            dataBoard = plugin;
+            tsExecution = plugin;
             this.Loaded -= Window_Loaded;
         }
     }
diff --git a/HMI.Util/ExcelHelper.cs b/HMI.Util/ExcelHelper.cs
index b2ee156..ee8f3fc 100644
--- a/HMI.Util/ExcelHelper.cs
+++ b/HMI.Util/ExcelHelper.cs
@@ -43,7 +43,8 @@
                 IRow rowTemp = SheetOne.CreateRow(i + 1);
                 for (int j = 0; j < properties.Count; j++)
                 {
-                    var value = properties[j].GetValue(datas[i]);
+                    var attr = attrs[j];
+                    var value = attr.IgnoreExport ? "" : properties[j].GetValue(datas[i]);
                     rowTemp.CreateCell(j).SetCellValue(value.ToString());
                 }
             }
@@ -97,7 +98,10 @@
                             string title = titleRow.GetCell(j).StringCellValue;
                             if (dict.ContainsKey(title))
                             {
-                                CellType ct = row.GetCell(j).CellType;
+                                var cell = row.GetCell(j);
+                                if (cell == null || cell.CellType == CellType.Blank)
+                                    continue;
+                                CellType ct = cell.CellType;
                                 object value = null;
                                 if (ct == CellType.String)
                                     value = row.GetCell(j).StringCellValue;
diff --git a/Plugin.DataBoard/DataBoard.xaml b/Plugin.DataBoard/DataBoard.xaml
index 81d6c11..d73e377 100644
--- a/Plugin.DataBoard/DataBoard.xaml
+++ b/Plugin.DataBoard/DataBoard.xaml
@@ -111,7 +111,7 @@
         Content="{DynamicResource bd.btn}"/>
             </Grid>
 
-            <wv2:WebView2 Grid.Row="2" Grid.ColumnSpan="4" Name = "webview2" 
+            <wv2:WebView2 Grid.Row="2" Grid.ColumnSpan="4" Name = "webview2"
                       SizeChanged="webview2_SizeChanged"
                       NavigationCompleted="Webview2_NavigationCompleted"
                       Loaded="webview2_Loaded"/>
diff --git a/Plugin.DataBoard/DataBoard.xaml.cs b/Plugin.DataBoard/DataBoard.xaml.cs
index 7e467e4..d820e13 100644
--- a/Plugin.DataBoard/DataBoard.xaml.cs
+++ b/Plugin.DataBoard/DataBoard.xaml.cs
@@ -6,6 +6,7 @@
 using System.Collections.Generic;
 using System.Collections.ObjectModel;
 using System.Data;
+using System.Drawing;
 using System.IO;
 using System.Linq;
 using System.Windows;
@@ -22,6 +23,7 @@
         {
             DataContext = this;
             InitializeComponent();
+            webview2.DefaultBackgroundColor = Color.Transparent;//Color.FromArgb(243, 249, 255);
         }
 
         private ObservableCollection<OptionData> _options = new ObservableCollection<OptionData> {
@@ -71,7 +73,6 @@
                 default:
                     break;
             }
-
         }
 
         private List<SerialData> SelectSerialDataByTime(DateTime start, DateTime end)
diff --git a/Plugin.DataBoard/Plugin.DataBoard.csproj b/Plugin.DataBoard/Plugin.DataBoard.csproj
index e09ab59..48a30fc 100644
--- a/Plugin.DataBoard/Plugin.DataBoard.csproj
+++ b/Plugin.DataBoard/Plugin.DataBoard.csproj
@@ -55,6 +55,7 @@
     <Reference Include="System" />
     <Reference Include="System.Configuration" />
     <Reference Include="System.Data" />
+    <Reference Include="System.Drawing" />
     <Reference Include="System.Web" />
     <Reference Include="System.Xml" />
     <Reference Include="Microsoft.CSharp" />
diff --git a/Plugin.JarUI/JarCell.xaml b/Plugin.JarUI/JarCell.xaml
new file mode 100644
index 0000000..1b82db9
--- /dev/null
+++ b/Plugin.JarUI/JarCell.xaml
@@ -0,0 +1,80 @@
+锘�<UserControl x:Class="Plugin.JarUI.JarCell"
+             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.JarUI"
+             mc:Ignorable="d"
+             xmlns:hb="clr-namespace:HMI.Base;assembly=HMI.Base"
+            xmlns:uic="clr-namespace:HMI.UI.Converter;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="10"
+             d:DesignHeight="450" d:DesignWidth="800">
+    <UserControl.Resources>
+        <Brush x:Key="passedColor">#beed00</Brush>
+    </UserControl.Resources>
+    <Grid>
+        <Grid.RowDefinitions>
+            <RowDefinition Height="Auto"/>
+            <RowDefinition Height="Auto"/>
+            <RowDefinition />
+        </Grid.RowDefinitions>
+
+        <pu:FormGroup Orientation="Vertical" HeaderHeight="Auto" HeaderPadding="0">
+            <pu:FormGroup.Header>
+                <TextBlock Foreground="#000" Text="{Binding SnText}"/>
+            </pu:FormGroup.Header>
+            <TextBox x:Name="TbSn" Style="{StaticResource EditorTextBoxStyle}" />
+        </pu:FormGroup>
+
+        <pu:FormGroup Grid.Row="1" Orientation="Vertical" HeaderHeight="Auto" HeaderPadding="0">
+            <pu:FormGroup.Header>
+                <TextBlock Foreground="#000" Text="{DynamicResource App.Status}"/>
+            </pu:FormGroup.Header>
+            <Label Margin="0" Padding="0,10" 
+       HorizontalContentAlignment="Center" 
+       VerticalContentAlignment="Center">
+                <Label.Style>
+                    <Style TargetType="Label">
+                        <Setter Property="Content" Value="{Binding Status}"/>
+                        <Setter Property="FontSize" Value="30"/>
+                        <Style.Triggers>
+                            <DataTrigger Binding="{Binding Status}" Value="Ready">
+                                <Setter Property="Background" Value="DeepSkyBlue"/>
+                            </DataTrigger>
+                            <DataTrigger Binding="{Binding Status}" Value="Passed">
+                                <Setter Property="Background" Value="{StaticResource passedColor}"/>
+                            </DataTrigger>
+                            <DataTrigger Binding="{Binding Status}" Value="Failed">
+                                <Setter Property="Background" Value="{StaticResource DangerColor}"/>
+                            </DataTrigger>
+                            <DataTrigger Binding="{Binding Status}" Value="Testing">
+                                <Setter Property="Background" Value="Gold"/>
+                            </DataTrigger>
+                        </Style.Triggers>
+                    </Style>
+                </Label.Style>
+            </Label>
+        </pu:FormGroup>
+
+        <pu:FormGroup Grid.Row="2" Orientation="Vertical" HeaderHeight="Auto" HeaderPadding="0">
+            <pu:FormGroup.Header>
+                <TextBlock Foreground="#000" Text="{DynamicResource App.Info}"/>
+            </pu:FormGroup.Header>
+            <Label Margin="0" Padding="0,10" 
+HorizontalContentAlignment="Center" 
+VerticalContentAlignment="Center">
+                <Label.Style>
+                    <Style TargetType="Label">
+                        <Setter Property="Content" Value="{Binding Info}"/>
+                        <Setter Property="FontSize" Value="30"/>
+                        <Setter Property="Background" Value="#e8e8e8"/>
+                    </Style>
+                </Label.Style>
+            </Label>
+        </pu:FormGroup>
+
+    </Grid>
+</UserControl>
diff --git a/Plugin.JarUI/JarCell.xaml.cs b/Plugin.JarUI/JarCell.xaml.cs
new file mode 100644
index 0000000..6c612c5
--- /dev/null
+++ b/Plugin.JarUI/JarCell.xaml.cs
@@ -0,0 +1,84 @@
+锘縰sing System.ComponentModel;
+using System.Runtime.CompilerServices;
+using System.Windows.Controls;
+
+namespace Plugin.JarUI
+{
+    /// <summary>
+    /// JarCell.xaml 鐨勪氦浜掗�昏緫
+    /// </summary>
+    public partial class JarCell : UserControl, INotifyPropertyChanged
+    {
+        #region ...
+        public event PropertyChangedEventHandler PropertyChanged;
+
+        protected void Set([CallerMemberName] string name = null)
+        {
+
+            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
+        }
+
+        protected void Set<T>(ref T old, T @new, [CallerMemberName] string propertyName = null)
+        {
+            old = @new;
+            if (propertyName != null)
+            {
+                Set(propertyName);
+            }
+        }
+        #endregion
+
+        public JarCell(int index)
+        {
+            DataContext = this;
+            Index = index;
+            Name = $"Socket_{index + 1}";
+            SnText = $"SN{index + 1}";
+            InitializeComponent();
+        }
+
+        #region Properties
+        public int Index { get; private set; }
+        public string Name { get; private set; }
+
+        private string _status = "Ready";
+
+        public string Status
+        {
+            get { return _status; }
+            set { Set(ref _status, value); }
+        }
+
+        private string _info = "";
+
+        public string Info
+        {
+            get { return _info; }
+            set { Set(ref _info, value); }
+        }
+
+        private string _snText;
+
+        public string SnText
+        {
+            get { return _snText; }
+            set { Set(ref _snText, value); }
+        }
+        #endregion
+
+        public void SetStatus(string status)
+        {
+            Status = status;
+        }
+
+        public void SetInfo(string info)
+        {
+            Info = info;
+        }
+
+        public void SetSn(string sn)
+        {
+            TbSn.Text = sn;
+        }
+    }
+}
diff --git a/Plugin.JarUI/JarUI.xaml b/Plugin.JarUI/JarUI.xaml
new file mode 100644
index 0000000..c160e66
--- /dev/null
+++ b/Plugin.JarUI/JarUI.xaml
@@ -0,0 +1,47 @@
+锘�<hb:PluginControl x:Class="Plugin.JarUI.JarUI"
+             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.JarUI"
+             mc:Ignorable="d"
+             xmlns:hb="clr-namespace:HMI.Base;assembly=HMI.Base"
+             xmlns:uic="clr-namespace:HMI.UI.Converter;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"
+             Background="#FFF"
+                  Loaded="PluginControl_Loaded"
+             d:DesignHeight="450" d:DesignWidth="800">
+    <Border Style="{StaticResource CardBoarder}" Padding="0">
+        <Grid dp:GridHelper.Space="5">
+            <Grid.RowDefinitions>
+                <RowDefinition Height="Auto"/>
+                <RowDefinition/>
+            </Grid.RowDefinitions>
+            <WindowsFormsHost x:Name="host" Visibility="Collapsed"/>
+            <DockPanel>
+                <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 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}"
+DisplayMemberPath="Name" SelectedValuePath="Name"
+                              MaxWidth="200"
+SelectedItem="{Binding SelectedProduct}" x:Name="CbProduct"/>
+                </pu:FormGroup>
+            </DockPanel>
+            <UniformGrid Grid.Row="1" x:Name="uGrid" dp:UniformGridHelper.InnerBorder="1" Columns="6">
+
+            </UniformGrid>
+        </Grid>
+    </Border>
+</hb:PluginControl>
diff --git a/Plugin.JarUI/JarUI.xaml.cs b/Plugin.JarUI/JarUI.xaml.cs
new file mode 100644
index 0000000..cdaea7c
--- /dev/null
+++ b/Plugin.JarUI/JarUI.xaml.cs
@@ -0,0 +1,498 @@
+锘縰sing HMI.Base;
+using HMI.Core.Entity;
+using HMI.Core.Manager;
+using HMI.Core.Util;
+using HMI.UI;
+using HMI.UI.Entity;
+using HMI.UI.Util;
+using NationalInstruments.TestStand.Interop.API;
+using NationalInstruments.TestStand.Interop.UI.Ax;
+using Panuon.WPF.UI;
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.IO;
+using System.IO.Ports;
+using System.Linq;
+using System.Reflection;
+using System.Windows;
+using System.Windows.Controls;
+
+namespace Plugin.JarUI
+{
+    /// <summary>
+    /// UserControl1.xaml 鐨勪氦浜掗�昏緫
+    /// </summary>
+    public partial class JarUI : PluginControl
+    {
+        public JarUI()
+        {
+            DataContext = this;
+            InitializeComponent();
+            InitProducts();
+            InitTsEngine();
+        }
+
+        public override void InitFinish()
+        {
+            string iniFile = Path.Combine(BaseFolder, "Config.ini");
+            if (!File.Exists(iniFile))
+            {
+                File.Create(iniFile);
+            }
+            this.iniFile = iniFile;
+        }
+
+        #region Properties
+
+        #region TestStand鐩稿叧
+        private AxApplicationMgr AxApplicationMgr;
+        /// <summary>
+        /// TS 寮曟搸
+        /// </summary>
+        private Engine engine = null;
+
+        private string testSeqFile = string.Empty;
+        /// <summary>
+        /// 娴嬭瘯搴忓垪
+        /// </summary>
+        private SequenceFile testSeq = null;
+
+        /// <summary>
+        /// 娴嬭瘯妯″瀷搴忓垪
+        /// </summary>
+        private SequenceFile modeSeq = null;
+        public SequenceContext SequenceContext { get; private set; } = null;
+        private Execution execution = null;
+        #endregion
+
+        private bool _isRunning;
+        public bool IsRunning
+        {
+            get => _isRunning;
+            set
+            {
+                Dispatcher.Invoke(() =>
+                {
+                    startBtn.IsEnabled = !value;
+                    stopBtn.IsEnabled = value;
+                    CbProduct.IsEnabled = !value;
+                });
+                Set(ref _isRunning, value);
+            }
+        }
+
+        private ObservableCollection<ProductData> _products;
+
+        public ObservableCollection<ProductData> Products
+        {
+            get { return _products; }
+            set { Set(ref _products, value); }
+        }
+
+        private ProductData _selectedProduct;
+
+        public ProductData SelectedProduct
+        {
+            get { return _selectedProduct; }
+            set
+            {
+                SetTestSeqFile(value);
+                Set(ref _selectedProduct, value);
+            }
+        }
+
+
+        private bool isLoadOk = false;
+        private string iniFile;
+        private JarCell selectedCell;
+
+        private JarCell[] tsSockets;
+        private Dictionary<string, JarCell> socketMap = new Dictionary<string, JarCell>();
+        private SerialPort serialPort;
+        #endregion
+
+        private void RefershUI()
+        {
+            tsSockets = new JarCell[18];
+            for (int i = 0; i < 18; i++)
+            {
+                var cell = new JarCell(i);
+                cell.GotFocus += Cell_GotFocus;
+                uGrid.Children.Add(cell);
+                tsSockets[i] = cell;
+                socketMap.Add(cell.Name, cell);
+            }
+        }
+
+        private void Cell_GotFocus(object sender, RoutedEventArgs e)
+        {
+            selectedCell = sender as JarCell;
+        }
+
+        private void SetTestSeqFile(ProductData product)
+        {
+            if (product == null)
+            {
+                testSeqFile = null;
+                return;
+            }
+            string name = product.Name;
+            List<ParameterData> parameters = ProductManager.GetParametersByProductName(name);
+            testSeqFile = parameters.FirstOrDefault(p => p.Name == "TestSequence")?.Value?.ToString();
+            StartSequence();
+        }
+
+        private void InitProducts()
+        {
+            Products = ProductManager.GetProducts<ObservableCollection<ProductData>>();
+            if (Products != null && Products.Count > 0)
+            {
+                List<ParameterData> parameters =
+                    ParameterManager.GetCommonParameter<List<ParameterData>>();
+                string tmpNum = parameters?.FirstOrDefault(p => p.Name == "SocketNum")?.Value?.ToString();
+                string lastModel = engine?.Globals.GetValString("StationState.ProductModel", 1);
+
+                if (!string.IsNullOrEmpty(lastModel))
+                {
+                    var product = Products.FirstOrDefault(p => p.Name == lastModel);
+                    if (product != null) SelectedProduct = product;
+                    goto Write;
+                }
+                goto Write;
+
+            Write:
+                SelectedProduct = Products[0];
+                engine?.Globals.SetValString("StationState.ProductModel", 1, Products[0].Name);
+
+                RefershUI();
+            }
+        }
+
+        #region 鎵爜鏋浉鍏�
+        /// <summary>
+        /// 璁剧疆鎵爜鏋�
+        /// </summary>
+        private void InitScanner()
+        {
+            if (serialPort != null) return;
+            ScannerConfig common = ScannerConfig.ReadFromIni(iniFile, "Config");
+            // 閰嶇疆涓插彛
+            serialPort = new SerialPort
+            {
+                PortName = common.PortName,
+                BaudRate = common.BaudRate,
+                Parity = Parity.None,
+                DataBits = common.DataBits,
+                StopBits = common.StopBits,
+                Handshake = Handshake.None,
+                DtrEnable = common.DtrEnable,
+                RtsEnable = common.RtsEnable
+                //ReadTimeout = 500,
+                //WriteTimeout = 500,
+                //ReciveBufferSize=4096
+            };
+
+            try
+            {
+                // 鎵撳紑涓插彛
+                serialPort.Open();
+                // 娉ㄥ唽鎺ユ敹鏁版嵁浜嬩欢
+                serialPort.DataReceived += SerialPort_DataReceived;
+            }
+            catch (Exception ex)
+            {
+                log.Error("Init Scan Gun Failed.", ex);
+                MsgBoxHelper.Error(this, $"{this.lang("Tr.InitGunFailed")}: {ex.StackTrace}");
+            }
+        }
+        private string ReadDataFrom(SerialPort sp)
+        {
+            string barcode = "";
+            while (sp.BytesToRead > 0)
+            {
+                byte[] buffer = new byte[sp.BytesToRead];
+                sp.Read(buffer, 0, buffer.Length);
+                barcode += System.Text.Encoding.UTF8.GetString(buffer);
+                System.Threading.Thread.Sleep(50);
+            }
+            return barcode;
+        }
+
+        private void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
+        {
+            try
+            {
+                SerialPort sp = (SerialPort)sender;
+                string barcode = ReadDataFrom(sp);
+                if (selectedCell != null)
+                {
+                    Dispatcher.Invoke(() =>
+                    {
+                        selectedCell.SetSn(barcode);
+                    });
+                    SequenceContext.StationGlobals.SetValString($"StationState.SN[{selectedCell.Index}]", 1, barcode);
+                    SequenceContext.StationGlobals.SetValBoolean($"StationState.Socket_{selectedCell.Index}", 1, true);
+                }
+            }
+            catch (Exception ex)
+            {
+                log.Error("Get Scan Gun Data Failed.", ex);
+                MessageBox.Show($"{this.lang("Tr.GetGunData")}: {ex.StackTrace}");
+            }
+        }
+        #endregion
+
+        private void Button_Click(object sender, RoutedEventArgs e)
+        {
+            Button btn = sender as Button;
+            switch (btn?.Name)
+            {
+                case "startBtn":
+                    StartBtnClick();
+                    break;
+                case "stopBtn":
+                    StopBtnClick();
+                    break;
+                case "scannerSettingBtn":
+                    ScannerSetting ss = new ScannerSetting
+                    {
+                        Owner = Window.GetWindow(this)
+                    };
+                    if (ss.Open(true, "Config", iniFile) == true)
+                    {
+                        serialPort?.Close();
+                        InitScanner();
+                    }
+                    break;
+                default:
+                    break;
+            }
+        }
+
+        private void StartBtnClick()
+        {
+            new MaskWindow(Window.GetWindow(this),
+                (win) =>
+                {
+                    try
+                    {
+                        StartSequence();
+                    }
+                    finally
+                    {
+                        win.Dispatcher.Invoke(() =>
+                        {
+                            win.Close();
+                        });
+                    }
+                }).Info(LangUtil.Get("Ts.SequenceIsStarting"));
+        }
+
+        public void StopBtnClick()
+        {
+            new MaskWindow((WindowX)Window.GetWindow(this),
+                    (win) =>
+                    {
+                        try
+                        {
+                            bool stoped = StopSequence();
+                            if (stoped)
+                            {
+                                IsRunning = false;
+                            }
+                        }
+                        finally
+                        {
+                            Dispatcher.Invoke(() =>
+                            {
+                                win.Close();
+                            });
+                        }
+                    }
+                    ).Info(LangUtil.Get("App.SequenceIsStopping"));
+        }
+
+        private bool StopSequence()
+        {
+            try
+            {
+                SequenceContext?.StationGlobals.SetValBoolean("StationState.StopTest", 1, true);
+                if (this.execution == null) return true;
+                return this.execution.WaitForEndEx(2000, true, null, null);
+            }
+            catch (Exception ex)
+            {
+                log.Error("Stop Sequence Failed.", ex);
+                MsgBoxHelper.Error(this, $"{LangUtil.Get("Ts.StopSequenceFail")}: {ex.Message}");
+                return false;
+            }
+        }
+
+        private void PluginControl_Loaded(object sender, RoutedEventArgs e)
+        {
+            new MaskWindow((WindowX)Window.GetWindow(this),
+                (win) =>
+                {
+                    try
+                    {
+                        InitScanner();
+                        isLoadOk = true;
+                        StartSequence();
+                    }
+                    catch (Exception ex)
+                    {
+                        log.Error("TsExecution Loaded Failed.", ex);
+                        MsgBoxHelper.Error(this, $"{LangUtil.Get("Ts.UserControlLoadFailed")}: {ex.Message}");
+                    }
+                    Dispatcher.Invoke(() =>
+                    {
+                        if (isLoadOk) Loaded -= PluginControl_Loaded;
+                    });
+                }
+                ).Info(LangUtil.Get("App.SequenceIsStarting"));
+        }
+
+        #region teststand鍒濆鍖栫浉鍏�
+        /// <summary>
+        /// 鍚姩娴嬭瘯搴忓垪
+        /// </summary>
+        private void StartSequence()
+        {
+            try
+            {
+                if (!isLoadOk) return;
+                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);
+                IsRunning = true;
+            }
+            catch (Exception ex)
+            {
+                Dispatcher.Invoke(() =>
+                {
+                    log.Error("Start Sequence Failed.", ex);
+                    MsgBoxHelper.Error(this, $"{LangUtil.Get("Ts.StartSequenceFail")}: {ex.Message}");
+                });
+            }
+        }
+
+        /// <summary>
+        /// 鍒濆鍖朤estStand寮曟搸
+        /// </summary>
+        private void InitTsEngine()
+        {
+            try
+            {
+                AxApplicationMgr = new AxApplicationMgr();
+                AxApplicationMgr.ExitApplication += new EventHandler(this.AxApplicationMgr_ExitApplication);
+                AxApplicationMgr.UIMessageEvent +=
+                    new _ApplicationMgrEvents_UIMessageEventEventHandler(this.AxApplicationMgr_UIMessageEvent);
+                AxApplicationMgr.ExecutionClosed +=
+                    new _ApplicationMgrEvents_ExecutionClosedEventHandler(this.AxApplicationMgr_ExecutionClosed);
+                ((System.ComponentModel.ISupportInitialize)(AxApplicationMgr)).BeginInit();
+                host.Child = AxApplicationMgr;
+                ((System.ComponentModel.ISupportInitialize)(AxApplicationMgr)).EndInit();
+
+                this.engine = this.AxApplicationMgr.GetEngine();
+                this.engine.PersistBreakpoints = false;
+                this.AxApplicationMgr.IsEditor = true;
+                this.AxApplicationMgr.LoginOnStart = false;
+                this.AxApplicationMgr.Start();
+                this.modeSeq = this.engine.GetSequenceFileEx(@"C:\Program Files\National Instruments\TestStand 2020\Components\Models\TestStandModels\ParallelModel.seq");
+            }
+            catch (Exception ex)
+            {
+                log.Error("InitTsEngine Failed.", ex);
+                throw ex;
+            }
+        }
+
+        /// <summary>
+        /// 閫�鍑哄簲鐢�
+        /// </summary>
+        /// <param name="sender"></param>
+        /// <param name="e"></param>
+        private void AxApplicationMgr_ExitApplication(object sender, EventArgs e)
+        {
+            //Environment.ExitCode = this.AxApplicationMgr.ExitCode;
+            //this.Close();
+        }
+
+        /// <summary>
+        /// 杩涚▼缁撴潫浜嬩欢
+        /// </summary>
+        /// <param name="sender"></param>
+        /// <param name="e"></param>
+        private void AxApplicationMgr_ExecutionClosed(object sender, _ApplicationMgrEvents_ExecutionClosedEvent e)
+        {
+            try
+            {
+                if (this.testSeq == null)
+                    return;
+                this.engine.ReleaseSequenceFileEx(this.testSeq, 0);
+                this.engine.ReleaseSequenceFileEx(this.modeSeq, 0);
+                this.engine.UnloadAllModules();
+                this.testSeq = null;
+                this.modeSeq = null;
+            }
+            catch (Exception ex)
+            {
+                log.Error("ReleaseSequenceFileEx Failed.", ex);
+                MsgBoxHelper.Error(Window.GetWindow(this), $"{LangUtil.Get("Ts.ReleaseSequenceFileFail")}: {ex.Message}");
+            }
+        }
+
+        /// <summary>
+        /// TestStand鐢ㄦ埛浜や簰浜嬩欢
+        /// </summary>
+        /// <param name="sender"></param>
+        /// <param name="e"></param>
+        private void AxApplicationMgr_UIMessageEvent(object sender, _ApplicationMgrEvents_UIMessageEventEvent e)
+        {
+            try
+            {
+                int EventCase = Convert.ToInt32(e.uiMsg.Event);
+                int MethodCode = Convert.ToInt16(e.uiMsg.NumericData);
+                string strData = e.uiMsg.StringData;
+
+                try
+                {
+                    log.Info($"Event: {EventCase}, NumericData: {MethodCode}, StringData: {strData}");
+                }
+                catch
+                {
+
+                }
+
+                if (EventCase != 10015) return;
+                switch (MethodCode)
+                {
+                    case 0:
+                        /// 璁剧疆鍙ユ焺
+                        this.SequenceContext = (SequenceContext)e.uiMsg.ActiveXData;
+                        break;
+                    case 1:
+                        var arr1 = strData.Split(';');
+                        int index1 = int.Parse(arr1[0]);
+                        this.tsSockets[index1].SetStatus(arr1[1]);
+                        break;
+                    case 2:
+                        var arr2 = strData.Split(';');
+                        int index2 = int.Parse(arr2[0]);
+                        this.tsSockets[index2].SetInfo(arr2[1]);
+                        break;
+                    default:
+                        break;
+                }
+            }
+            catch (Exception ex)
+            {
+                log.Warn("Ts Event Error.", ex);
+            }
+        }
+        #endregion
+
+
+    }
+}
diff --git a/Plugin.JarUI/Plugin.JarUI.csproj b/Plugin.JarUI/Plugin.JarUI.csproj
new file mode 100644
index 0000000..6c998d7
--- /dev/null
+++ b/Plugin.JarUI/Plugin.JarUI.csproj
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProjectGuid>{968FE602-22D6-42BD-981C-601AD518A896}</ProjectGuid>
+    <OutputType>library</OutputType>
+    <RootNamespace>Plugin.JarUI</RootNamespace>
+    <AssemblyName>Plugin.JarUI</AssemblyName>
+    <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
+    <FileAlignment>512</FileAlignment>
+    <ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+    <WarningLevel>4</WarningLevel>
+    <Deterministic>true</Deterministic>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>bin\Release\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="log4net, Version=3.0.4.0, Culture=neutral, PublicKeyToken=669e0ddf0bb1aa2a, processorArchitecture=MSIL">
+      <HintPath>..\WPFProject\packages\log4net.3.0.4\lib\net462\log4net.dll</HintPath>
+    </Reference>
+    <Reference Include="NationalInstruments.TestStand.Interop.API, Version=20.0.0.49158, Culture=neutral, PublicKeyToken=ad9244eb3b825cd8, processorArchitecture=MSIL">
+      <EmbedInteropTypes>True</EmbedInteropTypes>
+    </Reference>
+    <Reference Include="NationalInstruments.TestStand.Interop.UI.AxControls, Version=20.0.0.49158, Culture=neutral, PublicKeyToken=ad9244eb3b825cd8, processorArchitecture=MSIL" />
+    <Reference Include="Panuon.WPF, Version=1.1.2.2, Culture=neutral, processorArchitecture=MSIL">
+      <HintPath>..\WPFProject\packages\Panuon.WPF.1.1.3\lib\net472\Panuon.WPF.dll</HintPath>
+    </Reference>
+    <Reference Include="Panuon.WPF.UI, Version=1.3.0.2, Culture=neutral, processorArchitecture=MSIL">
+      <HintPath>..\WPFProject\packages\Panuon.WPF.UI.1.3.0.2\lib\net472\Panuon.WPF.UI.dll</HintPath>
+    </Reference>
+    <Reference Include="System" />
+    <Reference Include="System.Configuration" />
+    <Reference Include="System.Data" />
+    <Reference Include="System.Web" />
+    <Reference Include="System.Windows.Forms" />
+    <Reference Include="System.Xml" />
+    <Reference Include="Microsoft.CSharp" />
+    <Reference Include="System.Core" />
+    <Reference Include="System.Xml.Linq" />
+    <Reference Include="System.Data.DataSetExtensions" />
+    <Reference Include="System.Net.Http" />
+    <Reference Include="System.Xaml">
+      <RequiredTargetFramework>4.0</RequiredTargetFramework>
+    </Reference>
+    <Reference Include="WindowsBase" />
+    <Reference Include="PresentationCore" />
+    <Reference Include="PresentationFramework" />
+    <Reference Include="WindowsFormsIntegration" />
+  </ItemGroup>
+  <ItemGroup>
+    <Page Include="JarCell.xaml">
+      <SubType>Designer</SubType>
+      <Generator>MSBuild:Compile</Generator>
+    </Page>
+    <Page Include="JarUI.xaml">
+      <Generator>MSBuild:Compile</Generator>
+      <SubType>Designer</SubType>
+    </Page>
+    <Compile Include="JarCell.xaml.cs">
+      <DependentUpon>JarCell.xaml</DependentUpon>
+    </Compile>
+    <Compile Include="JarUI.xaml.cs">
+      <DependentUpon>JarUI.xaml</DependentUpon>
+      <SubType>Code</SubType>
+    </Compile>
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="Properties\AssemblyInfo.cs">
+      <SubType>Code</SubType>
+    </Compile>
+    <Compile Include="Properties\Resources.Designer.cs">
+      <AutoGen>True</AutoGen>
+      <DesignTime>True</DesignTime>
+      <DependentUpon>Resources.resx</DependentUpon>
+    </Compile>
+    <Compile Include="Properties\Settings.Designer.cs">
+      <AutoGen>True</AutoGen>
+      <DependentUpon>Settings.settings</DependentUpon>
+      <DesignTimeSharedInput>True</DesignTimeSharedInput>
+    </Compile>
+    <EmbeddedResource Include="Properties\Resources.resx">
+      <Generator>ResXFileCodeGenerator</Generator>
+      <LastGenOutput>Resources.Designer.cs</LastGenOutput>
+    </EmbeddedResource>
+    <None Include="packages.config" />
+    <None Include="Properties\Settings.settings">
+      <Generator>SettingsSingleFileGenerator</Generator>
+      <LastGenOutput>Settings.Designer.cs</LastGenOutput>
+    </None>
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\HMI.Base\HMI.Base.csproj">
+      <Project>{2a6f9720-a890-4036-9d73-41a0dae28341}</Project>
+      <Name>HMI.Base</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\HMI.Core\HMI.Core.csproj">
+      <Project>{3e3bf44a-0b69-45c5-9512-528cb48a7106}</Project>
+      <Name>HMI.Core</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\HMI.UI\HMI.UI.csproj">
+      <Project>{f17de6b5-af1d-48c6-94a0-6b763a6546a6}</Project>
+      <Name>HMI.UI</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\HMI.Util\HMI.Util.csproj">
+      <Project>{afea100a-9223-448d-ab10-3015d93dd98c}</Project>
+      <Name>HMI.Util</Name>
+    </ProjectReference>
+  </ItemGroup>
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+</Project>
\ No newline at end of file
diff --git a/Plugin.JarUI/Properties/AssemblyInfo.cs b/Plugin.JarUI/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..c4dfda0
--- /dev/null
+++ b/Plugin.JarUI/Properties/AssemblyInfo.cs
@@ -0,0 +1,52 @@
+锘縰sing HMI.Core.Annotation;
+using System.Reflection;
+using System.Runtime.InteropServices;
+using System.Windows;
+
+// 鏈夊叧绋嬪簭闆嗙殑涓�鑸俊鎭敱浠ヤ笅
+// 鎺у埗銆傛洿鏀硅繖浜涚壒鎬у�煎彲淇敼
+// 涓庣▼搴忛泦鍏宠仈鐨勪俊鎭��
+[assembly: AssemblyTitle("Plugin.JarUI")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("Plugin.JarUI")]
+[assembly: AssemblyCopyright("Copyright 漏  2025")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// 灏� ComVisible 璁剧疆涓� false 浼氫娇姝ょ▼搴忛泦涓殑绫诲瀷
+//瀵� COM 缁勪欢涓嶅彲瑙併�傚鏋滈渶瑕佷粠 COM 璁块棶姝ょ▼搴忛泦涓殑绫诲瀷
+//璇峰皢姝ょ被鍨嬬殑 ComVisible 鐗规�ц缃负 true銆�
+[assembly: ComVisible(false)]
+
+//鑻ヨ寮�濮嬬敓鎴愬彲鏈湴鍖栫殑搴旂敤绋嬪簭锛岃璁剧疆
+//.csproj 鏂囦欢涓殑 <UICulture>CultureYouAreCodingWith</UICulture>
+//鍦� <PropertyGroup> 涓�備緥濡傦紝濡傛灉浣犱娇鐢ㄧ殑鏄編鍥借嫳璇��
+//浣跨敤鐨勬槸缇庡浗鑻辫锛岃灏� <UICulture> 璁剧疆涓� en-US銆�  鐒跺悗鍙栨秷
+//瀵逛互涓� NeutralResourceLanguage 鐗规�х殑娉ㄩ噴銆�  鏇存柊
+//浠ヤ笅琛屼腑鐨勨�渆n-US鈥濅互鍖归厤椤圭洰鏂囦欢涓殑 UICulture 璁剧疆銆�
+
+//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
+
+
+[assembly:ThemeInfo(
+    ResourceDictionaryLocation.None, //涓婚鐗瑰畾璧勬簮璇嶅吀鎵�澶勪綅缃�
+                             //(鏈湪椤甸潰涓壘鍒拌祫婧愭椂浣跨敤锛�
+                             //鎴栧簲鐢ㄧ▼搴忚祫婧愬瓧鍏镐腑鎵惧埌鏃朵娇鐢�)
+    ResourceDictionaryLocation.SourceAssembly //甯歌璧勬簮璇嶅吀鎵�澶勪綅缃�
+                                      //(鏈湪椤甸潰涓壘鍒拌祫婧愭椂浣跨敤锛�
+                                      //銆佸簲鐢ㄧ▼搴忔垨浠讳綍涓婚涓撶敤璧勬簮瀛楀吀涓壘鍒版椂浣跨敤)
+)]
+
+
+// 绋嬪簭闆嗙殑鐗堟湰淇℃伅鐢变笅鍒楀洓涓�肩粍鎴�: 
+//
+//      涓荤増鏈�
+//      娆$増鏈�
+//      鐢熸垚鍙�
+//      淇鍙�
+//
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
+[assembly: PluginClassName("Plugin.JarUI.JarUI")]
diff --git a/Plugin.JarUI/Properties/Resources.Designer.cs b/Plugin.JarUI/Properties/Resources.Designer.cs
new file mode 100644
index 0000000..3d09aed
--- /dev/null
+++ b/Plugin.JarUI/Properties/Resources.Designer.cs
@@ -0,0 +1,62 @@
+锘�//------------------------------------------------------------------------------
+// <auto-generated>
+//     姝や唬鐮佺敱宸ュ叿鐢熸垚銆�
+//     杩愯鏃剁増鏈�: 4.0.30319.42000
+//
+//     瀵规鏂囦欢鐨勬洿鏀瑰彲鑳藉鑷翠笉姝g‘鐨勮涓猴紝濡傛灉
+//     閲嶆柊鐢熸垚浠g爜锛屽垯鎵�鍋氭洿鏀瑰皢涓㈠け銆�
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+namespace Plugin.JarUI.Properties {
+    
+    
+    /// <summary>
+    ///   寮虹被鍨嬭祫婧愮被锛岀敤浜庢煡鎵炬湰鍦板寲瀛楃涓茬瓑銆�
+    /// </summary>
+    // 姝ょ被鏄敱 StronglyTypedResourceBuilder
+    // 绫婚�氳繃绫讳技浜� ResGen 鎴� Visual Studio 鐨勫伐鍏疯嚜鍔ㄧ敓鎴愮殑銆�
+    // 鑻ヨ娣诲姞鎴栫Щ闄ゆ垚鍛橈紝璇风紪杈� .ResX 鏂囦欢锛岀劧鍚庨噸鏂拌繍琛� ResGen
+    // (浠� /str 浣滀负鍛戒护閫夐」)锛屾垨閲嶆柊鐢熸垚 VS 椤圭洰銆�
+    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+    internal class Resources {
+        
+        private static global::System.Resources.ResourceManager resourceMan;
+        
+        private static global::System.Globalization.CultureInfo resourceCulture;
+        
+        [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+        internal Resources() {
+        }
+        
+        /// <summary>
+        ///   杩斿洖姝ょ被浣跨敤鐨勭紦瀛� ResourceManager 瀹炰緥銆�
+        /// </summary>
+        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+        internal static global::System.Resources.ResourceManager ResourceManager {
+            get {
+                if ((resourceMan == null)) {
+                    global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Plugin.JarUI.Properties.Resources", typeof(Resources).Assembly);
+                    resourceMan = temp;
+                }
+                return resourceMan;
+            }
+        }
+        
+        /// <summary>
+        ///   閲嶅啓褰撳墠绾跨▼鐨� CurrentUICulture 灞炴�э紝瀵�
+        ///   浣跨敤姝ゅ己绫诲瀷璧勬簮绫荤殑鎵�鏈夎祫婧愭煡鎵炬墽琛岄噸鍐欍��
+        /// </summary>
+        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+        internal static global::System.Globalization.CultureInfo Culture {
+            get {
+                return resourceCulture;
+            }
+            set {
+                resourceCulture = value;
+            }
+        }
+    }
+}
diff --git a/Plugin.JarUI/Properties/Resources.resx b/Plugin.JarUI/Properties/Resources.resx
new file mode 100644
index 0000000..af7dbeb
--- /dev/null
+++ b/Plugin.JarUI/Properties/Resources.resx
@@ -0,0 +1,117 @@
+锘�<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+</root>
\ No newline at end of file
diff --git a/Plugin.JarUI/Properties/Settings.Designer.cs b/Plugin.JarUI/Properties/Settings.Designer.cs
new file mode 100644
index 0000000..0911324
--- /dev/null
+++ b/Plugin.JarUI/Properties/Settings.Designer.cs
@@ -0,0 +1,30 @@
+锘�//------------------------------------------------------------------------------
+// <auto-generated>
+//     This code was generated by a tool.
+//     Runtime Version:4.0.30319.42000
+//
+//     Changes to this file may cause incorrect behavior and will be lost if
+//     the code is regenerated.
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+namespace Plugin.JarUI.Properties
+{
+    
+    
+    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
+    internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
+    {
+        
+        private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
+        
+        public static Settings Default
+        {
+            get
+            {
+                return defaultInstance;
+            }
+        }
+    }
+}
diff --git a/Plugin.JarUI/Properties/Settings.settings b/Plugin.JarUI/Properties/Settings.settings
new file mode 100644
index 0000000..033d7a5
--- /dev/null
+++ b/Plugin.JarUI/Properties/Settings.settings
@@ -0,0 +1,7 @@
+锘�<?xml version='1.0' encoding='utf-8'?>
+<SettingsFile xmlns="uri:settings" CurrentProfile="(Default)">
+  <Profiles>
+    <Profile Name="(Default)" />
+  </Profiles>
+  <Settings />
+</SettingsFile>
\ No newline at end of file
diff --git a/Plugin.JarUI/packages.config b/Plugin.JarUI/packages.config
new file mode 100644
index 0000000..c3ebe09
--- /dev/null
+++ b/Plugin.JarUI/packages.config
@@ -0,0 +1,6 @@
+锘�<?xml version="1.0" encoding="utf-8"?>
+<packages>
+  <package id="log4net" version="3.0.4" targetFramework="net472" />
+  <package id="Panuon.WPF" version="1.1.3" targetFramework="net472" />
+  <package id="Panuon.WPF.UI" version="1.3.0.2" targetFramework="net472" />
+</packages>
\ No newline at end of file
diff --git a/Plugin.SNGenerateTool/Form1.cs b/Plugin.SNGenerateTool/Form1.cs
index 43385d6..b322481 100644
--- a/Plugin.SNGenerateTool/Form1.cs
+++ b/Plugin.SNGenerateTool/Form1.cs
@@ -26,8 +26,6 @@
 
     private DataTable table = new DataTable();
 
-    private bool flag;
-
     private string[] years = new string[30]
     {
         "1", "2", "3", "4", "5", "6", "7", "8", "9", "A",
diff --git a/Plugin.SNGenerateTool/UserControl1.xaml.cs b/Plugin.SNGenerateTool/UserControl1.xaml.cs
index 5538e26..e523016 100644
--- a/Plugin.SNGenerateTool/UserControl1.xaml.cs
+++ b/Plugin.SNGenerateTool/UserControl1.xaml.cs
@@ -168,7 +168,6 @@
                     int num14 = num5;
                     while (num11 <= num2)
                     {
-                        DataRow dataRow = null;
                         DataInfo di = new DataInfo();
                         if (num12.ToString().Length < 5 && num13.ToString().Length < 8 && num14.ToString().Length < 5)
                         {
diff --git a/Plugin.TestResult/ViewControl.xaml.cs b/Plugin.TestResult/ViewControl.xaml.cs
index 4ea1f1f..f072a88 100644
--- a/Plugin.TestResult/ViewControl.xaml.cs
+++ b/Plugin.TestResult/ViewControl.xaml.cs
@@ -342,7 +342,7 @@
 
         private void ScannerSetting_Click(object sender, RoutedEventArgs e)
         {
-            ScannerSetting setting = new ScannerSetting() { 
+            Plugin.TestResult.ScannerSetting setting = new Plugin.TestResult.ScannerSetting() { 
                 Owner = Window.GetWindow(this)
             };
             setting.Open(false,"Config");
diff --git a/Plugin.TsExecution/SingleControl.xaml b/Plugin.TsExecution/SingleControl.xaml
index 77ef0cd..0aa2f1e 100644
--- a/Plugin.TsExecution/SingleControl.xaml
+++ b/Plugin.TsExecution/SingleControl.xaml
@@ -9,19 +9,27 @@
              xmlns:local="clr-namespace:Plugin.TsExecution"
              mc:Ignorable="d" 
              Loaded="UserControl_Loaded"
-             Background="{StaticResource WarningColorContract}"
+             Background="#ccc6c7"
+             FontSize="{Binding ActualHeight,RelativeSource={RelativeSource AncestorType=UserControl}, 
+Converter={StaticResource hfc}, ConverterParameter='12,816'}"
+             x:Name="control"
              d:DesignHeight="450" d:DesignWidth="800">
     <UserControl.Resources>
         <Style TargetType="Label" x:Key="baseLabel">
             <Setter Property="HorizontalContentAlignment" Value="Left"/>
             <Setter Property="VerticalContentAlignment" Value="Center"/>
             <Setter Property="Padding" Value="5"/>
+            <Setter Property="FontSize" Value="{Binding ActualWidth,ElementName=control,
+Converter={StaticResource fc}, ConverterParameter='20,1093,20'}"/>
         </Style>
         <Style TargetType="TextBlock" x:Key="baseText">
             <Setter Property="HorizontalAlignment" Value="Left"/>
             <Setter Property="VerticalAlignment" Value="Center"/>
-            <Setter Property="Padding" Value="5"/>
+            <Setter Property="Padding" Value="0,0,2,0"/>
+            <Setter Property="FontSize" Value="{Binding ActualWidth,ElementName=control,
+Converter={StaticResource fc}, ConverterParameter='20,1093,20'}"/>
         </Style>
+        <Brush x:Key="passedColor">#beed00</Brush>
     </UserControl.Resources>
 
     <Grid>
@@ -44,97 +52,185 @@
                 <ColumnDefinition Width="Auto"/>
                 <ColumnDefinition/>
             </Grid.ColumnDefinitions>
-            <Label Grid.RowSpan="3">
-                <Label.Style>
-                    <Style TargetType="Label">
-                        <Setter Property="Padding" Value="10"/>
-                        <Setter Property="FontFamily" Value="{StaticResource iconfont}"/>
+            <StackPanel Grid.RowSpan="3">
+                <StackPanel.Style>
+                    <Style TargetType="StackPanel">
                         <Setter Property="Background" Value="DeepSkyBlue"/>
-                        <Setter Property="Foreground" Value="White"/>
-                        <Setter Property="HorizontalContentAlignment" Value="Center"/>
-                        <Setter Property="VerticalContentAlignment" Value="Center"/>
-                        <Setter Property="FontSize" Value="50"/>
                         <Style.Triggers>
                             <DataTrigger Binding="{Binding Status}" Value="Passed">
-                                <Setter Property="Content" Value="&#xe65a;"/>
-                                <Setter Property="Background" Value="Lime"/>
+                                <Setter Property="Background" Value="{StaticResource passedColor}"/>
                             </DataTrigger>
                             <DataTrigger Binding="{Binding Status}" Value="Testing">
-                                <Setter Property="Content" Value="&#xeb61;"/>
                                 <Setter Property="Background" Value="Gold"/>
                             </DataTrigger>
                             <DataTrigger Binding="{Binding Status}" Value="Failed">
-                                <Setter Property="Content" Value="&#xe604;"/>
                                 <Setter Property="Background" Value="{StaticResource DangerColor}"/>
                             </DataTrigger>
                             <DataTrigger Binding="{Binding Status}" Value="Failed">
-                                <Setter Property="Content" Value="&#xe604;"/>
                                 <Setter Property="Background" Value="{StaticResource DangerColor}"/>
                             </DataTrigger>
                             <DataTrigger Binding="{Binding Status}" Value="Ready">
-                                <Setter Property="Content" Value="&#xe6ae;"/>
                                 <Setter Property="Background" Value="DeepSkyBlue"/>
+                            </DataTrigger>
+                        </Style.Triggers>
+                    </Style>
+                </StackPanel.Style>
+
+                <Label>
+                    <Label.Style>
+                        <Style TargetType="Label">
+                            <Setter Property="Padding" Value="10,10,10,0"/>
+                            <Setter Property="Margin" Value="0"/>
+                            <Setter Property="FontFamily" Value="{StaticResource iconfont}"/>
+                            <Setter Property="Foreground" Value="White"/>
+                            <Setter Property="HorizontalContentAlignment" Value="Center"/>
+                            <Setter Property="VerticalContentAlignment" Value="Center"/>
+                            <Setter Property="FontSize" Value="{Binding ActualWidth,ElementName=control,
+Converter={StaticResource fc}, ConverterParameter='50,816'}"/>
+                            <Style.Triggers>
+                                <DataTrigger Binding="{Binding Status}" Value="Passed">
+                                    <Setter Property="Content" Value="&#xe65a;"/>
+                                    <Setter Property="Background" Value="{StaticResource passedColor}"/>
+                                </DataTrigger>
+                                <DataTrigger Binding="{Binding Status}" Value="Testing">
+                                    <Setter Property="Content" Value="&#xeb61;"/>
+                                    <Setter Property="Background" Value="Gold"/>
+                                </DataTrigger>
+                                <DataTrigger Binding="{Binding Status}" Value="Failed">
+                                    <Setter Property="Content" Value="&#xe604;"/>
+                                    <Setter Property="Background" Value="{StaticResource DangerColor}"/>
+                                </DataTrigger>
+                                <DataTrigger Binding="{Binding Status}" Value="Failed">
+                                    <Setter Property="Content" Value="&#xe604;"/>
+                                    <Setter Property="Background" Value="{StaticResource DangerColor}"/>
+                                </DataTrigger>
+                                <DataTrigger Binding="{Binding Status}" Value="Ready">
+                                    <Setter Property="Content" Value="&#xe6ae;"/>
+                                    <Setter Property="Background" Value="DeepSkyBlue"/>
+                                </DataTrigger>
+                            </Style.Triggers>
+                        </Style>
+                    </Label.Style>
+                </Label>
+
+                <Label Content="{Binding Status}" VerticalContentAlignment="Center"
+                       Padding="0"
+                       Margin="0"
+                       HorizontalContentAlignment="Center"
+                       Foreground="#FFF"
+                       FontWeight="Bold"
+                       FontSize="{Binding ActualWidth,ElementName=control,
+Converter={StaticResource fc}, ConverterParameter='15,816'}"/>
+            </StackPanel>
+
+            <!--<Label HorizontalContentAlignment="Left" Grid.Column="1" Content="{Binding Status}"/>-->
+
+            <Label Style="{StaticResource baseLabel}" Background="#FFF" Grid.Row="1" Grid.Column="1">
+                <StackPanel Orientation="Horizontal">
+                    <TextBlock Style="{StaticResource baseText}" Text="{DynamicResource Ts.Total}"/>
+                    <TextBlock Style="{StaticResource baseText}" Margin="2,0,18,0" Text="{Binding TotalNum}"/>
+                    <TextBlock Style="{StaticResource baseText}" Text="{DynamicResource Ts.Pass}"/>
+                    <TextBlock Style="{StaticResource baseText}" Margin="2,0,18,0" Text="{Binding PassNum}"/>
+                    <TextBlock Style="{StaticResource baseText}" Text="{DynamicResource Ts.Fail}"/>
+                    <TextBlock Style="{StaticResource baseText}" Margin="2,0,18,0" Text="{Binding FailNum}"/>
+                    <TextBlock Style="{StaticResource baseText}" Text="{DynamicResource Ts.PassRate}"/>
+                    <TextBlock Style="{StaticResource baseText}" Margin="2,0,18,0" Text="{Binding PassRate,StringFormat={}{0:P2}}"/>
+                    <TextBlock Style="{StaticResource baseText}" Text="{DynamicResource Ts.ProbeCount}"/>
+                    <TextBlock Style="{StaticResource baseText}" Margin="2,0,18,0" Text="{Binding ProbeCount}"/>
+                </StackPanel>
+            </Label>
+
+            <Label Style="{StaticResource baseLabel}" Background="#FFF" Grid.Row="2" Grid.Column="1">
+                <StackPanel Orientation="Horizontal">
+                    <Label Style="{StaticResource baseLabel}" Content="{DynamicResource Ts.CurrentSnr}"/>
+                    <Label Style="{StaticResource baseLabel}" Margin="2,0,18,0" Content="{Binding Barcode}"/>
+                    <Label Style="{StaticResource baseLabel}" Content="{DynamicResource Ts.TimeSpan}"/>
+                    <Label Style="{StaticResource baseLabel}" Margin="2,0,18,0" Content="{Binding ElapseTime}"/>
+                </StackPanel>
+            </Label>
+        </Grid>
+
+        <Grid Grid.Row="2" x:Name="simplePanel" dp:GridHelper.Space="5" dp:GridHelper.SpaceIgnore="TOP">
+            <Grid.RowDefinitions>
+                <RowDefinition/>
+                <RowDefinition Height="Auto"/>
+            </Grid.RowDefinitions>
+            <Label VerticalContentAlignment="Center"
+                       HorizontalContentAlignment="Center"
+                       FontSize="{Binding ActualWidth,ElementName=control,
+Converter={StaticResource fc}, ConverterParameter='50,816'}">
+                <Label.Style>
+                    <Style TargetType="Label">
+                        <Setter Property="Foreground" Value="#848286"/>
+                        <Setter Property="Content" Value="{Binding CurrentStep}"/>
+                        <Style.Triggers>
+                            <DataTrigger Binding="{Binding Status}" Value="Passed">
+                                <Setter Property="Background" Value="{StaticResource passedColor}"/>
+                                <Setter Property="Content" Value="--PASS--"/>
+                                <Setter Property="Foreground" Value="#000"/>
+                            </DataTrigger>
+                            <DataTrigger Binding="{Binding Status}" Value="Failed">
+                                <Setter Property="Background" Value="{StaticResource DangerColor}"/>
+                                <Setter Property="Content" Value="--FAIL--"/>
+                                <Setter Property="Foreground" Value="#000"/>
                             </DataTrigger>
                         </Style.Triggers>
                     </Style>
                 </Label.Style>
             </Label>
-
-            <Label HorizontalContentAlignment="Left" Grid.Column="1" Content="{Binding Status}"/>
-
-            <Label Style="{StaticResource baseLabel}" Grid.Row="1" Grid.Column="1">
-                <StackPanel Orientation="Horizontal">
-                    <TextBlock Style="{StaticResource baseText}" Text="{DynamicResource Ts.Total}"/>
-                    <TextBlock Style="{StaticResource baseText}" Margin="5,0,20,0" Text="{Binding TotalNum}"/>
-                    <TextBlock Style="{StaticResource baseText}" Text="{DynamicResource Ts.Pass}"/>
-                    <TextBlock Style="{StaticResource baseText}" Margin="5,0,20,0" Text="{Binding PassNum}"/>
-                    <TextBlock Style="{StaticResource baseText}" Text="{DynamicResource Ts.Fail}"/>
-                    <TextBlock Style="{StaticResource baseText}" Margin="5,0,20,0" Text="{Binding FailNum}"/>
-                    <TextBlock Style="{StaticResource baseText}" Text="{DynamicResource Ts.PassRate}"/>
-                    <TextBlock Style="{StaticResource baseText}" Margin="5,0,20,0" Text="{Binding PassRate,StringFormat={}{0:P2}}"/>
-                </StackPanel>
-            </Label>
-
-            <Label Style="{StaticResource baseLabel}" Grid.Row="2" Grid.Column="1">
-                <StackPanel Orientation="Horizontal">
-                    <Label Style="{StaticResource baseLabel}" Content="{DynamicResource Ts.CurrentSnr}"/>
-                    <Label Style="{StaticResource baseLabel}" Margin="5,0,20,0" Content="{Binding Barcode}"/>
-                    <Label Style="{StaticResource baseLabel}" Content="{DynamicResource Ts.TimeSpan}"/>
-                    <Label Style="{StaticResource baseLabel}" Margin="5,0,20,0" Content="{Binding ElapseTime}"/>
-                </StackPanel>
-            </Label>
+            <ProgressBar Grid.Row="1" MinHeight="20" Value="{Binding ProcessValue}"
+                         Height="{Binding ActualHeight,ElementName=control,
+Converter={StaticResource hfc}, ConverterParameter='30,816'}"/>
+            <Label Grid.Row="1" 
+                   VerticalContentAlignment="Center"
+                   HorizontalContentAlignment="Center"
+                   Content="{Binding ProcessValueLabel}" Background="Transparent" Foreground="#000"/>
         </Grid>
 
-        <DataGrid Grid.Row="2" Style="{StaticResource DefaultDataGrid}" 
-                  pu:DataGridHelper.ColumnHeaderFontSize="12"
+        <Grid Grid.Row="2" x:Name="detailPanel">
+            <Grid.RowDefinitions>
+                <RowDefinition/>
+                <RowDefinition Height="Auto"/>
+                <RowDefinition Height="Auto"/>
+            </Grid.RowDefinitions>
+
+            <DataGrid Grid.Row="0" Style="{StaticResource DefaultDataGrid}" 
+                      Margin="5,0,5,5"
+                  pu:DataGridHelper.ColumnHeaderFontSize="{Binding ActualWidth,
+            ElementName=control,
+Converter={StaticResource fc}, ConverterParameter='15,1093,15,22'}"
+            FontSize="{Binding ActualWidth,
+            ElementName=control,
+Converter={StaticResource fc}, ConverterParameter='15,1093,15,20'}"
                   pu:DataGridHelper.ColumnHeaderPadding="3"
                   dp:DataGridHelper.ShowRowNumber="True"
                   VirtualizingPanel.IsContainerVirtualizable="True"
                   VirtualizingPanel.IsVirtualizing="True" 
                   VirtualizingPanel.VirtualizationMode="Recycling"
-                  RowHeight="20"
-                  FontSize="12"
-                  ColumnHeaderHeight="30"
+                  RowHeight="{Binding ActualWidth,ElementName=control, 
+Converter={StaticResource fc}, ConverterParameter='35,1093,-1,45'}"
+                  ColumnHeaderHeight="{Binding ActualWidth,ElementName=control,
+Converter={StaticResource fc}, ConverterParameter='30,1093,-1,45'}"
                   ItemsSource="{Binding Datasource}"
                   x:Name="table">
-            <DataGrid.Resources>
-                <Style TargetType="DataGridCell" 
+                <DataGrid.Resources>
+                    <Style TargetType="DataGridCell" 
        BasedOn="{StaticResource {x:Static pu:StyleKeys.DataGridCellStyle}}">
-                    <Setter Property="Background" Value="Transparent"/>
-                    <Setter Property="Padding" Value="2"/>
-                    <Setter Property="BorderThickness" Value="0"/>
-                    <Setter Property="Background" Value="Transparent"/>
-                </Style>
+                        <Setter Property="Background" Value="Transparent"/>
+                        <Setter Property="Padding" Value="2"/>
+                        <Setter Property="BorderThickness" Value="0"/>
+                        <Setter Property="Background" Value="Transparent"/>
+                    </Style>
 
-                <Style TargetType="DataGridColumnHeader" 
+                    <Style TargetType="DataGridColumnHeader" 
 BasedOn="{StaticResource {x:Static pu:StyleKeys.DataGridColumnHeaderStyle}}">
-                    <Setter Property="Padding" Value="5"/>
-                    <Setter Property="BorderThickness" Value="0"/>
-                </Style>
-            </DataGrid.Resources>
+                        <Setter Property="Padding" Value="5"/>
+                        <Setter Property="BorderThickness" Value="0"/>
+                    </Style>
+                </DataGrid.Resources>
 
-            <DataGrid.Columns>
-                <!--<DataGridTemplateColumn Header="#" CanUserResize="False">
+                <DataGrid.Columns>
+                    <!--<DataGridTemplateColumn Header="#" CanUserResize="False">
                     <DataGridTemplateColumn.CellTemplate>
                         <DataTemplate>
                             <TextBlock Text="{Binding Converter={StaticResource rowNumber},
@@ -143,90 +239,147 @@
                     </DataGridTemplateColumn.CellTemplate>
                 </DataGridTemplateColumn>-->
 
-                <DataGridTextColumn Header="#"
-                                    Binding="{Binding Header, RelativeSource={RelativeSource AncestorType={x:Type DataGridRow}, Mode=FindAncestor}}" 
-                                    Width="40"/>
-                <DataGridTextColumn Width="*" Binding="{Binding Name}">
-                    <DataGridTextColumn.Header>
-                        <TextBlock Text="{DynamicResource Test.Name}"/>
-                    </DataGridTextColumn.Header>
-                </DataGridTextColumn>
-                <DataGridTextColumn Width="*" Binding="{Binding Unit}">
-                    <DataGridTextColumn.Header>
-                        <TextBlock Text="{DynamicResource Test.Unit}"/>
-                    </DataGridTextColumn.Header>
-                </DataGridTextColumn>
-                <DataGridTextColumn Width="*" Binding="{Binding Value}">
-                    <DataGridTextColumn.Header>
-                        <TextBlock Text="{DynamicResource Test.Value}"/>
-                    </DataGridTextColumn.Header>
-                </DataGridTextColumn>
-                <DataGridTextColumn Width="*" Binding="{Binding LimitDown}">
-                    <DataGridTextColumn.Header>
-                        <TextBlock Text="{DynamicResource Test.LimitDown}"/>
-                    </DataGridTextColumn.Header>
-                </DataGridTextColumn>
-                <DataGridTextColumn Width="*" Binding="{Binding LimitUp}">
-                    <DataGridTextColumn.Header>
-                        <TextBlock Text="{DynamicResource Test.LimitUp}"/>
-                    </DataGridTextColumn.Header>
-                </DataGridTextColumn>
-                <DataGridTemplateColumn Width="*">
-                    <DataGridTemplateColumn.Header>
-                        <TextBlock Text="{DynamicResource Test.Result}"/>
-                    </DataGridTemplateColumn.Header>
-                    <DataGridTemplateColumn.CellTemplate>
-                        <DataTemplate>
-                            <TextBlock x:Name="text" VerticalAlignment="Center" Text="{Binding Result}"/>
-                            <DataTemplate.Triggers>
-                                <DataTrigger Binding="{Binding Result}" Value="Passed">
-                                    <Setter TargetName="text" Property="Foreground" Value="Green"/>
-                                </DataTrigger>
-                                <DataTrigger Binding="{Binding Result}" Value="Failed">
-                                    <Setter TargetName="text" Property="Foreground" Value="Red"/>
-                                </DataTrigger>
-                            </DataTemplate.Triggers>
-                        </DataTemplate>
-                    </DataGridTemplateColumn.CellTemplate>
-                </DataGridTemplateColumn>
-            </DataGrid.Columns>
-        </DataGrid>
-        <Grid Grid.Row="3" Height="20" Margin="0">
-            <ProgressBar Grid.Row="3" x:Name="testProcess" />
-            <Label Width="45" DockPanel.Dock="Right" x:Name="testProcessLabel" Padding="0" 
+                    <DataGridTextColumn Header="#" 
+                                        ElementStyle="{StaticResource TableTextBlock}"
+                        Binding="{Binding Header, RelativeSource={RelativeSource AncestorType={x:Type DataGridRow}, Mode=FindAncestor}}" 
+                                    Width="60"/>
+                    <DataGridTextColumn Width="*" Binding="{Binding Name}" 
+                                        ElementStyle="{StaticResource TableTextBlock}">
+                        <DataGridTextColumn.Header>
+                            <TextBlock Text="{DynamicResource Test.Name}"/>
+                        </DataGridTextColumn.Header>
+                    </DataGridTextColumn>
+                    <DataGridTextColumn Width="*" Binding="{Binding Unit}" 
+                                        ElementStyle="{StaticResource TableTextBlock}">
+                        <DataGridTextColumn.Header>
+                            <TextBlock Text="{DynamicResource Test.Unit}"/>
+                        </DataGridTextColumn.Header>
+                    </DataGridTextColumn>
+                    <DataGridTextColumn Width="*" Binding="{Binding Value}" 
+                                        ElementStyle="{StaticResource TableTextBlock}">
+                        <DataGridTextColumn.Header>
+                            <TextBlock Text="{DynamicResource Test.Value}"/>
+                        </DataGridTextColumn.Header>
+                    </DataGridTextColumn>
+                    <DataGridTextColumn Width="*" Binding="{Binding LimitDown}" 
+                                        ElementStyle="{StaticResource TableTextBlock}">
+                        <DataGridTextColumn.Header>
+                            <TextBlock Text="{DynamicResource Test.LimitDown}"/>
+                        </DataGridTextColumn.Header>
+                    </DataGridTextColumn>
+                    <DataGridTextColumn Width="*" Binding="{Binding LimitUp}" 
+                                        ElementStyle="{StaticResource TableTextBlock}">
+                        <DataGridTextColumn.Header>
+                            <TextBlock Text="{DynamicResource Test.LimitUp}"/>
+                        </DataGridTextColumn.Header>
+                    </DataGridTextColumn>
+                    <DataGridTemplateColumn Width="*">
+                        <DataGridTemplateColumn.Header>
+                            <TextBlock Text="{DynamicResource Test.Result}"/>
+                        </DataGridTemplateColumn.Header>
+                        <DataGridTemplateColumn.CellTemplate>
+                            <DataTemplate>
+                                <Grid x:Name="border" Margin="0">
+                                    <TextBlock x:Name="text" Margin="-1" Padding="0" VerticalAlignment="Center" Text="{Binding Result}"/>
+                                </Grid>
+                                <DataTemplate.Triggers>
+                                    <DataTrigger Binding="{Binding Result}" Value="Passed">
+                                        <Setter TargetName="border" Property="Background" Value="{StaticResource passedColor}"/>
+                                        <Setter TargetName="text" Property="Foreground" Value="#000"/>
+                                    </DataTrigger>
+                                    <DataTrigger Binding="{Binding Result}" Value="Failed">
+                                        <Setter TargetName="border" Property="Background" Value="{StaticResource DangerColor}"/>
+                                        <Setter TargetName="text" Property="Foreground" Value="#000"/>
+                                    </DataTrigger>
+                                </DataTemplate.Triggers>
+                            </DataTemplate>
+                        </DataGridTemplateColumn.CellTemplate>
+                    </DataGridTemplateColumn>
+                </DataGrid.Columns>
+            </DataGrid>
+
+            <Grid Grid.Row="1" Height="20" Margin="0">
+                <ProgressBar Grid.Row="3" x:Name="testProcess" Value="{Binding ProcessValue}"/>
+                <Label Width="45" DockPanel.Dock="Right" x:Name="testProcessLabel" Padding="0" 
+                       Content="{Binding ProcessValueLabel}"
                    VerticalContentAlignment="Center"/>
-        </Grid>
+            </Grid>
 
-        <Grid Background="#f1eded" Grid.Row="4" Height="25">
-            <DockPanel>
-                <Label Padding="0" VerticalContentAlignment="Center" Content="{Binding CurrentStep}"/>
+            <Grid Background="#f1eded" Grid.Row="2" Height="25">
+                <DockPanel>
+                    <Label Padding="0" FontSize="12" VerticalContentAlignment="Center" Content="{Binding CurrentStep}"/>
 
-                <StackPanel DockPanel.Dock="Right" Orientation="Horizontal" Margin="0,0,5,0">
-                    <Label Padding="0" FontSize="12" 
+                    <StackPanel DockPanel.Dock="Right" Orientation="Horizontal" Margin="0,0,5,0">
+                        <Label Padding="0" FontSize="12" 
 VerticalAlignment="Center"
 VerticalContentAlignment="Center" Content="{DynamicResource Ts.ProbeCount}"/>
-                    <Label Padding="0" FontSize="12"  Margin="0,0,15,0"
+                        <Label Padding="0" FontSize="12"  Margin="0,0,15,0"
 VerticalAlignment="Center"
 VerticalContentAlignment="Center" Content="{Binding ProbeCount}"/>
 
-                    <StackPanel x:Name="probeWarnPanel" Orientation="Horizontal" Visibility="Collapsed">
-                        <Label x:Name="alarmLabel" Padding="0" FontSize="12" 
+                        <StackPanel x:Name="probeWarnPanel" Orientation="Horizontal" Visibility="Collapsed">
+                            <Label x:Name="alarmLabel" Padding="0" FontSize="12" 
 VerticalAlignment="Center"
 VerticalContentAlignment="Center" Content="{DynamicResource Ts.ProbeAlarmCount}"/>
-                        <Label x:Name="alarmValue" Padding="0" FontSize="12" 
+                            <Label x:Name="alarmValue" Padding="0" FontSize="12" 
 VerticalAlignment="Center"
 VerticalContentAlignment="Center" Content="{Binding ProbeWarn}"/>
+                        </StackPanel>
+
+                        <Button FontSize="12" Style="{StaticResource LinkButton}" x:Name="clear" Margin="10,0,0,0" Click="Button_Click" 
+                            Content="{DynamicResource App.Clear}"/>
                     </StackPanel>
 
-                    <Button Style="{StaticResource LinkButton}" x:Name="clear" Margin="10,0,0,0" Click="Button_Click" 
-                            Content="{DynamicResource App.Clear}"/>
-                </StackPanel>
-
-                <Label x:Name="probeCountAlarm" Visibility="Collapsed" DockPanel.Dock="Left" Padding="30,0" FontSize="12" Foreground="Red"
+                    <Label x:Name="probeCountAlarm" Visibility="Collapsed" DockPanel.Dock="Left" Padding="30,0" FontSize="12" Foreground="Red"
 VerticalAlignment="Center"
 VerticalContentAlignment="Center" Content="{DynamicResource Ts.ProbeAlarm}"/>
-            </DockPanel>
+                </DockPanel>
+            </Grid>
         </Grid>
+
+        <Canvas Grid.Row="0" Grid.RowSpan="5" Visibility="Collapsed"
+                Background="#AAFFFFFF" x:Name="pausePanel" SizeChanged="pausePanel_Loaded">
+            <Border BorderBrush="#000" Background="#FFF" BorderThickness="0.5"
+                    CornerRadius="5"
+                    x:Name="visualModal">
+                <Grid>
+                    <Grid.RowDefinitions>
+                        <RowDefinition Height="Auto"/>
+                        <RowDefinition Height="Auto"/>
+                        <RowDefinition Height="Auto"/>
+                    </Grid.RowDefinitions>
+                    <Grid.ColumnDefinitions>
+                        <ColumnDefinition Width="Auto"/>
+                        <ColumnDefinition />
+                    </Grid.ColumnDefinitions>
+                    <Border Padding="10,5,0,5" Grid.ColumnSpan="2" Background="{StaticResource WarningColor}"
+                           CornerRadius="5,5,0,0">
+                        <Label Padding="0" Margin="0" Foreground="#FFF" Content="{DynamicResource App.Warn}"/>
+                    </Border>
+
+                    <TextBlock Margin="20,20,20,0" Grid.Row="1" 
+                               FontSize="40"
+                               FontFamily="/Panuon.WPF.UI;component/Resources/Fonts/#PanuonIcon"
+           Foreground="#F9D01A"
+           Text="&#xe931;"
+           />
+
+                    <Label Margin="0,20,20,0" Grid.Row="1" Grid.Column="1" Content="{DynamicResource Ts.PauseMsg}"/>
+                    <StackPanel Margin="10" Grid.Row="2" Grid.ColumnSpan="2" Orientation="Horizontal" HorizontalAlignment="Right">
+                        <Button Style="{StaticResource ModalButton}" Tag="Cancel"
+                                x:Name="cancelPauseBtn"
+                                Click="Button_Click" Content="{DynamicResource App.Cancel}"/>
+                        <Button Style="{StaticResource ModalButton}" 
+                                x:Name="continuePauseBtn"
+                                Click="Button_Click"
+                                Background="{StaticResource WarningColor}"
+                                Tag="OK" Content="{DynamicResource App.Continue}"/>
+                    </StackPanel>
+                </Grid>
+            </Border>
+        </Canvas>
+
+
         <!--<DockPanel Grid.Row="4">
             <StatusBar>
                 <StatusBarItem>
diff --git a/Plugin.TsExecution/SingleControl.xaml.cs b/Plugin.TsExecution/SingleControl.xaml.cs
index 8d2e1e0..8b0cfff 100644
--- a/Plugin.TsExecution/SingleControl.xaml.cs
+++ b/Plugin.TsExecution/SingleControl.xaml.cs
@@ -157,6 +157,45 @@
             }
         }
 
+        /// <summary>
+        /// 杩涘害鍊�
+        /// </summary>
+        private int _processValue;
+        public int ProcessValue
+        {
+            get => _processValue;
+            set
+            {
+                Set(ref _processValue, value);
+            }
+        }
+
+        public string ProcessValueLabel
+        {
+            get
+            {
+                if (ProcessValue > 0) return $"{ProcessValue}%";
+                return "";
+            }
+            set
+            {
+            }
+        }
+
+        public void SetSimpleUI(bool flag)
+        {
+            if (flag)
+            {
+                simplePanel.Visibility = Visibility.Visible;
+                detailPanel.Visibility = Visibility.Collapsed;
+            }
+            else
+            {
+                simplePanel.Visibility = Visibility.Collapsed;
+                detailPanel.Visibility = Visibility.Visible;
+            }
+        }
+
         public int ProbeWarn { get; private set; }
 
         public SingleControl(int Index, string CountIniPath, int probeWarn)
@@ -275,8 +314,8 @@
         /// <param name="ProcessVal"></param>
         public void SetTestProcess(int ProcessVal)
         {
-            this.testProcess.Value = ProcessVal;
-            this.testProcessLabel.Content = ProcessVal.ToString() + "%";
+            ProcessValue = ProcessVal;
+            Set("ProcessValueLabel");
         }
 
         private void UserControl_Loaded(object sender, RoutedEventArgs e)
@@ -362,8 +401,8 @@
 
         public void ResetCounter()
         {
-            this.passNum = 0;
-            this.failNum = 0;
+            this.PassNum = 0;
+            this.FailNum = 0;
             this.UpdateTestCounter(null);
         }
 
@@ -399,6 +438,11 @@
             }
         }
 
+        public void ShowPausePanel()
+        {
+            pausePanel.Visibility = Visibility.Visible;
+        }
+
         private void Button_Click(object sender, RoutedEventArgs e)
         {
             Button btn = sender as Button;
@@ -407,9 +451,25 @@
                 case "clear":
                     UpdateProbeCount(0);
                     break;
+                case "cancelPauseBtn":
+                    SequenceFileContext?.StationGlobals.SetValBoolean($"StationState.IsContinue_{index}", 1, false);
+                    pausePanel.Visibility = Visibility.Collapsed;
+                    break;
+                case "continuePauseBtn":
+                    SequenceFileContext?.StationGlobals.SetValBoolean($"StationState.IsContinue_{index}", 1, true);
+                    pausePanel.Visibility = Visibility.Collapsed;
+                    break;
                 default:
                     break;
             }
         }
+
+        private void pausePanel_Loaded(object sender, RoutedEventArgs e)
+        {
+            var can = pausePanel;
+            var modal = visualModal;
+            Canvas.SetLeft(modal, (pausePanel.ActualWidth - visualModal.ActualWidth) / 2);
+            Canvas.SetTop(modal, (pausePanel.ActualHeight - visualModal.ActualHeight) / 2);
+        }
     }
 }
diff --git a/Plugin.TsExecution/TsExecution.xaml b/Plugin.TsExecution/TsExecution.xaml
index c6373ff..e2dbdfe 100644
--- a/Plugin.TsExecution/TsExecution.xaml
+++ b/Plugin.TsExecution/TsExecution.xaml
@@ -13,32 +13,116 @@
          mc:Ignorable="d" 
          Padding="10"
          Loaded="PluginControl_Loaded"
-                  SizeChanged="PluginControl_SizeChanged"
+         SizeChanged="PluginControl_SizeChanged"
          Background="{StaticResource WorkspaceBackground}"
          d:DesignHeight="450" d:DesignWidth="800">
     <Grid>
         <Grid.ColumnDefinitions>
-            <ColumnDefinition Width="8*"/>
-            <ColumnDefinition Width="2*"/>
+            <ColumnDefinition />
+            <ColumnDefinition Width="Auto"/>
         </Grid.ColumnDefinitions>
+
+        <Grid.RowDefinitions>
+            <RowDefinition Height="Auto"/>
+            <RowDefinition/>
+        </Grid.RowDefinitions>
         <WindowsFormsHost x:Name="host" Visibility="Collapsed"></WindowsFormsHost>
 
-        <Grid>
+        <DockPanel Grid.Row="0" Grid.ColumnSpan="2" LastChildFill="False">
+            <DockPanel.Resources>
+                <Style TargetType="CheckBox">
+                    <Setter Property="FontSize" Value="20"/>
+                    <Setter Property="VerticalContentAlignment" Value="Center"/>
+                </Style>
+            </DockPanel.Resources>
+
+            <StackPanel Orientation="Horizontal" VerticalAlignment="Center" dp:StackPanelHelper.ChildSpace="20">
+                <pu:FormGroup HeaderHeight="Auto" HeaderPadding="0">
+                    <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"
+SelectedItem="{Binding SelectedProduct}" x:Name="CbProduct"/>
+                </pu:FormGroup>
+
+                <CheckBox Grid.Column="1"
+        IsChecked="{Binding EnableMes,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
+        Content="{DynamicResource Ts.EnableMes}"
+        />
+                <CheckBox Grid.Column="2" 
+              IsChecked="{Binding EnableNGPause,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
+              Content="{DynamicResource Ts.EnableNGPause}"
+              />
+                <CheckBox Grid.Column="3" 
+              IsChecked="{Binding EnableDebugMode,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
+              Content="{DynamicResource Ts.DebugMode}"/>
+                <CheckBox Grid.Column="4" 
+IsChecked="{Binding EnableSimpleMode,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
+Content="{DynamicResource Ts.EnableSimpleMode}"/>
+
+                <CheckBox Grid.Column="4" 
+IsChecked="{Binding UI_ShowInfoDock,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
+Content="{DynamicResource Ts.UI_ShowInfoDock}"/>
+
+                <Label FontSize="20" VerticalContentAlignment="Center" HorizontalContentAlignment="Center" Padding="0"
+                       Content="{DynamicResource HMI.User.Code}"
+                       />
+                <Label FontSize="20" VerticalContentAlignment="Center" HorizontalContentAlignment="Center" Padding="0" 
+                       Content="{Binding CurrentUserCode}" />
+            </StackPanel>
+
+
+
+            <StackPanel DockPanel.Dock="Right" Orientation="Horizontal" dp:StackPanelHelper.ChildSpace="20">
+                <Button Style="{StaticResource SuccessButton}" 
+x:Name="BtnStartSeq" Click="Button_Click" 
+Content="{DynamicResource Ts.BtnStartSeq}"/>
+                <Button Grid.Row="1" Style="{StaticResource DangerButton}" IsEnabled="False"
+x:Name="BtnStopSeq" Click="Button_Click"
+Content="{DynamicResource Ts.BtnStopSeq}"/>
+                <Button Grid.Row="2" Style="{StaticResource PrimaryButton}" 
+x:Name="BtnClearNum" Click="Button_Click"
+Content="{DynamicResource Ts.BtnClearNum}"/>
+                <Button Grid.Row="3"
+x:Name="BtnSample" Click="Button_Click">
+                    <Button.Style>
+                        <Style TargetType="Button" BasedOn="{StaticResource PrimaryButton}">
+                            <Style.Triggers>
+                                <DataTrigger Binding="{Binding SampleStatus}" Value="True">
+                                    <Setter Property="Content" Value="{DynamicResource Ts.BtnIsSample}"/>
+                                </DataTrigger>
+                                <DataTrigger Binding="{Binding SampleStatus}" Value="False">
+                                    <Setter Property="Content" Value="{DynamicResource Ts.BtnNotSample}"/>
+                                </DataTrigger>
+                            </Style.Triggers>
+                        </Style>
+                    </Button.Style>
+                </Button>
+            </StackPanel>
+        </DockPanel>
+
+        <Grid dp:GridHelper.Space="5" Grid.Row="1">
             <Grid.RowDefinitions>
+                <RowDefinition Height="Auto"/>
                 <RowDefinition/>
                 <RowDefinition Height="Auto"/>
             </Grid.RowDefinitions>
-            <UniformGrid x:Name="panel" Columns="2" dp:UniformGridHelper.InnerBorder="1" 
+
+
+            <UniformGrid x:Name="panel" Grid.Row="1" Columns="2" dp:UniformGridHelper.InnerBorder="1"
                          dp:UniformGridHelper.InnerBorderBrush="#3a3a3a">
 
+
             </UniformGrid>
-            <Border Grid.Row="1" Visibility="{Binding AlarmVisiable,Converter={StaticResource vbConverter}}" 
-                     Height="50" Background="Red"/>
+            <Border x:Name="alarmPanel" VerticalAlignment="Bottom" Visibility="Visible" Background="Red"/>
+            <!--<Border Grid.Row="1" x:Name="alarmPanel" Visibility="{Binding AlarmVisiable,Converter={StaticResource vbConverter}}" 
+                     Height="50" Background="Red"/>-->
         </Grid>
 
 
 
-        <Grid Grid.Column="1">
+        <Grid Grid.Row="1" Grid.Column="1" x:Name="detailPanel">
             <Grid.RowDefinitions>
                 <RowDefinition Height="Auto"/>
                 <RowDefinition Height="Auto"/>
@@ -47,7 +131,7 @@
                 <RowDefinition Height="Auto"/>
             </Grid.RowDefinitions>
 
-            <GroupBox>
+            <!--<GroupBox>
                 <pu:FormGroup Orientation="Vertical" 
                               HeaderHeight="Auto"
                               HeaderPadding="0" Header="{DynamicResource Ts.SelectProductModel}">
@@ -57,7 +141,7 @@
           SelectedItem="{Binding SelectedProduct}"
           x:Name="CbProduct"/>
                 </pu:FormGroup>
-            </GroupBox>
+            </GroupBox>-->
 
             <GroupBox Grid.Row="2">
                 <StackPanel>
@@ -109,7 +193,7 @@
                 </ScrollViewer>
             </GroupBox>
 
-            <GroupBox Grid.Row="4">
+            <!-- <GroupBox Grid.Row="4">
                 <Grid dp:GridHelper.Space="20">
                     <Grid.RowDefinitions>
                         <RowDefinition/>
@@ -143,27 +227,7 @@
                         </Button.Style>
                     </Button>
 
-                    <!--<Grid Grid.Row="4" dp:GridHelper.Space="20" dp:GridHelper.SpaceIgnore="ALL">
-                        <Grid.ColumnDefinitions>
-                            <ColumnDefinition/>
-                            <ColumnDefinition/>
-                            <ColumnDefinition/>
-                        </Grid.ColumnDefinitions>
-
-                        <CheckBox 
-                            IsChecked="{Binding EnableMes,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
-                            Content="{DynamicResource Ts.EnableMes}"
-                            />
-                        <CheckBox Grid.Column="1" 
-                                  IsChecked="{Binding EnableCanLog,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
-                                  Content="{DynamicResource Ts.EnableCanLog}"
-                                  />
-                        <CheckBox Grid.Column="2" 
-                                  IsChecked="{Binding EnableDebugMode,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
-                                  Content="{DynamicResource Ts.DebugMode}"/>
-                    </Grid>-->
-
-                    <!--<pu:Switch Grid.Row="3" BoxWidth="120"
+                    <pu:Switch Grid.Row="3" BoxWidth="120"
     CheckedBackground="{StaticResource PrimaryColor}"
     CheckedBorderBrush="#36abff"
     ToggleBrush="#030303"
@@ -188,9 +252,9 @@
                                 </DataTemplate.Triggers>
                             </DataTemplate>
                         </pu:Switch.BoxTemplate>
-                    </pu:Switch>-->
+                    </pu:Switch>
                 </Grid>
-            </GroupBox>
+        </GroupBox> -->
         </Grid>
 
     </Grid>
diff --git a/Plugin.TsExecution/TsExecution.xaml.cs b/Plugin.TsExecution/TsExecution.xaml.cs
index 846c349..fb1f427 100644
--- a/Plugin.TsExecution/TsExecution.xaml.cs
+++ b/Plugin.TsExecution/TsExecution.xaml.cs
@@ -3,7 +3,6 @@
 using HMI.Core.Entity;
 using HMI.Core.Manager;
 using HMI.Core.Util;
-using HMI.ParameterManage;
 using HMI.UI;
 using HMI.UI.Util;
 using HMI.Util;
@@ -19,11 +18,12 @@
 using System.ComponentModel;
 using System.IO;
 using System.Linq;
+using System.Reflection;
 using System.Timers;
 using System.Windows;
 using System.Windows.Controls;
 using System.Windows.Forms.DataVisualization.Charting;
-using System.Windows.Markup;
+using System.Xml.Linq;
 
 namespace Plugin.TsExecution
 {
@@ -32,7 +32,6 @@
     /// </summary>
     public partial class TsExecution : PluginControl
     {
-        //private OpcInterface.OpcInterface OpcHandle = new OpcInterface.OpcInterface();
         #region TestStand浜嬩欢
         /// <summary>
         /// TS 寮曟搸
@@ -70,6 +69,15 @@
                 SampleTest(value);
             }
         }
+
+        private string _currentUserCode;
+
+        public string CurrentUserCode
+        {
+            get { return _currentUserCode; }
+            set { Set(ref _currentUserCode, value); }
+        }
+
 
         private bool isLoadOK = false;
         private Timer GCTimer;
@@ -130,8 +138,10 @@
             get => _enableMes;
             set
             {
-                Set(ref _enableMes, value);
-                WriteConfig("EnableMes", value);
+                if (WriteConfig("EnableMes", value))
+                {
+                    Set(ref _enableMes, value);
+                }
             }
         }
 
@@ -141,8 +151,10 @@
             get => _enableCanLog;
             set
             {
-                Set(ref _enableCanLog, value);
-                WriteConfig("EnableCanLog", value);
+                if (WriteConfig("EnableCanLog", value))
+                {
+                    Set(ref _enableCanLog, value);
+                }
             }
         }
 
@@ -152,8 +164,51 @@
             get => _enableDebugMode;
             set
             {
-                Set(ref _enableDebugMode, value);
-                WriteConfig("EnableDebugMode", value);
+                if (WriteConfig("EnableDebugMode", value))
+                {
+                    Set(ref _enableDebugMode, value);
+                }
+            }
+        }
+
+        private bool _enableNGPause;
+        public bool EnableNGPause
+        {
+            get => _enableNGPause;
+            set
+            {
+                if (WriteConfig("EnableNGPause", value))
+                {
+                    Set(ref _enableNGPause, value);
+                }
+            }
+        }
+
+        private bool _enableSimpleMode;
+        public bool EnableSimpleMode
+        {
+            get => _enableSimpleMode;
+            set
+            {
+                if (WriteConfig("EnableSimpleMode", value))
+                {
+                    Set(ref _enableSimpleMode, value);
+                    foreach (var cell in tsSockets) cell.SetSimpleUI(value);
+                }
+            }
+        }
+
+        private bool _ui_showInfoDock;
+        public bool UI_ShowInfoDock
+        {
+            get => _ui_showInfoDock;
+            set
+            {
+                if (WriteConfig("UI_ShowInfoDock", value))
+                {
+                    Set(ref _ui_showInfoDock, value);
+                    detailPanel.Visibility = value ? Visibility.Visible : Visibility.Collapsed;
+                }
             }
         }
 
@@ -195,16 +250,16 @@
             var legend = pieChart.Plot.ShowLegend(Alignment.UpperRight);
             legend.Alignment = Alignment.UpperRight;
             legend.Padding = new PixelPadding(0);
-            legend.FontSize = (float)CustomFontSizeConverter.CountFontSize(20, 300, 300);
+            legend.FontSize = (float)WidthSizeConverter.CountFontSize(20, 300, 300);
             pieChart.UserInputProcessor.Disable();
             pieChart.Plot.Font.Automatic();
 
             List<PieSlice> slices = new List<PieSlice> {
     new PieSlice() { Value = 5, FillColor = Colors.Green,
-        LabelFontSize = (float)CustomFontSizeConverter.CountFontSize(20,300,300),
+        LabelFontSize = (float)WidthSizeConverter.CountFontSize(20,300,300),
         Label = "Pass",LegendText="Pass" },
     new PieSlice() { Value = 2, FillColor = Colors.Red,
-        LabelFontSize = (float)CustomFontSizeConverter.CountFontSize(20,300,300),
+        LabelFontSize = (float)WidthSizeConverter.CountFontSize(20,300,300),
         Label = "Fail",LegendText="Fail" } };
 
 
@@ -223,10 +278,39 @@
                 {
                     this.socketNum = Convert.ToInt32(tmpNum);
                 }
+                string lastModel = engine?.Globals.GetValString("StationState.ProductModel", 1);
+
+                if (!string.IsNullOrEmpty(lastModel))
+                {
+                    var product = Products.FirstOrDefault(p => p.Name == lastModel);
+                    if (product != null) SelectedProduct = product;
+                    goto Write;
+                }
+                goto Write;
+
+            Write:
                 SelectedProduct = Products[0];
+                engine?.Globals.SetValString("StationState.ProductModel", 1, Products[0].Name);
+
                 RefershUI();
             }
             SampleStatus = false;
+        }
+
+        private bool _isTesting = false;
+        public bool IsTesting
+        {
+            get { return _isTesting; }
+            set
+            {
+                Set(ref _isTesting, value);
+                Dispatcher.Invoke(() =>
+                {
+                    BtnStartSeq.IsEnabled = !value;
+                    BtnStopSeq.IsEnabled = value;
+                    CbProduct.IsEnabled = !value;
+                });
+            }
         }
 
         /// <summary>
@@ -250,39 +334,64 @@
             new MaskWindow((WindowX)Window.GetWindow(this),
                 (win) =>
                 {
+                    try
+                    {
+                        Dispatcher.Invoke(() =>
+                        {
+                            try
+                            {
+                                UserInfo user = UserInfo.User;
+                                CurrentUserCode = user?.Code;
+                            }
+                            catch (Exception ex)
+                            {
+                                log.Warn("Get Login User Failed.", ex);
+                            }
+                            ReadConfig();
+                        });
+                        isLoadOK = true;
+                        StartSequence();
+                    }
+                    catch (Exception ex)
+                    {
+                        log.Error("TsExecution Loaded Failed.", ex);
+                        MsgBoxHelper.Error(this, $"{LangUtil.Get("Ts.UserControlLoadFailed")}: {ex.Message}");
+                    }
+
+                    try
+                    {
+                        if (Environment.Version.ToString() != this.AxApplicationMgr.GetEngine().DotNetCLRVersion)
+                            this.GCTimer.Enabled = true;
+                    }
+                    catch (Exception ex)
+                    {
+                        log.Error("TsExecution GCTimer Start Failed", ex);
+                        MsgBoxHelper.Error(this, $"{LangUtil.Get("Ts.LoadFailed")}: {ex.Message}");
+                    }
+
                     Dispatcher.Invoke(() =>
                     {
-                        try
-                        {
-                            ReadConfig();
-                            //杩炴帴OPChandle
-                            //this.OpcHandle.Connect2("127.0.0.1", 6543, 18081, out string ErrMsg, null);
-                            //鍚姩娴嬭瘯杩涚▼
-                            StartSequence();
-                            isLoadOK = true;
-                        }
-                        catch (Exception ex)
-                        {
-                            log.Error("TsExecution Loaded Failed.", ex);
-                            MsgBoxHelper.Error(this, $"{LangUtil.Get("Ts.UserControlLoadFailed")}: {ex.Message}");
-                        }
-
-                        try
-                        {
-                            if (Environment.Version.ToString() != this.AxApplicationMgr.GetEngine().DotNetCLRVersion)
-                                this.GCTimer.Enabled = true;
-                        }
-                        catch (Exception ex)
-                        {
-                            log.Error("TsExecution GCTimer Start Failed", ex);
-                            MsgBoxHelper.Error(this, $"{LangUtil.Get("Ts.LoadFailed")}: {ex.Message}");
-                        }
-                        finally
-                        {
-                            win.Close();
-                        }
                         if (isLoadOK) Loaded -= PluginControl_Loaded;
                     });
+
+                    //Dispatcher.Invoke(() =>
+                    //{
+                    //    try
+                    //    {
+                    //        ReadConfig();
+                    //        //杩炴帴OPChandle
+                    //        //this.OpcHandle.Connect2("127.0.0.1", 6543, 18081, out string ErrMsg, null);
+                    //        isLoadOK = true;
+                    //        //鍚姩娴嬭瘯杩涚▼
+                    //        StartSequence();
+                    //    }
+                    //    catch (Exception ex)
+                    //    {
+                    //        log.Error("TsExecution Loaded Failed.", ex);
+                    //        MsgBoxHelper.Error(this, $"{LangUtil.Get("Ts.UserControlLoadFailed")}: {ex.Message}");
+                    //    }
+
+                    //});
                 }
                 ).Info(LangUtil.Get("App.UsercontrolIsLoading"));
         }
@@ -359,24 +468,114 @@
         /// <param name="fail"></param>
         private void ReadConfig()
         {
+            ParameterData value;
             try
             {
-                var p = ParameterManager.GetCommonParameterByName("EnableMes");
-                EnableMes = bool.Parse(p?.Value);
+                value = ParameterManager.GetCommonParameterByName("EnableMes");
+                _enableMes = bool.Parse(value.Value);
+                Set("EnableMes");
             }
             catch
             {
-                EnableMes = false;
+                _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;
+            }
+
             //Convert.ToBoolean(IniFileHelper.ReadIniData("Config", "EnableMes", "true", CountIniPath));
-            EnableCanLog = Convert.ToBoolean(IniFileHelper.ReadIniData("Config", "EnableCanLog", "true", CountIniPath));
-            EnableDebugMode = Convert.ToBoolean(IniFileHelper.ReadIniData("Config", "EnableDebugMode", "true", CountIniPath));
+            //EnableCanLog = Convert.ToBoolean(IniFileHelper.ReadIniData("Config", "EnableCanLog", "true", CountIniPath));
+            //EnableDebugMode = Convert.ToBoolean(IniFileHelper.ReadIniData("Config", "EnableDebugMode", "true", CountIniPath));
         }
 
-        private void WriteConfig(string name, bool value)
+        //private void WriteToTsWhenInit()
+        //{
+        //    foreach (var ts in tsSockets)
+        //    {
+        //        ts?.SequenceFileContext?.StationGlobals.SetValBoolean($"SystemSetting.EnableMes", 1, EnableMes);
+        //        ts?.SequenceFileContext?.StationGlobals.SetValBoolean($"SystemSetting.EnableNGPause", 1, EnableNGPause);
+        //        ts?.SequenceFileContext?.StationGlobals.SetValBoolean($"SystemSetting.EnableCanLog", 1, EnableCanLog);
+        //        ts?.SequenceFileContext?.StationGlobals.SetValBoolean($"SystemSetting.EnableDebugMode", 1, EnableDebugMode);
+        //    }
+        //}
+
+        private bool WriteConfig(string name, bool value)
         {
-            IniFileHelper.WriteIniData("Config", name, value.ToString(), CountIniPath);
-            //this.engine.Globals.SetValBoolean($"StationState.{name}", 1, value);
+            //IniFileHelper.WriteIniData("Config", name, value.ToString(), CountIniPath);
+            try
+            {
+                if (ParameterManager.UpdateCommonParameter(name, value, out string errMsg))
+                {
+                    foreach (var ts in tsSockets)
+                    {
+                        ts?.SequenceFileContext?.StationGlobals.SetValBoolean($"SystemSetting.{name}", 1, value);
+                    }
+                    return true;
+                }
+                else
+                {
+                    throw new Exception(errMsg);
+                }
+            }
+            catch (Exception ex)
+            {
+                MsgBoxHelper2.Error(ex.Message);
+                return false;
+            }
         }
 
         private void WriteCount(double pass, double fail)
@@ -444,18 +643,14 @@
         /// </summary>
         private void StartSequence()
         {
+            if (!isLoadOK) return;
             //MaskWindow mask = new MaskWindow();
             try
             {
                 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);
-                Dispatcher.Invoke(() =>
-                {
-                    this.CbProduct.IsEnabled = false;
-                    this.BtnStartSeq.IsEnabled = false;
-                    this.BtnStopSeq.IsEnabled = true;
-                });
+                IsTesting = true;
             }
             catch (Exception ex)
             {
@@ -481,12 +676,7 @@
                             bool stoped = StopSequence();
                             if (stoped)
                             {
-                                Dispatcher.Invoke(() =>
-                                {
-                                    this.CbProduct.IsEnabled = true;
-                                    this.BtnStartSeq.IsEnabled = true;
-                                    this.BtnStopSeq.IsEnabled = false;
-                                });
+                                IsTesting = false;
                             }
                         }
                         finally
@@ -523,7 +713,7 @@
                         //    context.StationGlobals.SetValBoolean("StationState.StopTest", 0, true);
                         //    bool isStop = this.execution.WaitForEndEx(2000, true, null, null);
                         //}
-                        context?.StationGlobals.SetValBoolean("StationState.StopTest", 0, true);
+                        context?.StationGlobals.SetValBoolean("StationState.StopTest", 1, true);
                     }
                 }
                 if (this.execution == null) return true;
@@ -564,17 +754,17 @@
             new MaskWindow(Window.GetWindow(this),
                 (win) =>
                 {
-                    Dispatcher.Invoke(() =>
+                    try
                     {
-                        try
-                        {
-                            StartSequence();
-                        }
-                        finally
+                        StartSequence();
+                    }
+                    finally
+                    {
+                        win.Dispatcher.Invoke(() =>
                         {
                             win.Close();
-                        }
-                    });
+                        });
+                    }
                 }).Info(LangUtil.Get("Ts.SequenceIsStarting"));
         }
 
@@ -594,7 +784,7 @@
         /// </summary>
         /// <param name="sender"></param>
         /// <param name="e"></param>
-        private void AxApplicationMgr_ExecutionClosed(object sender, NationalInstruments.TestStand.Interop.UI.Ax._ApplicationMgrEvents_ExecutionClosedEvent e)
+        private void AxApplicationMgr_ExecutionClosed(object sender, _ApplicationMgrEvents_ExecutionClosedEvent e)
         {
             try
             {
@@ -619,11 +809,26 @@
             this.UpdateTestCounter();
         }
 
+        public void UpdatePositionProcessValue(int index, int value)
+        {
+            this.tsSockets[index].SetTestProcess(value);
+        }
+
+        public void PausePosition(int index)
+        {
+            this.tsSockets[index].ShowPausePanel();
+        }
+
         public void AddTestData(int index, string data)
         {
             string[] TestItems = data.Split(',');
             tsSockets[index].AddTestData(TestItems[0], TestItems[1], TestItems[2], TestItems[3],
                 TestItems[4], TestItems[5], TestItems[6]);
+        }
+
+        public void SetPositionBarcode(int index, string barcode)
+        {
+            this.tsSockets[index].Barcode = barcode;
         }
 
         /// <summary>
@@ -637,6 +842,17 @@
             {
                 int EventCase = Convert.ToInt32(e.uiMsg.Event);
                 int Index = Convert.ToInt16(e.uiMsg.NumericData);//SocketIndex
+                string strData = e.uiMsg.StringData;
+
+                try
+                {
+                    log.Info($"Event: {EventCase}, NumericData: {Index}, StringData: {strData}");
+                }
+                catch
+                {
+
+                }
+
                 switch (EventCase)
                 {
                     case 10001://浼犻�扴ocketContext
@@ -661,7 +877,7 @@
                         break;
 
                     case 10006://鍙戦�佷骇鍝佹潯鐮�
-                        this.tsSockets[Index].Barcode = e.uiMsg.StringData;
+                        SetPositionBarcode(Index, e.uiMsg.StringData);
                         break;
 
                     case 10007://寮�濮嬫祴璇曡鏃�
@@ -715,8 +931,7 @@
                         this.tsSockets[Index].ElapseTime = e.uiMsg.StringData;
                         break;
                     case (int)UIMessageCodes.UIMsg_EndExecution://鎵ц缁撴潫
-                        this.BtnStartSeq.IsEnabled = true;
-                        this.BtnStopSeq.IsEnabled = true;
+                        IsTesting = false;
                         // this.ReleaseSequenceFile();
                         break;
                     case 10030://鏄剧ず娴嬭瘯杩涘害
@@ -725,6 +940,9 @@
                         break;
                     case 10031://瀵煎嚭CSV
                         this.tsSockets[Index].WriteToCSV(e.uiMsg.StringData);
+                        break;
+                    case 10032://鎸変笅鏍囨殏鍋滄祴璇�
+                        this.tsSockets[Index].ShowPausePanel();
                         break;
                     default:
                         break;
@@ -750,9 +968,15 @@
 
         private void SetTestSeqFile(ProductData product)
         {
+            if (product == null)
+            {
+                testSeqFile = null;
+                return;
+            }
             string name = product.Name;
             List<ParameterData> parameters = ProductManager.GetParametersByProductName(name);
             testSeqFile = parameters.FirstOrDefault(p => p.Name == "TestSequence")?.Value?.ToString();
+            StartSequence();
         }
 
         private void ClearNum()
@@ -772,13 +996,22 @@
 
         public override void OnClose(object sender, CancelEventArgs e)
         {
-            StopSequence();
-            this.engine?.ReleaseSequenceFileEx(this.testSeq, 0);
-            this.engine?.ReleaseSequenceFileEx(this.modeSeq, 0);
-            this.engine?.UnloadAllModules();
-            this.testSeq = null;
-            this.modeSeq = null;
-            base.OnClose(sender, e);
+            try
+            {
+                StopSequence();
+                // this.engine?.TerminateAll();
+                // this.engine?.ReleaseSequenceFileEx(this.testSeq, 0);
+                // this.engine?.ReleaseSequenceFileEx(this.modeSeq, 0);
+                // this.engine?.UnloadAllModules();
+                // this.engine?.ShutDown(true);
+                //this.testSeq = null;
+                //this.modeSeq = null;
+                base.OnClose(sender, e);
+            }
+            catch (Exception ex)
+            {
+                log.Error("OnClose Error.", ex);
+            }
         }
 
         private void PluginControl_SizeChanged(object sender, SizeChangedEventArgs e)
diff --git a/SimpleUtil/HMI.LogUtil/HMI.LogUtil.csproj b/SimpleUtil/HMI.LogUtil/HMI.LogUtil.csproj
new file mode 100644
index 0000000..7f76962
--- /dev/null
+++ b/SimpleUtil/HMI.LogUtil/HMI.LogUtil.csproj
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProjectGuid>{6D0AAB08-0F3E-48CE-B755-9D29BC8F1225}</ProjectGuid>
+    <OutputType>Library</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>HMI.LogUtil</RootNamespace>
+    <AssemblyName>HMI.LogUtil</AssemblyName>
+    <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
+    <FileAlignment>512</FileAlignment>
+    <Deterministic>true</Deterministic>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>..\..\WPFProject\bin\x64\HMI\Api\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="System" />
+    <Reference Include="System.Core" />
+    <Reference Include="System.Data.SQLite">
+      <HintPath>..\..\..\HMI.Old\Plugin_OPCServer\Libs\System.Data.SQLite.dll</HintPath>
+    </Reference>
+    <Reference Include="System.Xml.Linq" />
+    <Reference Include="System.Data.DataSetExtensions" />
+    <Reference Include="Microsoft.CSharp" />
+    <Reference Include="System.Data" />
+    <Reference Include="System.Net.Http" />
+    <Reference Include="System.Xml" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="LogHelper.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <Content Include="x64\SQLite.Interop.dll">
+      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+    </Content>
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\HMI.Util.Base\HMI.Util.Base.csproj">
+      <Project>{d59e25d1-9b27-46a1-8438-92661dd23fae}</Project>
+      <Name>HMI.Util.Base</Name>
+    </ProjectReference>
+  </ItemGroup>
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+</Project>
\ No newline at end of file
diff --git a/SimpleUtil/HMI.LogUtil/LogHelper.cs b/SimpleUtil/HMI.LogUtil/LogHelper.cs
new file mode 100644
index 0000000..007182b
--- /dev/null
+++ b/SimpleUtil/HMI.LogUtil/LogHelper.cs
@@ -0,0 +1,112 @@
+锘縰sing HMI.Simple.Util;
+using System;
+using System.Data.SQLite;
+using System.IO;
+
+namespace HMI.LogUtil
+{
+    public class LogHelper
+    {
+        private static string FileName = "log";
+        private static string createTableSql = @"CREATE TABLE IF NOT EXISTS log (
+                    id INTEGER PRIMARY KEY AUTOINCREMENT,
+                    level TEXT NOT NULL,
+                    text TEXT NOT NULL,
+                    source TEXT,
+                    time TIME
+                    );";
+        /// <summary>
+        /// 闃叉澶氱嚎绋嬪垱寤烘枃浠讹紙浣嗘槸涓嶇煡閬搕eststand浼氫笉浼氬杩涚▼璋冪敤锛�
+        /// </summary>
+        private static object fileLock = new object();
+
+        private static string GetConnectionStr(string dbFile)
+        {
+            return $"DataSource={dbFile};Version=3;Pooling=False;Max Pool Size=100";
+        }
+
+        private static string GetDbFile()
+        {
+            string value = RegUtil.GetLocaltion();
+            string path = Path.Combine(value, "Config", "LogViewer", $"{FileName}_{DateTime.Now:yyyy-MM-dd}.db");
+            if (!File.Exists(path))
+            {
+                CreateDbFile(path);
+            }
+            return path;
+        }
+
+        private static void CreateDbFile(string dbFile)
+        {
+            lock (fileLock)
+            {
+                if (!File.Exists(dbFile))
+                {
+                    string dir = Path.GetDirectoryName(dbFile);
+                    if (!Directory.Exists(dir))
+                    {
+                        Directory.CreateDirectory(dir);
+                    }
+                    SQLiteConnection.CreateFile(dbFile);
+
+                    using (SQLiteConnection conn = new SQLiteConnection())
+                    using (SQLiteCommand cmd = new SQLiteCommand(conn))
+                    {
+                        conn.ConnectionString = GetConnectionStr(dbFile);
+                        conn.Open();
+                        cmd.CommandText = createTableSql;
+                        cmd.ExecuteNonQuery();
+                    }
+                }
+            }
+        }
+
+        private static bool DoLog(string level, string source, string content, out string errMsg)
+        {
+            errMsg = string.Empty;
+            try
+            {
+                string dbFile = GetDbFile();
+                using (SQLiteConnection conn = new SQLiteConnection())
+                using (SQLiteCommand cmd = new SQLiteCommand(conn))
+                {
+                    conn.ConnectionString = GetConnectionStr(dbFile);
+                    conn.Open();
+                    cmd.CommandText =
+                        $@"INSERT INTO log (level, text, source, time) 
+VALUES ('{level}', '{content}', '{source}', '{DateTime.Now:yyyy-MM-dd HH:mm:ss}');";
+                    if (cmd.ExecuteNonQuery() > 0)
+                    {
+                        return true;
+                    }
+                    errMsg = "鍐欏叆鏃ュ織澶辫触";
+                    return false;
+                }
+            }
+            catch (Exception ex)
+            {
+                errMsg = ex.Message;
+                return false;
+            }
+        }
+
+        public static bool Debug(string source, string content, out string errMsg)
+        {
+            return DoLog("Debug", source, content, out errMsg);
+        }
+
+        public static bool Info(string source, string content, out string errMsg)
+        {
+            return DoLog("Info", source, content, out errMsg);
+        }
+        public static bool Warn(string source, string content, out string errMsg)
+        {
+            return DoLog("Warn", source, content, out errMsg);
+        }
+
+        public static bool Error(string source, string content, out string errMsg)
+        {
+            return DoLog("Error", source, content, out errMsg);
+        }
+    }
+}
diff --git a/SimpleUtil/HMI.LogUtil/Properties/AssemblyInfo.cs b/SimpleUtil/HMI.LogUtil/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..45789b3
--- /dev/null
+++ b/SimpleUtil/HMI.LogUtil/Properties/AssemblyInfo.cs
@@ -0,0 +1,33 @@
+锘縰sing System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// 鏈夊叧绋嬪簭闆嗙殑涓�鑸俊鎭敱浠ヤ笅
+// 鎺у埗銆傛洿鏀硅繖浜涚壒鎬у�煎彲淇敼
+// 涓庣▼搴忛泦鍏宠仈鐨勪俊鎭��
+[assembly: AssemblyTitle("HMI.LogUtil")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("HMI.LogUtil")]
+[assembly: AssemblyCopyright("Copyright 漏  2025")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// 灏� ComVisible 璁剧疆涓� false 浼氫娇姝ょ▼搴忛泦涓殑绫诲瀷
+//瀵� COM 缁勪欢涓嶅彲瑙併�傚鏋滈渶瑕佷粠 COM 璁块棶姝ょ▼搴忛泦涓殑绫诲瀷
+//璇峰皢姝ょ被鍨嬬殑 ComVisible 鐗规�ц缃负 true銆�
+[assembly: ComVisible(false)]
+
+// 濡傛灉姝ら」鐩悜 COM 鍏紑锛屽垯涓嬪垪 GUID 鐢ㄤ簬绫诲瀷搴撶殑 ID
+[assembly: Guid("6d0aab08-0f3e-48ce-b755-9d29bc8f1225")]
+
+// 绋嬪簭闆嗙殑鐗堟湰淇℃伅鐢变笅鍒楀洓涓�肩粍鎴�: 
+//
+//      涓荤増鏈�
+//      娆$増鏈�
+//      鐢熸垚鍙�
+//      淇鍙�
+//
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/SimpleUtil/HMI.SerialData.Util/HMI.SerialData.Util.csproj b/SimpleUtil/HMI.SerialData.Util/HMI.SerialData.Util.csproj
index 36235c4..dc47a93 100644
--- a/SimpleUtil/HMI.SerialData.Util/HMI.SerialData.Util.csproj
+++ b/SimpleUtil/HMI.SerialData.Util/HMI.SerialData.Util.csproj
@@ -27,10 +27,28 @@
   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
     <DebugType>pdbonly</DebugType>
     <Optimize>true</Optimize>
-    <OutputPath>bin\Release\</OutputPath>
+    <OutputPath>..\..\WPFProject\bin\x64\HMI\Api\</OutputPath>
     <DefineConstants>TRACE</DefineConstants>
     <ErrorReport>prompt</ErrorReport>
     <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
+    <DebugSymbols>true</DebugSymbols>
+    <OutputPath>bin\x64\Debug\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <DebugType>full</DebugType>
+    <PlatformTarget>x64</PlatformTarget>
+    <LangVersion>7.3</LangVersion>
+    <ErrorReport>prompt</ErrorReport>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
+    <OutputPath>bin\x64\Release\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <Optimize>true</Optimize>
+    <DebugType>pdbonly</DebugType>
+    <PlatformTarget>x64</PlatformTarget>
+    <LangVersion>7.3</LangVersion>
+    <ErrorReport>prompt</ErrorReport>
   </PropertyGroup>
   <ItemGroup>
     <Reference Include="System" />
@@ -54,5 +72,11 @@
       <CopyToOutputDirectory>Always</CopyToOutputDirectory>
     </Content>
   </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\HMI.Util.Base\HMI.Util.Base.csproj">
+      <Project>{d59e25d1-9b27-46a1-8438-92661dd23fae}</Project>
+      <Name>HMI.Util.Base</Name>
+    </ProjectReference>
+  </ItemGroup>
   <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
 </Project>
\ No newline at end of file
diff --git a/SimpleUtil/HMI.SerialData.Util/SerialDataHelper.cs b/SimpleUtil/HMI.SerialData.Util/SerialDataHelper.cs
index a725973..4661184 100644
--- a/SimpleUtil/HMI.SerialData.Util/SerialDataHelper.cs
+++ b/SimpleUtil/HMI.SerialData.Util/SerialDataHelper.cs
@@ -6,13 +6,19 @@
 {
     public class SerialDataHelper
     {
+        private static string FileName = "SerialData";
+        /// <summary>
+        /// 闃叉澶氱嚎绋嬪垱寤烘枃浠讹紙浣嗘槸涓嶇煡閬搕eststand浼氫笉浼氬杩涚▼璋冪敤锛�
+        /// </summary>
+        private static object fileLock = new object();
+
         private static string createTableSql = @"CREATE TABLE IF NOT EXISTS serialData (
                     id INTEGER PRIMARY KEY AUTOINCREMENT,
                     sn TEXT NOT NULL,
                     station TEXT NOT NULL,
                     step TEXT NOT NULL,
                     status TEXT NOT NULL,
-                    time TIME NOT NULL);";
+                    time DATETIME NOT NULL);";
 
         private static string GetConnectionStr(string dbFile)
         {
@@ -21,42 +27,53 @@
 
         private static void CreateDbFile(string dbFile)
         {
-            if (!File.Exists(dbFile))
+            lock (fileLock)
             {
-                string dir = Path.GetDirectoryName(dbFile);
-                if (!Directory.Exists(dir))
+                if (!File.Exists(dbFile))
                 {
-                    Directory.CreateDirectory(dir);
-                }
-                SQLiteConnection.CreateFile(dbFile);
+                    string dir = Path.GetDirectoryName(dbFile);
+                    if (!Directory.Exists(dir))
+                    {
+                        Directory.CreateDirectory(dir);
+                    }
+                    SQLiteConnection.CreateFile(dbFile);
 
-                using (SQLiteConnection conn = new SQLiteConnection())
-                using (SQLiteCommand cmd = new SQLiteCommand(conn))
-                {
-                    conn.ConnectionString = GetConnectionStr(dbFile);
-                    conn.Open();
-                    cmd.CommandText = createTableSql;
-                    cmd.ExecuteNonQuery();
+                    using (SQLiteConnection conn = new SQLiteConnection())
+                    using (SQLiteCommand cmd = new SQLiteCommand(conn))
+                    {
+                        conn.ConnectionString = GetConnectionStr(dbFile);
+                        conn.Open();
+                        cmd.CommandText = createTableSql;
+                        cmd.ExecuteNonQuery();
+                    }
                 }
             }
         }
 
-        public static bool InsertSerialData(string dbFile, string sn, string station, string step, string status, out string errMsg)
+        private static string GetDbFile()
+        {
+            string value = RegUtil.GetLocaltion();
+            string path = Path.Combine(value, "Config", "DataBoard", $"{FileName}_{DateTime.Now:yyyy-MM}.db");
+            if (!File.Exists(path))
+            {
+                CreateDbFile(path);
+            }
+            return path;
+        }
+
+        public static bool InsertSerialData(string sn, string station, string step, string status, out string errMsg)
         {
             errMsg = string.Empty;
             try
             {
-                if (!File.Exists(dbFile))
-                {
-                    CreateDbFile(dbFile);
-                }
+                string dbFile = GetDbFile();
                 using (SQLiteConnection conn = new SQLiteConnection())
                 using (SQLiteCommand cmd = new SQLiteCommand(conn))
                 {
                     conn.ConnectionString = GetConnectionStr(dbFile);
                     conn.Open();
                     cmd.CommandText =
-                        $@"INSERT INTO serialData (sn, station, step, status, time) VALUES ('{sn}', '{station}', '{step}', '{status}', '{DateTime.Now:yyyy-MM-dd HH:mm.ss}');";
+                        $@"INSERT INTO serialData (sn, station, step, status, time) VALUES ('{sn}', '{station}', '{step}', '{status}', '{DateTime.Now:yyyy-MM-dd HH:mm:ss}');";
                     if (cmd.ExecuteNonQuery() > 0)
                     {
                         return true;
@@ -88,7 +105,7 @@
                     conn.ConnectionString = GetConnectionStr(dbFile);
                     conn.Open();
                     cmd.CommandText =
-                        $@"UPDATE serialData SET status='{status}', time='{DateTime.Now:yyyy-MM-dd HH:mm.ss}' WHERE sn='{sn}';";
+                        $@"UPDATE serialData SET status='{status}', time='{DateTime.Now:yyyy-MM-dd HH:mm:ss}' WHERE sn='{sn}';";
                     if (cmd.ExecuteNonQuery() > 0)
                     {
                         return true;
diff --git a/SimpleUtil/HMI.Simple.Util/HMI.Simple.Util.csproj b/SimpleUtil/HMI.Simple.Util/HMI.Simple.Util.csproj
index 52e1110..8d18e6e 100644
--- a/SimpleUtil/HMI.Simple.Util/HMI.Simple.Util.csproj
+++ b/SimpleUtil/HMI.Simple.Util/HMI.Simple.Util.csproj
@@ -25,12 +25,15 @@
   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
     <DebugType>pdbonly</DebugType>
     <Optimize>true</Optimize>
-    <OutputPath>bin\Release\</OutputPath>
+    <OutputPath>..\..\WPFProject\bin\x64\HMI\Api\</OutputPath>
     <DefineConstants>TRACE</DefineConstants>
     <ErrorReport>prompt</ErrorReport>
     <WarningLevel>4</WarningLevel>
   </PropertyGroup>
   <ItemGroup>
+    <Reference Include="NationalInstruments.TestStand.Interop.API, Version=20.0.0.49158, Culture=neutral, PublicKeyToken=ad9244eb3b825cd8, processorArchitecture=MSIL">
+      <EmbedInteropTypes>True</EmbedInteropTypes>
+    </Reference>
     <Reference Include="System" />
     <Reference Include="System.Core" />
     <Reference Include="System.Xml.Linq" />
@@ -43,8 +46,15 @@
   <ItemGroup>
     <Compile Include="ParameterUtil.cs" />
     <Compile Include="Parameter.cs" />
+    <Compile Include="ProductUtil.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
     <Compile Include="XmlHelper.cs" />
   </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\HMI.Util.Base\HMI.Util.Base.csproj">
+      <Project>{d59e25d1-9b27-46a1-8438-92661dd23fae}</Project>
+      <Name>HMI.Util.Base</Name>
+    </ProjectReference>
+  </ItemGroup>
   <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
 </Project>
\ No newline at end of file
diff --git a/SimpleUtil/HMI.Simple.Util/ParameterUtil.cs b/SimpleUtil/HMI.Simple.Util/ParameterUtil.cs
index e979546..7b86228 100644
--- a/SimpleUtil/HMI.Simple.Util/ParameterUtil.cs
+++ b/SimpleUtil/HMI.Simple.Util/ParameterUtil.cs
@@ -1,4 +1,4 @@
-锘縰sing Microsoft.Win32;
+锘縰sing NationalInstruments.TestStand.Interop.API;
 using System;
 using System.Collections.Generic;
 using System.IO;
@@ -8,8 +8,12 @@
 {
     public class ParameterUtil
     {
-        private static string Company = "JingCe";
-        private static string Program = "HMI";
+        /// <summary>
+        /// 
+        /// </summary>
+        /// <param name="path"></param>
+        /// <param name="name"></param>
+        /// <returns></returns>
         public static string GetCommonParameterValue(string path, string name)
         {
             if (!File.Exists(path)) return null;
@@ -29,38 +33,21 @@
             errMsg = string.Empty;
             try
             {
-                string registryPath = $@"SOFTWARE\{Company}\{Program}";
-                // 灏濊瘯鎵撳紑娉ㄥ唽琛ㄩ敭
-                using (RegistryKey key = Registry.LocalMachine.OpenSubKey(registryPath))
+                string value = RegUtil.GetLocaltion();
+                string path = Path.Combine(value, "Config", "Parameter", "CommonParameter.data");
+                if (!File.Exists(path))
                 {
-                    if (key == null)
-                    {
-                        errMsg = "娉ㄥ唽琛ㄤ笉瀛樺湪锛岃鍏堣繍琛屼竴娆MI";
-                        return null;
-                    }
-
-                    string value = key.GetValue("Location")?.ToString();
-                    if (string.IsNullOrEmpty(value))
-                    {
-                        errMsg = "娉ㄥ唽琛ㄤ笉瀛樺湪锛岃鍏堣繍琛屼竴娆MI";
-                        return null;
-                    }
-                    string path = Path.Combine(value, "Config", "Parameter", "CommonParameter.data");
-                    if (!File.Exists(path))
-                    {
-                        errMsg = "鏈壘鍒伴厤缃枃浠�";
-                        return null;
-                    }
-                    List<ParameterData> datas = XmlHelper.DeserializeFromXml<List<ParameterData>>(path);
-                    ParameterData data = datas.FirstOrDefault(d => d.Name == name);
-                    if (data == null)
-                    {
-                        errMsg = "鏈壘鍒板弬鏁帮紝璇锋鏌ラ厤缃�";
-                        return null;
-                    }
-                    ;
-                    return data.Value;
+                    errMsg = "鏈壘鍒伴厤缃枃浠�";
+                    return null;
                 }
+                List<ParameterData> datas = XmlHelper.DeserializeFromXml<List<ParameterData>>(path);
+                ParameterData data = datas.FirstOrDefault(d => d.Name == name);
+                if (data == null)
+                {
+                    errMsg = "鏈壘鍒板弬鏁帮紝璇锋鏌ラ厤缃�";
+                    return null;
+                }
+                return data.Value;
             }
             catch (Exception ex)
             {
@@ -68,5 +55,137 @@
                 return null;
             }
         }
+
+        private static void WriteParameterData(SequenceContext context, ParameterData data)
+        {
+            string prefix = "SystemSetting";
+            if (data.Type == "Bool")
+            {
+                context.StationGlobals.SetValBoolean($"{prefix}.{data.Name}", 1, bool.Parse(data.Value));
+            }
+            else if (data.Type == "Int")
+            {
+                context.StationGlobals.SetValInteger64($"{prefix}.{data.Name}", 1, int.Parse(data.Value));
+            }
+            else if (data.Type == "Double")
+            {
+                context.StationGlobals.SetValNumber($"{prefix}.{data.Name}", 1, double.Parse(data.Value));
+            }
+            else
+            {
+                context.StationGlobals.SetValString($"{prefix}.{data.Name}", 1, data.Value);
+            }
+        }
+
+        /// <summary>
+        /// 灏嗗弬鏁板啓杩汦ngine鐨凷tationGlobals.StationState
+        /// </summary>
+        /// <param name="name"></param>
+        /// <returns></returns>
+        public static bool WriteConfigToContext(object instance, out string errMsg)
+        {
+            errMsg = string.Empty;
+            try
+            {
+                string value = RegUtil.GetLocaltion();
+                string path = Path.Combine(value, "Config", "Parameter", "CommonParameter.data");
+                return WriteConfigToContext(path, instance, out errMsg);
+            }
+            catch (Exception ex)
+            {
+                errMsg = ex.Message;
+                return false;
+            }
+        }
+
+        /// <summary>
+        /// 灏嗗弬鏁板啓杩汦ngine鐨凷tationGlobals.StationState
+        /// </summary>
+        /// <param name="name"></param>
+        /// <returns></returns>
+        public static bool WriteConfigToContext(string xmlPath, object instance, out string errMsg)
+        {
+            errMsg = string.Empty;
+            try
+            {
+                SequenceContext context = (SequenceContext)instance;
+                if (string.IsNullOrEmpty(xmlPath))
+                {
+                    throw new Exception("閰嶇疆鏂囦欢璺緞涓嶈兘涓虹┖");
+                }
+                if (!File.Exists(xmlPath))
+                {
+                    throw new Exception("鏈壘鍒伴厤缃枃浠�");
+                }
+                List<ParameterData> datas = XmlHelper.DeserializeFromXml<List<ParameterData>>(xmlPath);
+                foreach (var data in datas)
+                {
+                    WriteParameterData(context, data);
+                }
+                return true;
+            }
+            catch (Exception ex)
+            {
+                errMsg = ex.Message;
+                return false;
+            }
+        }
+
+        /// <summary>
+        /// 灏嗗弬鏁板啓杩汦ngine鐨凷tationGlobals.StationState
+        /// </summary>
+        /// <param name="name"></param>
+        /// <returns></returns>
+        public static bool WriteConfigToEngineByName(string xmlPath, object instance, string name, out string errMsg)
+        {
+            errMsg = string.Empty;
+            try
+            {
+                SequenceContext context = (SequenceContext)instance;
+                if (string.IsNullOrEmpty(xmlPath))
+                {
+                    throw new Exception("閰嶇疆鏂囦欢璺緞涓嶈兘涓虹┖");
+                }
+                if (!File.Exists(xmlPath))
+                {
+                    throw new Exception("鏈壘鍒伴厤缃枃浠�");
+                }
+                List<ParameterData> datas = XmlHelper.DeserializeFromXml<List<ParameterData>>(xmlPath);
+                ParameterData data = datas.FirstOrDefault(d => d.Name == name);
+                if (data == null)
+                {
+                    throw new Exception("鏈壘鍒板弬鏁帮紝璇锋鏌ラ厤缃�");
+                }
+                WriteParameterData(context, data);
+                return true;
+            }
+            catch (Exception ex)
+            {
+                errMsg = ex.Message;
+                return false;
+            }
+        }
+
+        /// <summary>
+        /// 灏嗗弬鏁板啓杩汦ngine鐨凷tationGlobals.StationState
+        /// </summary>
+        /// <param name="name"></param>
+        /// <returns></returns>
+        public static bool WriteConfigToEngineByName(object instance, string name, out string errMsg)
+        {
+            errMsg = string.Empty;
+            try
+            {
+                SequenceContext engine = (SequenceContext)instance;
+                string value = RegUtil.GetLocaltion();
+                string path = Path.Combine(value, "Config", "Parameter", "CommonParameter.data");
+                return WriteConfigToEngineByName(path, instance, name, out errMsg);
+            }
+            catch (Exception ex)
+            {
+                errMsg = ex.Message;
+                return false;
+            }
+        }
     }
 }
diff --git a/SimpleUtil/HMI.Simple.Util/ProductUtil.cs b/SimpleUtil/HMI.Simple.Util/ProductUtil.cs
new file mode 100644
index 0000000..b4cf7db
--- /dev/null
+++ b/SimpleUtil/HMI.Simple.Util/ProductUtil.cs
@@ -0,0 +1,70 @@
+锘縰sing NationalInstruments.TestStand.Interop.API;
+using System;
+using System.Collections.Generic;
+using System.IO;
+
+namespace HMI.Simple.Util
+{
+    public class ProductUtil
+    {
+        private static void WriteParameterData(object instance, ParameterData data)
+        {
+            SequenceContext context = (SequenceContext)instance;
+            if (data.Type == "Bool")
+            {
+                context.FileGlobals.SetValBoolean($"DUTSetting.{data.Name}", 1, bool.Parse(data.Value));
+            }
+            else if (data.Type == "Int")
+            {
+                context.FileGlobals.SetValInteger64($"DUTSetting.{data.Name}", 1, int.Parse(data.Value));
+            }
+            else if (data.Type == "Double")
+            {
+                context.FileGlobals.SetValNumber($"DUTSetting.{data.Name}", 1, double.Parse(data.Value));
+            }
+            else
+            {
+                context.FileGlobals.SetValString($"DUTSetting.{data.Name}", 1, data.Value);
+            }
+        }
+
+        public static bool WriteConfigToContextByModel(string model, object instance, out string errMsg)
+        {
+            errMsg = string.Empty;
+            try
+            {
+                string dir = RegUtil.GetLocaltion();
+                string path = Path.Combine(dir, "Config", "Product", $"Parameter_{model}.data");
+                return WriteConfigToContextByPath(path, instance, out errMsg);
+            }
+            catch (Exception ex)
+            {
+                errMsg = ex.Message;
+                return false;
+            }
+        }
+
+        public static bool WriteConfigToContextByPath(string path, object instance, out string errMsg)
+        {
+            errMsg = string.Empty;
+            try
+            {
+                if (!File.Exists(path))
+                {
+                    throw new Exception("鏈壘鍒拌閰嶇疆锛岃妫�鏌�");
+                }
+                List<ParameterData> datas = XmlHelper.DeserializeFromXml<List<ParameterData>>(path);
+                foreach (var data in datas)
+                {
+                    WriteParameterData(instance, data);
+                }
+                return true;
+            }
+            catch (Exception ex)
+            {
+                errMsg = ex.Message;
+                return false;
+            }
+        }
+    }
+}
diff --git a/SimpleUtil/HMI.Util.Base/HMI.Util.Base.csproj b/SimpleUtil/HMI.Util.Base/HMI.Util.Base.csproj
new file mode 100644
index 0000000..eeaf7ca
--- /dev/null
+++ b/SimpleUtil/HMI.Util.Base/HMI.Util.Base.csproj
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProjectGuid>{D59E25D1-9B27-46A1-8438-92661DD23FAE}</ProjectGuid>
+    <OutputType>Library</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>HMI.Util.Base</RootNamespace>
+    <AssemblyName>HMI.Util.Base</AssemblyName>
+    <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
+    <FileAlignment>512</FileAlignment>
+    <Deterministic>true</Deterministic>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>bin\Release\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="System" />
+    <Reference Include="System.Core" />
+    <Reference Include="System.Xml.Linq" />
+    <Reference Include="System.Data.DataSetExtensions" />
+    <Reference Include="Microsoft.CSharp" />
+    <Reference Include="System.Data" />
+    <Reference Include="System.Net.Http" />
+    <Reference Include="System.Xml" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="RegUtil.cs" />
+  </ItemGroup>
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+</Project>
\ No newline at end of file
diff --git a/SimpleUtil/HMI.Util.Base/Properties/AssemblyInfo.cs b/SimpleUtil/HMI.Util.Base/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..d4c4d1f
--- /dev/null
+++ b/SimpleUtil/HMI.Util.Base/Properties/AssemblyInfo.cs
@@ -0,0 +1,33 @@
+锘縰sing System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// 鏈夊叧绋嬪簭闆嗙殑涓�鑸俊鎭敱浠ヤ笅
+// 鎺у埗銆傛洿鏀硅繖浜涚壒鎬у�煎彲淇敼
+// 涓庣▼搴忛泦鍏宠仈鐨勪俊鎭��
+[assembly: AssemblyTitle("HMI.Util.Base")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("HMI.Util.Base")]
+[assembly: AssemblyCopyright("Copyright 漏  2025")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// 灏� ComVisible 璁剧疆涓� false 浼氫娇姝ょ▼搴忛泦涓殑绫诲瀷
+//瀵� COM 缁勪欢涓嶅彲瑙併�傚鏋滈渶瑕佷粠 COM 璁块棶姝ょ▼搴忛泦涓殑绫诲瀷
+//璇峰皢姝ょ被鍨嬬殑 ComVisible 鐗规�ц缃负 true銆�
+[assembly: ComVisible(false)]
+
+// 濡傛灉姝ら」鐩悜 COM 鍏紑锛屽垯涓嬪垪 GUID 鐢ㄤ簬绫诲瀷搴撶殑 ID
+[assembly: Guid("d59e25d1-9b27-46a1-8438-92661dd23fae")]
+
+// 绋嬪簭闆嗙殑鐗堟湰淇℃伅鐢变笅鍒楀洓涓�肩粍鎴�: 
+//
+//      涓荤増鏈�
+//      娆$増鏈�
+//      鐢熸垚鍙�
+//      淇鍙�
+//
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/SimpleUtil/HMI.Util.Base/RegUtil.cs b/SimpleUtil/HMI.Util.Base/RegUtil.cs
new file mode 100644
index 0000000..1ea48f8
--- /dev/null
+++ b/SimpleUtil/HMI.Util.Base/RegUtil.cs
@@ -0,0 +1,31 @@
+锘縰sing Microsoft.Win32;
+using System;
+
+namespace HMI.Simple.Util
+{
+    public class RegUtil
+    {
+        private static string Company = "JingCe";
+        private static string Program = "HMI";
+
+        public static string GetLocaltion()
+        {
+            string registryPath = $@"SOFTWARE\{Company}\{Program}";
+            // 灏濊瘯鎵撳紑娉ㄥ唽琛ㄩ敭
+            using (RegistryKey key = Registry.CurrentUser.OpenSubKey(registryPath))
+            {
+                if (key == null)
+                {
+                    throw new Exception("娉ㄥ唽琛ㄤ笉瀛樺湪锛岃鍏堣繍琛屼竴娆MI");
+                }
+
+                string value = key.GetValue("Location")?.ToString();
+                if (string.IsNullOrEmpty(value))
+                {
+                    throw new Exception("娉ㄥ唽琛ㄤ笉瀛樺湪锛岃鍏堣繍琛屼竴娆MI");
+                }
+                return value;
+            }
+        }
+    }
+}
diff --git a/VisionBuilder.Test/PluginOne.cs b/VisionBuilder.Test/PluginOne.cs
index 06f122a..ca5baf2 100644
--- a/VisionBuilder.Test/PluginOne.cs
+++ b/VisionBuilder.Test/PluginOne.cs
@@ -4,11 +4,7 @@
 
 namespace VisionBuilder.Test
 {
-    public class PluginOne : IStepRunner
+    public class PluginOne
     {
-        public void RunStep(Dictionary<string, Param> param)
-        {
-            param["yoho"] = new Param { Name = "1", Value = "2" };
-        }
     }
 }
diff --git a/WPFProject/Api/Api.txt b/WPFProject/Api/Api.txt
new file mode 100644
index 0000000..5f28270
--- /dev/null
+++ b/WPFProject/Api/Api.txt
@@ -0,0 +1 @@
+锘�
\ No newline at end of file
diff --git a/WPFProject/App.xaml b/WPFProject/App.xaml
index d38cde6..0368538 100644
--- a/WPFProject/App.xaml
+++ b/WPFProject/App.xaml
@@ -24,6 +24,8 @@
             <uic:VisibilityBoolConverter x:Key="vbConverter"/>
             <uic:DataGridRowFlagConverter x:Key="rowFlag"/>
             <hc:LangConverter x:Key="lc"/>
+            <hc:WidthSizeConverter x:Key="fc"/>
+            <hc:HeightSizeConverter x:Key="hfc"/>
         </ResourceDictionary>
     </Application.Resources>
 </Application>
diff --git a/WPFProject/App.xaml.cs b/WPFProject/App.xaml.cs
index bbef0c0..c63a2e2 100644
--- a/WPFProject/App.xaml.cs
+++ b/WPFProject/App.xaml.cs
@@ -5,7 +5,9 @@
 using Microsoft.Win32;
 using Panuon.WPF.UI;
 using System;
+using System.Diagnostics;
 using System.IO;
+using System.Management;
 using System.Reflection;
 using System.Threading;
 using System.Windows;
@@ -35,6 +37,25 @@
                     MessageBoxIcon.Error, DefaultButton.YesOK, 5);
         }
 
+        public static Process GetProcess()
+        {
+            Process cur = Process.GetCurrentProcess();
+            string exePath = Assembly.GetExecutingAssembly().Location;
+            string query = $"SELECT ProcessId FROM Win32_Process WHERE ExecutablePath = '{exePath.Replace("\\", "\\\\")}'";
+            using (var searcher = new ManagementObjectSearcher(query))
+            {
+                foreach (var obj in searcher.Get())
+                {
+                    int processId = Convert.ToInt32(obj["ProcessId"]);
+                    if (processId != cur.Id)
+                    {
+                        return Process.GetProcessById(processId);
+                    }
+                }
+            }
+            return null;
+        }
+
         /// <summary>
         /// 鍚姩鍚庡線娉ㄥ唽琛ㄥ啓鐐逛笢瑗�
         /// </summary>
@@ -45,7 +66,7 @@
             {
                 string registryPath = @$"SOFTWARE\{Company}\{Program}";
 
-                using RegistryKey key = Registry.LocalMachine.CreateSubKey(registryPath, true)
+                using RegistryKey key = Registry.CurrentUser.CreateSubKey(registryPath, true)
                     ?? throw new Exception("鏃犳硶鍒涘缓鎴栨墦寮�娉ㄥ唽琛ㄩ敭");
                 string path = AppDomain.CurrentDomain.BaseDirectory;
                 key.SetValue("Location", path, RegistryValueKind.String);
@@ -56,13 +77,34 @@
             }
         }
 
+        private void DoUpdateAction()
+        {
+            using (SqliteHelper helper = new SqliteHelper(PathHelper.DatabasePath))
+            {
+                if (!helper.HasData("SELECT 1 FROM pragma_table_info('user') WHERE name = 'code';"))
+                {
+                    helper.ExecuteSql("ALTER TABLE user ADD code TEXT;");
+                }
+            }
+        }
+
         private static Mutex mutex;
         private void Application_Startup(object sender, StartupEventArgs e)
         {
+            var process = GetProcess();
+
             mutex = new Mutex(true, "TestPlatform_OnlyRun_CRNS");
-            if (mutex.WaitOne(0, false))
+            if (mutex.WaitOne(0, false))//&& process == null
             {
                 WriteRegister();
+                try
+                {
+                    DoUpdateAction();
+                }
+                catch (Exception ex) {
+                    log.Error("Update Failed.", ex);
+                }
+
                 logCleanner = new LogCleanner();
                 AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
                 LoggerHelper.Register(new CommonLogger());
diff --git a/WPFProject/BuildCmd.txt b/WPFProject/BuildCmd.bat
similarity index 78%
rename from WPFProject/BuildCmd.txt
rename to WPFProject/BuildCmd.bat
index 92b09e8..c912389 100644
--- a/WPFProject/BuildCmd.txt
+++ b/WPFProject/BuildCmd.bat
@@ -1,6 +1,8 @@
 锘縨ove /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.AlarmInfoManage.dll" $(TargeDir)Plugin\HMI.AlarmInfoManage.dll"
 move /y "$(TargeDir)HMI.DataViewer.dll" $(TargeDir)Plugin\HMI.DataViewer.dll"
@@ -15,6 +17,7 @@
 move /y "$(TargeDir)Plugin.DOControl.dll" $(TargeDir)Plugin\Plugin.DOControl.dll"
 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"
 
 setlocal enabledelayedexpansion
 
@@ -25,7 +28,7 @@
 mkdir "%destination_folder%"
 
 for /d %%D in ("%source_folder%\*") do (
-    if /I not "%%~nxD"=="Config" if /I not "%%~nxD"=="Icon" if /I not "%%~nxD"=="Lang" if /I not "%%~nxD"=="Libs" if /I not "%%~nxD"=="Log" if /I not "%%~nxD"=="Plugin" (
+    if /I not "%%~nxD"=="Api" if /I not "%%~nxD"=="Config" if /I not "%%~nxD"=="Icon" if /I not "%%~nxD"=="Lang" if /I not "%%~nxD"=="Libs" if /I not "%%~nxD"=="Log" if /I not "%%~nxD"=="Plugin" if /I not "%%~nxD"=="TestPlatform.exe.WebView2" (
         move /Y "%%D" "%destination_folder%"
     )
 )
@@ -34,4 +37,6 @@
     if /I not "%%~nxF"=="$(TargetFileName)" if /I not "%%~nxF"=="$(TargetFileName).config" if /I not "%%~nxF"=="$(TargetName).pdb" if /I not "%%~nxF"=="clear.bat" (
         move /Y "%%F" "%destination_folder%"
     )
-)
\ No newline at end of file
+)
+
+del /s /q "%source_folder%\*.pdb"
\ No newline at end of file
diff --git a/WPFProject/Lang/en-US/TestExecution.json b/WPFProject/Lang/en-US/TestExecution.json
index eaab7de..617a893 100644
--- a/WPFProject/Lang/en-US/TestExecution.json
+++ b/WPFProject/Lang/en-US/TestExecution.json
@@ -23,6 +23,11 @@
   "Ts.EnableMes": "Enable Mes",
   "Ts.EnableCanLog": "Enable CanLog",
   "Ts.DebugMode": "Debug Mode",
+  "Ts.EnableNGPause": "NG Pause",
+  "Ts.EnableSimpleMode": "Simple Mode",
+  "Ts.DisableSimpleMode": "Not Simple Mode",
+  "Ts.UI_ShowInfoDock": "Show Info Dock",
+  "Ts.PauseMsg": "NG Paused,Continue?",
 
   "Ts.ProbeCount": "Probe Count:",
   "Ts.ProbeAlarmCount": "Alarm Count:",
diff --git a/WPFProject/Lang/en-US/app.json b/WPFProject/Lang/en-US/app.json
index 1e28970..1f219a1 100644
--- a/WPFProject/Lang/en-US/app.json
+++ b/WPFProject/Lang/en-US/app.json
@@ -56,6 +56,8 @@
   "App.Import": "Import",
   "App.ImportSuccess": "Import success",
   "App.ImportFailed": "Import failed",
+  "App.ExportSuccess": "Export Success",
+  "App.ExportFailed": "Export Failed",
 
   "App.Save": "Save",
   "App.SaveSuccess": "Save success",
@@ -103,6 +105,7 @@
   "App.Welcome": "Welcom! {0}",
   "App.MakeSureLogout": "Are you sure you want to logout?",
   "App.MakeSureRestartApp": "Are you sure you want to restart锛�",
+  "App.NoExportData": "No Data To Export",
 
 
   "HMI.NoDeleteData": "Please select items you want to delete",
@@ -130,6 +133,7 @@
   "HMI.User.Title": "User list",
   "HMI.User.RealName": "Name",
   "HMI.User.Account": "Username",
+  "HMI.User.Code": "Code",
   "HMI.User.Password": "Password",
   "HMI.User.Role": "User role",
   "HMI.User.Add": "New user",
@@ -144,5 +148,14 @@
   "HMI.Role.Edit": "Edit role",
 
   "HMI.Line": "Line",
-  "HMI.Station": "Station"
+  "HMI.Station": "Station",
+  "App.SequenceIsStopping": "Stopping Test,Please Wait...",
+
+
+  "App.StopSequence": "Stop",
+  "App.StartSequence": "Start",
+  "App.ScannerSetting": "Scanner Setting",
+  "App.SequenceIsStarting": "Starting Test,Please Wait...",
+  "App.Status": "Status",
+  "App.Info": "Info"
 }
\ No newline at end of file
diff --git a/WPFProject/Lang/zh-CN/TestExecution.json b/WPFProject/Lang/zh-CN/TestExecution.json
index ba61cf9..f88011b 100644
--- a/WPFProject/Lang/zh-CN/TestExecution.json
+++ b/WPFProject/Lang/zh-CN/TestExecution.json
@@ -6,10 +6,10 @@
   "Ts.BtnNotSample": "鏍蜂欢妯″紡鍏抽棴",
   "Ts.SelectProductModel": "璇烽�夋嫨浜у搧鍨嬪彿",
 
-  "Ts.Total": "鎬绘暟",
-  "Ts.Pass": "鑹搧鏁�",
-  "Ts.Fail": "涓嶈壇鍝佹暟",
-  "Ts.PassRate": "鑹搧鐜�",
+  "Ts.Total": "鎬绘暟:",
+  "Ts.Pass": "鑹搧鏁�:",
+  "Ts.Fail": "涓嶈壇鍝佹暟:",
+  "Ts.PassRate": "鑹搧鐜�:",
 
   "Ts.SampleTestFail": "鏍蜂欢娴嬭瘯鍚姩澶辫触",
   "Ts.LoadSequenceFail": "鍔犺浇娴嬭瘯搴忓垪澶辫触",
@@ -18,11 +18,16 @@
   "Ts.MakeSureClearNum": "纭畾瑕佹竻绌鸿鏁板悧锛�",
   "Ts.SequenceIsStarting": "姝e湪鍚姩娴嬭瘯搴忓垪...",
   "Ts.SequenceIsStopping": "姝e湪鍋滄娴嬭瘯搴忓垪...",
-  "Ts.CurrentSnr": "褰撳墠搴忓垪鍙�",
-  "Ts.TimeSpan": "娴嬭瘯鑰楁椂",
+  "Ts.CurrentSnr": "褰撳墠搴忓垪鍙�:",
+  "Ts.TimeSpan": "娴嬭瘯鑰楁椂:",
   "Ts.EnableMes": "鍚敤Mes",
   "Ts.EnableCanLog": "鍚敤CanLog",
   "Ts.DebugMode": "Debug妯″紡",
+  "Ts.EnableNGPause": "NG鏆傚仠",
+  "Ts.EnableSimpleMode": "绠�鏄撴ā寮�",
+  "Ts.DisableSimpleMode": "璇︾粏妯″紡",
+  "Ts.UI_ShowInfoDock": "鏄剧ず淇℃伅鏍�",
+  "Ts.PauseMsg": "娴嬭瘯NG鏆傚仠锛屾槸鍚︾户缁紵",
 
   "Ts.ProbeCount": "鎺㈤拡浣跨敤娆℃暟:",
   "Ts.ProbeAlarmCount": "棰勮娆℃暟:",
diff --git a/WPFProject/Lang/zh-CN/app.json b/WPFProject/Lang/zh-CN/app.json
index 23f777d..79111c2 100644
--- a/WPFProject/Lang/zh-CN/app.json
+++ b/WPFProject/Lang/zh-CN/app.json
@@ -56,6 +56,8 @@
   "App.Import": "瀵煎叆",
   "App.ImportSuccess": "瀵煎叆鎴愬姛",
   "App.ImportFailed": "瀵煎叆澶辫触",
+  "App.ExportSuccess": "瀵煎嚭鎴愬姛",
+  "App.ExportFailed": "瀵煎嚭澶辫触",
 
   "App.Save": "淇濆瓨",
   "App.SaveSuccess": "淇濆瓨鎴愬姛",
@@ -103,6 +105,7 @@
   "App.Welcome": "娆㈣繋鎮紝{0}",
   "App.MakeSureLogout": "纭畾瑕侀��鍑虹櫥闄嗗悧锛�",
   "App.MakeSureRestartApp": "纭畾瑕侀噸鏂板惎鍔ㄥ悧锛�",
+  "App.NoExportData": "娌℃湁瑕佸鍑虹殑鏁版嵁",
 
 
   "HMI.NoDeleteData": "璇烽�夋嫨瑕佸垹闄ょ殑鏁版嵁",
@@ -130,6 +133,7 @@
   "HMI.User.Title": "鐢ㄦ埛鍒楄〃",
   "HMI.User.RealName": "鐢ㄦ埛鍚嶇О",
   "HMI.User.Account": "璐﹀彿",
+  "HMI.User.Code": "宸ュ彿",
   "HMI.User.Password": "瀵嗙爜",
   "HMI.User.Role": "鐢ㄦ埛瑙掕壊",
   "HMI.User.Add": "鏂板鐢ㄦ埛",
@@ -144,5 +148,13 @@
   "HMI.Role.Edit": "缂栬緫瑙掕壊",
 
   "HMI.Line": "浜х嚎",
-  "HMI.Station": "宸ョ珯"
+  "HMI.Station": "宸ョ珯",
+  "App.SequenceIsStopping": "姝e湪鍋滄娴嬭瘯锛岃绋嶅悗...",
+
+  "App.StopSequence": "鍋滄娴嬭瘯搴忓垪",
+  "App.StartSequence": "鍚姩娴嬭瘯搴忓垪",
+  "App.ScannerSetting": "鎵爜鏋缃�",
+  "App.SequenceIsStarting": "姝e湪鍚姩娴嬭瘯搴忓垪锛岃绋嶅悗...",
+  "App.Status": "鐘舵��",
+  "App.Info": "淇℃伅"
 }
\ No newline at end of file
diff --git a/WPFProject/Login.xaml.cs b/WPFProject/Login.xaml.cs
index bf04d2c..3be043e 100644
--- a/WPFProject/Login.xaml.cs
+++ b/WPFProject/Login.xaml.cs
@@ -59,7 +59,7 @@
             try
             {
                 helper = new SqliteHelper(PathHelper.DatabasePath);
-                string sql = $"SELECT id,username,password,name From user WHERE username='{username}' and password='{password}'";
+                string sql = $"SELECT id,code,username,password,name From user WHERE username='{username}' and password='{password}'";
                 DataTable dt = helper.GetDataTable(sql);
                 if (dt == null || dt.Rows.Count <= 0)
                 {
@@ -67,6 +67,7 @@
                 }
                 UserInfo user = new UserInfo(
                     dt.Rows[0]["id"].ToString(),
+                    dt.Rows[0]["code"].ToString(),
                     dt.Rows[0]["username"].ToString(),
                     dt.Rows[0]["name"].ToString());
                 UserInfo.Login(user);
@@ -75,7 +76,7 @@
                     // 璁颁綇鐧诲綍鎿嶄綔
                     string userToken =
                         JsonUtil.Serialize(
-                            new UserInfo(null, user.Username, user.Name, dt.Rows[0]["password"].ToString()));
+                            new UserInfo(null, user.Code, user.Username, user.Name, dt.Rows[0]["password"].ToString()));
                     // 瀛樺偍鏁版嵁
                     BinUtil.SaveStrToBin(encryptor.Encrypt(userToken),
                         Path.Combine(PathHelper.DatabaseFolder, "user.bin"));
diff --git a/WPFProject/PanuonWindow.xaml b/WPFProject/PanuonWindow.xaml
index ef1eee4..867d337 100644
--- a/WPFProject/PanuonWindow.xaml
+++ b/WPFProject/PanuonWindow.xaml
@@ -12,7 +12,7 @@
             WindowState="Maximized"
             x:Name="window"
             BorderBrush="Transparent"
-            pu:WindowXCaption.Buttons="All"
+            pu:WindowXCaption.Buttons="None"
             xmlns:sys="clr-namespace:System;assembly=mscorlib"
             xmlns:dp="clr-namespace:HMI.UI.DpUtil;assembly=HMI.UI"
             xmlns:hc="clr-namespace:HMI.Core;assembly=HMI.Core"
@@ -89,9 +89,10 @@
                 FontFamily="/HMI.UI;component/Resources/Font/#iconfont"
                 Content="&#xe60e;"
                 RenderTransformOrigin="0.5,0.5"
-                Click="Button_Click_2">
+                Click="Button_Click">
                 <Button.Style>
                     <Style TargetType="Button" BasedOn="{StaticResource {x:Static pu:WindowXCaption.MinimizeButtonStyleKey}}">
+                        <Setter Property="Focusable" Value="False"/>
                         <Style.Triggers>
                             <DataTrigger Binding="{Binding ShowMenu}" Value="True">
                                 <Setter Property="Button.RenderTransform">
@@ -103,7 +104,30 @@
                         </Style.Triggers>
                     </Style>
                 </Button.Style>
+            </Button>
 
+            <Button FontSize="18" Click="Button_Click" x:Name="minBtn">
+                <Button.Style>
+                    <Style TargetType="Button" BasedOn="{StaticResource {x:Static pu:WindowXCaption.MinimizeButtonStyleKey}}">
+                        <Setter Property="Focusable" Value="False"/>
+                    </Style>
+                </Button.Style>
+            </Button>
+
+            <Button FontSize="18" Click="Button_Click" x:Name="maxBtn">
+                <Button.Style>
+                    <Style TargetType="Button" BasedOn="{StaticResource {x:Static pu:WindowXCaption.MaximizeButtonStyleKey}}">
+                        <Setter Property="Focusable" Value="False"/>
+                    </Style>
+                </Button.Style>
+            </Button>
+
+            <Button FontSize="18" Click="Button_Click" x:Name="closeBtn">
+                <Button.Style>
+                    <Style TargetType="Button" BasedOn="{StaticResource {x:Static pu:WindowXCaption.CloseButtonStyleKey}}">
+                        <Setter Property="Focusable" Value="False"/>
+                    </Style>
+                </Button.Style>
             </Button>
         </StackPanel>
     </pu:WindowXCaption.ExtendControl>
diff --git a/WPFProject/PanuonWindow.xaml.cs b/WPFProject/PanuonWindow.xaml.cs
index d8b4ffe..71de9ee 100644
--- a/WPFProject/PanuonWindow.xaml.cs
+++ b/WPFProject/PanuonWindow.xaml.cs
@@ -40,7 +40,7 @@
             RefrshConfig();
             InitializeComponent();
             userMenu.Header = this.lang("App.Welcome", UserInfo.User.Name);
-            ShowMenu = true; 
+            ShowMenu = false;
             Application.Current.MainWindow = this;
             SystemSetting.SystemSettingChanged += SystemSetting_SystemSettingChanged;
         }
@@ -91,18 +91,6 @@
             }
         }
 
-
-        private void Button_Click_2(object sender, RoutedEventArgs e)
-        {
-            ShowMenu = !ShowMenu;
-            //Button btn = (Button)sender;
-            //double temp = btn.RenderTransform.Value.Determinant;
-            //btn.RenderTransformOrigin = new Point(0.5, 0.5);
-            //ScaleTransform verticalFlipTransform = new ScaleTransform(1, temp * -1);
-            //btn.RenderTransform = verticalFlipTransform;
-            //menuTab.ChangeItemVisibility();
-        }
-
         private void SystemSetting_SystemSettingChanged(object sender, SystemSetting e)
         {
             RefrshConfig();
@@ -121,8 +109,10 @@
         }
 
         private void MenuItem_Click(object sender, RoutedEventArgs e)
-        {MenuItem mi = sender as MenuItem;
-            switch (mi.Name) {
+        {
+            MenuItem mi = sender as MenuItem;
+            switch (mi.Name)
+            {
                 case "MiLogout":
                     Logout();
                     break;
@@ -139,7 +129,8 @@
             if (MsgBoxHelper.WarnMessage(this, this.lang("App.MakeSureLogout")) == MessageBoxResult.OK)
             {
                 string path = Path.Combine(PathHelper.DatabaseFolder, "user.bin");
-                if (File.Exists(path)) { 
+                if (File.Exists(path))
+                {
                     File.Delete(path);
                 }
                 this.Closed -= window_Closed;
@@ -169,6 +160,34 @@
             Application.Current.Shutdown();
         }
 
+        private void Button_Click(object sender, RoutedEventArgs e)
+        {
+            Button btn = sender as Button;
+            switch (btn.Name)
+            {
+                case "minBtn":
+                    this.Minimize();
+                    break;
+                case "maxBtn":
+                    this.Maximize();
+                    break;
+                case "closeBtn":
+                    this.Close();
+                    break;
+                case "BtnSwitchTheme":
+                    ShowMenu = !ShowMenu;
+                    //Button btn = (Button)sender;
+                    //double temp = btn.RenderTransform.Value.Determinant;
+                    //btn.RenderTransformOrigin = new Point(0.5, 0.5);
+                    //ScaleTransform verticalFlipTransform = new ScaleTransform(1, temp * -1);
+                    //btn.RenderTransform = verticalFlipTransform;
+                    //menuTab.ChangeItemVisibility();
+                    break;
+                default:
+                    break;
+            }
+        }
+
         //private void BaseWindow_Loaded(object sender, RoutedEventArgs e)
         //{
         //    var s = (Style)this.Resources["WindowButton"];
diff --git a/WPFProject/Properties/app.manifest b/WPFProject/Properties/app.manifest
index 5a95963..f7c83c6 100644
--- a/WPFProject/Properties/app.manifest
+++ b/WPFProject/Properties/app.manifest
@@ -1,10 +1,10 @@
 锘�<?xml version="1.0" encoding="utf-8"?>
 <assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
-  <assemblyIdentity version="1.0.0.0" name="MyApplication.app" />
-  <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
-    <security>
-      <requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
-        <!-- UAC 娓呭崟閫夐」
+	<assemblyIdentity version="1.0.0.0" name="MyApplication.app" />
+	<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
+		<security>
+			<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
+				<!-- UAC 娓呭崟閫夐」
              濡傛灉鎯宠鏇存敼 Windows 鐢ㄦ埛甯愭埛鎺у埗绾у埆锛岃浣跨敤
              浠ヤ笅鑺傜偣涔嬩竴鏇挎崲 requestedExecutionLevel 鑺傜偣銆�
 
@@ -16,38 +16,38 @@
             濡傛灉浣犵殑搴旂敤绋嬪簭闇�瑕佹铏氭嫙鍖栨潵瀹炵幇鍚戝悗鍏煎鎬э紝鍒欑Щ闄ゆ
             鍏冪礌銆�
         -->
-        <requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
-      </requestedPrivileges>
-      <applicationRequestMinimum>
-        <defaultAssemblyRequest permissionSetReference="Custom" />
-        <PermissionSet class="System.Security.PermissionSet" version="1" ID="Custom" SameSite="site" Unrestricted="true" />
-      </applicationRequestMinimum>
-    </security>
-  </trustInfo>
-  <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
-    <application>
-      <!-- 璁捐姝ゅ簲鐢ㄧ▼搴忎笌鍏朵竴璧峰伐浣滀笖宸查拡瀵规搴旂敤绋嬪簭杩涜娴嬭瘯鐨�
+				<requestedExecutionLevel level="asInvoker" uiAccess="false" />
+			</requestedPrivileges>
+			<applicationRequestMinimum>
+				<defaultAssemblyRequest permissionSetReference="Custom" />
+				<PermissionSet class="System.Security.PermissionSet" version="1" ID="Custom" SameSite="site" Unrestricted="true" />
+			</applicationRequestMinimum>
+		</security>
+	</trustInfo>
+	<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
+		<application>
+			<!-- 璁捐姝ゅ簲鐢ㄧ▼搴忎笌鍏朵竴璧峰伐浣滀笖宸查拡瀵规搴旂敤绋嬪簭杩涜娴嬭瘯鐨�
            Windows 鐗堟湰鐨勫垪琛ㄣ�傚彇娑堣瘎璁洪�傚綋鐨勫厓绱狅紝
            Windows 灏嗚嚜鍔ㄩ�夋嫨鏈�鍏煎鐨勭幆澧冦�� -->
-      <!-- Windows Vista -->
-      <!--<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}" />-->
-      <!-- Windows 7 -->
-      <!--<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />-->
-      <!-- Windows 8 -->
-      <!--<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />-->
-      <!-- Windows 8.1 -->
-      <!--<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />-->
-      <!-- Windows 10 -->
-      <!--<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />-->
-    </application>
-  </compatibility>
-  <!-- 鎸囩ず璇ュ簲鐢ㄧ▼搴忓彲鎰熺煡 DPI 涓� Windows 鍦� DPI 杈冮珮鏃跺皢涓嶄細瀵瑰叾杩涜
+			<!-- Windows Vista -->
+			<!--<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}" />-->
+			<!-- Windows 7 -->
+			<!--<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />-->
+			<!-- Windows 8 -->
+			<!--<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />-->
+			<!-- Windows 8.1 -->
+			<!--<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />-->
+			<!-- Windows 10 -->
+			<!--<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />-->
+		</application>
+	</compatibility>
+	<!-- 鎸囩ず璇ュ簲鐢ㄧ▼搴忓彲鎰熺煡 DPI 涓� Windows 鍦� DPI 杈冮珮鏃跺皢涓嶄細瀵瑰叾杩涜
        鑷姩缂╂斁銆俉indows Presentation Foundation (WPF)搴旂敤绋嬪簭鑷姩鎰熺煡 DPI锛屾棤闇�
        閫夋嫨鍔犲叆銆傞�夋嫨鍔犲叆姝よ缃殑 Windows 绐椾綋搴旂敤绋嬪簭(闈㈠悜 .NET Framework 4.6)杩樺簲
        鍦ㄥ叾 app.config 涓皢 "EnableWindowsFormsHighDpiAutoResizing" 璁剧疆璁剧疆涓� "true"銆�
        
        灏嗗簲鐢ㄧ▼搴忚涓烘劅鐭ラ暱璺緞銆傝鍙傞槄 https://docs.microsoft.com/windows/win32/fileio/maximum-file-path-limitation -->
-  <!--
+	<!--
   <application xmlns="urn:schemas-microsoft-com:asm.v3">
     <windowsSettings>
       <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
@@ -55,8 +55,8 @@
     </windowsSettings>
   </application>
   -->
-  <!-- 鍚敤 Windows 鍏叡鎺т欢鍜屽璇濇鐨勪富棰�(Windows XP 鍜屾洿楂樼増鏈�) -->
-  <!--
+	<!-- 鍚敤 Windows 鍏叡鎺т欢鍜屽璇濇鐨勪富棰�(Windows XP 鍜屾洿楂樼増鏈�) -->
+	<!--
   <dependency>
     <dependentAssembly>
       <assemblyIdentity
diff --git a/WPFProject/Libs/SuperSocket.SocketEngine.dll b/WPFProject/SuperSocket.SocketEngine.dll
similarity index 100%
rename from WPFProject/Libs/SuperSocket.SocketEngine.dll
rename to WPFProject/SuperSocket.SocketEngine.dll
Binary files differ
diff --git a/WPFProject/WPFProject.csproj b/WPFProject/WPFProject.csproj
index 0c79d61..89e92ba 100644
--- a/WPFProject/WPFProject.csproj
+++ b/WPFProject/WPFProject.csproj
@@ -64,7 +64,7 @@
     <Prefer32Bit>true</Prefer32Bit>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
-    <OutputPath>bin\x64\Release\</OutputPath>
+    <OutputPath>bin\x64\HMI\</OutputPath>
     <DefineConstants>TRACE</DefineConstants>
     <Optimize>true</Optimize>
     <DebugType>none</DebugType>
@@ -123,6 +123,7 @@
     <Reference Include="System.Configuration" />
     <Reference Include="System.Data" />
     <Reference Include="System.Drawing" />
+    <Reference Include="System.Management" />
     <Reference Include="System.Web" />
     <Reference Include="System.Web.Extensions" />
     <Reference Include="System.Windows.Forms" />
@@ -275,6 +276,9 @@
       <DependentUpon>Settings.settings</DependentUpon>
       <DesignTimeSharedInput>True</DesignTimeSharedInput>
     </Compile>
+    <Content Include="Api\Api.txt">
+      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+    </Content>
     <Content Include="Config\Put Your Config Here.txt">
       <CopyToOutputDirectory>Always</CopyToOutputDirectory>
     </Content>
@@ -284,7 +288,7 @@
     <Content Include="x64\SQLite.Interop.dll">
       <CopyToOutputDirectory>Always</CopyToOutputDirectory>
     </Content>
-    <Content Include="Libs\SuperSocket.SocketEngine.dll">
+    <Content Include="SuperSocket.SocketEngine.dll">
       <CopyToOutputDirectory>Always</CopyToOutputDirectory>
     </Content>
     <Resource Include="Icon\b1.jpg" />
@@ -297,7 +301,7 @@
     <Content Include="Icon\JingCeLogo2.png">
       <CopyToOutputDirectory>Always</CopyToOutputDirectory>
     </Content>
-    <Resource Include="BuildCmd.txt" />
+    <None Include="BuildCmd.bat" />
     <Resource Include="JingCeLogo2.ico" />
     <Resource Include="Icon\t1.png" />
     <Resource Include="Icon\t2.png" />
@@ -450,6 +454,10 @@
       <Project>{306a83dc-a7db-4057-baa7-477d8756f5d9}</Project>
       <Name>Plugin.DOControl</Name>
     </ProjectReference>
+    <ProjectReference Include="..\Plugin.JarUI\Plugin.JarUI.csproj">
+      <Project>{968fe602-22d6-42bd-981c-601ad518a896}</Project>
+      <Name>Plugin.JarUI</Name>
+    </ProjectReference>
     <ProjectReference Include="..\Plugin.ServoManage\Plugin.ServoManage.csproj">
       <Project>{1853e00f-6865-4bc6-974b-6a5e1c00bafe}</Project>
       <Name>Plugin.ServoManage</Name>
@@ -465,6 +473,18 @@
     <ProjectReference Include="..\Plugin.TsExecution\Plugin.TsExecution.csproj">
       <Project>{202d9a7b-96b3-4ad4-9977-fc4263207a02}</Project>
       <Name>Plugin.TsExecution</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\SimpleUtil\HMI.LogUtil\HMI.LogUtil.csproj">
+      <Project>{6d0aab08-0f3e-48ce-b755-9d29bc8f1225}</Project>
+      <Name>HMI.LogUtil</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\SimpleUtil\HMI.SerialData.Util\HMI.SerialData.Util.csproj">
+      <Project>{6735fdb8-b471-41c3-93cb-e8adbd0989f0}</Project>
+      <Name>HMI.SerialData.Util</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\SimpleUtil\HMI.Simple.Util\HMI.Simple.Util.csproj">
+      <Project>{cf5bebe9-2803-49c3-b773-73d082e0984a}</Project>
+      <Name>HMI.Simple.Util</Name>
     </ProjectReference>
   </ItemGroup>
   <ItemGroup>
@@ -490,6 +510,8 @@
     <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.AlarmInfoManage.dll" $(TargeDir)Plugin\HMI.AlarmInfoManage.dll"
 move /y "$(TargeDir)HMI.DataViewer.dll" $(TargeDir)Plugin\HMI.DataViewer.dll"
@@ -504,6 +526,7 @@
 move /y "$(TargeDir)Plugin.DOControl.dll" $(TargeDir)Plugin\Plugin.DOControl.dll"
 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"
 
 setlocal enabledelayedexpansion
 
@@ -514,7 +537,7 @@
 mkdir "%25destination_folder%25"
 
 for /d %25%25D in ("%25source_folder%25\*") do (
-    if /I not "%25%25~nxD"=="Config" if /I not "%25%25~nxD"=="Icon" if /I not "%25%25~nxD"=="Lang" if /I not "%25%25~nxD"=="Libs" if /I not "%25%25~nxD"=="Log" if /I not "%25%25~nxD"=="Plugin" (
+    if /I not "%25%25~nxD"=="Api" if /I not "%25%25~nxD"=="Config" if /I not "%25%25~nxD"=="Icon" if /I not "%25%25~nxD"=="Lang" if /I not "%25%25~nxD"=="Libs" if /I not "%25%25~nxD"=="Log" if /I not "%25%25~nxD"=="Plugin" if /I not "%25%25~nxD"=="TestPlatform.exe.WebView2" (
         move /Y "%25%25D" "%25destination_folder%25"
     )
 )
@@ -523,7 +546,9 @@
     if /I not "%25%25~nxF"=="$(TargetFileName)" if /I not "%25%25~nxF"=="$(TargetFileName).config" if /I not "%25%25~nxF"=="$(TargetName).pdb" if /I not "%25%25~nxF"=="clear.bat" (
         move /Y "%25%25F" "%25destination_folder%25"
     )
-)</PostBuildEvent>
+)
+
+del /s /q "%25source_folder%25\*.pdb"</PostBuildEvent>
   </PropertyGroup>
   <ProjectExtensions>
     <VisualStudio>
diff --git a/WPFProject/WPFProject.sln b/WPFProject/WPFProject.sln
index ac2ff6f..3c6d196 100644
--- a/WPFProject/WPFProject.sln
+++ b/WPFProject/WPFProject.sln
@@ -49,8 +49,6 @@
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Plugin.TestBuilder", "..\Plugin.TestBuilder\Plugin.TestBuilder.csproj", "{30201EC1-9A82-48DB-94B9-BD5C1190228B}"
 EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Plugin.SNGenerateTool", "..\Plugin.SNGenerateTool\Plugin.SNGenerateTool.csproj", "{3E18AF3F-8212-45A5-A6E3-F5B1CFA3AFAE}"
-EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Plugin.DataBoard", "..\Plugin.DataBoard\Plugin.DataBoard.csproj", "{BE42AB26-3270-43E1-84B8-6929D862FF8D}"
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HMI.Simple.Util", "..\SimpleUtil\HMI.Simple.Util\HMI.Simple.Util.csproj", "{CF5BEBE9-2803-49C3-B773-73D082E0984A}"
@@ -58,6 +56,12 @@
 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SimpleUtil", "SimpleUtil", "{02EA681E-C7D8-13C7-8484-4AC65E1B71E8}"
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HMI.SerialData.Util", "..\SimpleUtil\HMI.SerialData.Util\HMI.SerialData.Util.csproj", "{6735FDB8-B471-41C3-93CB-E8ADBD0989F0}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HMI.LogUtil", "..\SimpleUtil\HMI.LogUtil\HMI.LogUtil.csproj", "{6D0AAB08-0F3E-48CE-B755-9D29BC8F1225}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HMI.Util.Base", "..\SimpleUtil\HMI.Util.Base\HMI.Util.Base.csproj", "{D59E25D1-9B27-46A1-8438-92661DD23FAE}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Plugin.JarUI", "..\Plugin.JarUI\Plugin.JarUI.csproj", "{968FE602-22D6-42BD-981C-601AD518A896}"
 EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -251,14 +255,6 @@
 		{30201EC1-9A82-48DB-94B9-BD5C1190228B}.Release|Any CPU.Build.0 = Release|Any CPU
 		{30201EC1-9A82-48DB-94B9-BD5C1190228B}.Release|x64.ActiveCfg = Release|Any CPU
 		{30201EC1-9A82-48DB-94B9-BD5C1190228B}.Release|x64.Build.0 = Release|Any CPU
-		{3E18AF3F-8212-45A5-A6E3-F5B1CFA3AFAE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{3E18AF3F-8212-45A5-A6E3-F5B1CFA3AFAE}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{3E18AF3F-8212-45A5-A6E3-F5B1CFA3AFAE}.Debug|x64.ActiveCfg = Debug|Any CPU
-		{3E18AF3F-8212-45A5-A6E3-F5B1CFA3AFAE}.Debug|x64.Build.0 = Debug|Any CPU
-		{3E18AF3F-8212-45A5-A6E3-F5B1CFA3AFAE}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{3E18AF3F-8212-45A5-A6E3-F5B1CFA3AFAE}.Release|Any CPU.Build.0 = Release|Any CPU
-		{3E18AF3F-8212-45A5-A6E3-F5B1CFA3AFAE}.Release|x64.ActiveCfg = Release|Any CPU
-		{3E18AF3F-8212-45A5-A6E3-F5B1CFA3AFAE}.Release|x64.Build.0 = Release|Any CPU
 		{BE42AB26-3270-43E1-84B8-6929D862FF8D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{BE42AB26-3270-43E1-84B8-6929D862FF8D}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{BE42AB26-3270-43E1-84B8-6929D862FF8D}.Debug|x64.ActiveCfg = Debug|Any CPU
@@ -277,12 +273,36 @@
 		{CF5BEBE9-2803-49C3-B773-73D082E0984A}.Release|x64.Build.0 = Release|Any CPU
 		{6735FDB8-B471-41C3-93CB-E8ADBD0989F0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{6735FDB8-B471-41C3-93CB-E8ADBD0989F0}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{6735FDB8-B471-41C3-93CB-E8ADBD0989F0}.Debug|x64.ActiveCfg = Debug|Any CPU
-		{6735FDB8-B471-41C3-93CB-E8ADBD0989F0}.Debug|x64.Build.0 = Debug|Any CPU
+		{6735FDB8-B471-41C3-93CB-E8ADBD0989F0}.Debug|x64.ActiveCfg = Debug|x64
+		{6735FDB8-B471-41C3-93CB-E8ADBD0989F0}.Debug|x64.Build.0 = Debug|x64
 		{6735FDB8-B471-41C3-93CB-E8ADBD0989F0}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{6735FDB8-B471-41C3-93CB-E8ADBD0989F0}.Release|Any CPU.Build.0 = Release|Any CPU
 		{6735FDB8-B471-41C3-93CB-E8ADBD0989F0}.Release|x64.ActiveCfg = Release|Any CPU
 		{6735FDB8-B471-41C3-93CB-E8ADBD0989F0}.Release|x64.Build.0 = Release|Any CPU
+		{6D0AAB08-0F3E-48CE-B755-9D29BC8F1225}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{6D0AAB08-0F3E-48CE-B755-9D29BC8F1225}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{6D0AAB08-0F3E-48CE-B755-9D29BC8F1225}.Debug|x64.ActiveCfg = Debug|Any CPU
+		{6D0AAB08-0F3E-48CE-B755-9D29BC8F1225}.Debug|x64.Build.0 = Debug|Any CPU
+		{6D0AAB08-0F3E-48CE-B755-9D29BC8F1225}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{6D0AAB08-0F3E-48CE-B755-9D29BC8F1225}.Release|Any CPU.Build.0 = Release|Any CPU
+		{6D0AAB08-0F3E-48CE-B755-9D29BC8F1225}.Release|x64.ActiveCfg = Release|Any CPU
+		{6D0AAB08-0F3E-48CE-B755-9D29BC8F1225}.Release|x64.Build.0 = Release|Any CPU
+		{D59E25D1-9B27-46A1-8438-92661DD23FAE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{D59E25D1-9B27-46A1-8438-92661DD23FAE}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{D59E25D1-9B27-46A1-8438-92661DD23FAE}.Debug|x64.ActiveCfg = Debug|Any CPU
+		{D59E25D1-9B27-46A1-8438-92661DD23FAE}.Debug|x64.Build.0 = Debug|Any CPU
+		{D59E25D1-9B27-46A1-8438-92661DD23FAE}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{D59E25D1-9B27-46A1-8438-92661DD23FAE}.Release|Any CPU.Build.0 = Release|Any CPU
+		{D59E25D1-9B27-46A1-8438-92661DD23FAE}.Release|x64.ActiveCfg = Release|Any CPU
+		{D59E25D1-9B27-46A1-8438-92661DD23FAE}.Release|x64.Build.0 = Release|Any CPU
+		{968FE602-22D6-42BD-981C-601AD518A896}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{968FE602-22D6-42BD-981C-601AD518A896}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{968FE602-22D6-42BD-981C-601AD518A896}.Debug|x64.ActiveCfg = Debug|Any CPU
+		{968FE602-22D6-42BD-981C-601AD518A896}.Debug|x64.Build.0 = Debug|Any CPU
+		{968FE602-22D6-42BD-981C-601AD518A896}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{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
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
@@ -290,6 +310,8 @@
 	GlobalSection(NestedProjects) = preSolution
 		{CF5BEBE9-2803-49C3-B773-73D082E0984A} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
 		{6735FDB8-B471-41C3-93CB-E8ADBD0989F0} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
+		{6D0AAB08-0F3E-48CE-B755-9D29BC8F1225} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
+		{D59E25D1-9B27-46A1-8438-92661DD23FAE} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
 	EndGlobalSection
 	GlobalSection(ExtensibilityGlobals) = postSolution
 		SolutionGuid = {784CEA1D-A890-4625-B0C1-72294A6B4803}
diff --git a/WPFProject/Window1.xaml.cs b/WPFProject/Window1.xaml.cs
index 2f22f18..313aad7 100644
--- a/WPFProject/Window1.xaml.cs
+++ b/WPFProject/Window1.xaml.cs
@@ -20,7 +20,6 @@
     /// </summary>
     public partial class Window1 : Window
     {
-        private LoadingWindow loading;
         public Window1(LoadingWindow loading)
         {
             InitializeComponent();

--
Gitblit v1.9.1