From c401a2f2fd1a509cdeb33e850bb5c2827074af48 Mon Sep 17 00:00:00 2001
From: alielie <alielie@git.com>
Date: 星期一, 17 三月 2025 08:44:51 +0800
Subject: [PATCH] 完善框架,新增两个close方法;

---
 Plugin.TestResult/TestResultView.xaml.cs     |   42 +
 Plugin.TsExecution/Plugin.TsExecution.csproj |    4 
 WPFProject/Utils/BinUtil.cs                  |   58 ++
 WPFProject/Lang/zh-CN/app.json               |    4 
 WPFProject/Lang/en-US/TestExecution.json     |    1 
 HMI.MenuManage/MenuManage.xaml.cs            |   13 
 HMI.Core/Setting/SystemSetting.cs            |    5 
 WPFProject/LoadingWindow.xaml.cs             |   14 
 Plugin.TestResult/InfoTable.xaml.cs          |   12 
 WPFProject/PanuonWindow.xaml.cs              |   96 ++++
 HMI.Core/Util/LangUtil.cs                    |    1 
 Plugin.TestResult/ViewControl.xaml.cs        |   52 ++
 HMI.MenuManage/RoleManage.xaml.cs            |   11 
 HMI.Core/Util/JsonUtil.cs                    |   21 
 WPFProject/Utils/Encryptor.cs                |   63 +++
 WPFProject/Lang/zh-CN/ParamManage.json       |    1 
 Plugin.TsExecution/TsExecution.xaml.cs       |  128 +++---
 Plugin.TsExecution/TsExecution.xaml          |    2 
 WPFProject/App.xaml                          |    1 
 WPFProject/Lang/en-US/ParamManage.json       |   17 
 HMI.UI/Util/MsgBoxHelper.cs                  |    2 
 HMI.MenuManage/UserManage.xaml.cs            |   12 
 HMI.Core/Entity/UserInfo.cs                  |    6 
 HMI.Base/PluginControl.cs                    |   13 
 HMI.Core/Api/IPlugin.cs                      |   19 
 HMI.Core/HMI.Core.csproj                     |    1 
 HMI.Base/PluginLoader.cs                     |   70 --
 HMI.UI/Resources/Font/iconfont.json          |   28 +
 WPFProject/WPFProject.csproj                 |   19 
 WPFProject/Login.xaml.cs                     |   70 ++
 WPFProject/TextFile1.txt                     |    7 
 HMI.UI/Style/PanuonResource.xaml             |    4 
 WPFProject/PanuonWindow.xaml                 |   35 +
 WPFProject/Libs/SuperSocket.SocketEngine.dll |    0 
 Plugin.TsExecution/SingleControl.xaml        |   15 
 WPFProject/WPFProject.sln                    |    4 
 HMI.MenuManage/Modal/IconSelector.xaml.cs    |   11 
 Plugin.TestResult/Plugin.TestResult.csproj   |    7 
 Plugin.TsExecution/SingleControl.xaml.cs     |   50 +
 WPFProject/Lang/zh-CN/TestExecution.json     |    1 
 WPFProject/Login.xaml                        |    6 
 HMI.Core/Api/IPluginContainer.cs             |   12 
 HMI.Util/HMI.Util.csproj                     |   18 
 HMI.UI/Resources/Font/iconfont.ttf           |    0 
 WPFProject/App.xaml.cs                       |   30 +
 Plugin.TestResult/SingleCell.xaml.cs         |   52 +-
 Plugin.TestResult/ViewControl.xaml           |    3 
 WPFProject/Lang/en-US/app.json               |    9 
 Plugin.TestResult/DataTable.xaml.cs          |  118 +++++
 HMI.UI/HMI.UI.csproj                         |    2 
 50 files changed, 898 insertions(+), 272 deletions(-)

diff --git a/HMI.Base/PluginControl.cs b/HMI.Base/PluginControl.cs
index b8b307b..d3fee33 100644
--- a/HMI.Base/PluginControl.cs
+++ b/HMI.Base/PluginControl.cs
@@ -1,4 +1,5 @@
-锘縰sing System.ComponentModel;
+锘縰sing System;
+using System.ComponentModel;
 using System.Runtime.CompilerServices;
 using System.Windows.Controls;
 using HMI.Core.Api;
@@ -64,5 +65,15 @@
         {
             
         }
+
+        public virtual void OnClose(object sender, CancelEventArgs e)
+        {
+           
+        }
+
+        public virtual void DoClose(object sender, EventArgs e)
+        {
+
+        }
     }
 }
diff --git a/HMI.Base/PluginLoader.cs b/HMI.Base/PluginLoader.cs
index eef52ba..cf98bd3 100644
--- a/HMI.Base/PluginLoader.cs
+++ b/HMI.Base/PluginLoader.cs
@@ -44,6 +44,14 @@
             sqliteHelper = new SqliteHelper(PathHelper.DatabasePath);
         }
 
+        public static void Reset()
+        {
+            if (Instance != null)
+            {
+                Instance = null;
+            }
+        }
+
         public static PluginLoader GetLoader()
         {
             if (Instance == null)
@@ -53,79 +61,29 @@
             return Instance;
         }
 
-        public void LoadMenuAndPlugin(Dispatcher dispatcher)
+        private IPluginContainer pluginContainer;
+        public void LoadMenuAndPlugin(IPluginContainer container, Dispatcher dispatcher)
         {
+            pluginContainer = container;
             if (Menus == null)
             {
                 Plugins = new List<PluginInfo>();
                 Menus = LoadTreeMenu(dispatcher);
+                sqliteHelper.Close();
                 dispatcher.Invoke(() =>
                 {
                     LoadMessageAppend?.Invoke(this, new LoadMsg { Type = LoadMsgType.FINISHED });
                 });
             }
         }
-
-        private void LoadChildrenMenu(PluginInfo menu, Dispatcher dispatcher)
-        {
-            LoadMessageAppend?.Invoke(this, new LoadMsg
-            {
-                Type = LoadMsgType.LOADING,
-                Message = $"姝e湪鍔犺浇{menu.Name}鑿滃崟..."
-            });
-            try
-            {
-                Application.Current.Dispatcher.Invoke(() =>
-                {
-                    if (!string.IsNullOrEmpty(menu.DllPath))
-                    {
-                        string dllPath = Path.Combine(PluginFolder, menu.DllPath);
-                        Assembly ass = Assembly.LoadFrom(dllPath);
-                        Type ClassType = ass.GetType(menu.ClassName);
-                        object Instance = Activator.CreateInstance(ClassType);
-                        menu.Content = Instance;
-                        if (Instance is IPlugin plugin)
-                        {
-                            plugin.SetLogger(LogManager.GetLogger(menu.Code));
-                            string configFolder = Path.Combine(PathHelper.ConfigFolder, menu.Code);
-                            if (!Directory.Exists(configFolder))
-                            {
-                                Directory.CreateDirectory(configFolder);
-                            }
-                            plugin.SetBaseFolder(configFolder);
-                            plugin.InitFinish();
-                        }
-                        Type SettingType = ass.GetType($"{menu.ClassName}_Setting");
-                        if (SettingType != null)
-                        {
-                            object SettingInstance = Activator.CreateInstance(SettingType);
-                            menu.SettingControl = SettingInstance;
-                        }
-                    }
-                });
-                Thread.Sleep(100);
-            }
-            catch (Exception ex)
-            {
-                LoadMessageAppend?.Invoke(this,
-                     new LoadMsg
-                     {
-                         Type = LoadMsgType.LOADING,
-                         Result = false,
-                         Message = $"{ex.Message} {ex.StackTrace}"
-                     });
-            }
-        }
-
         private void LoadChildrenMenu()
         {
             foreach (var menu in Plugins)
             {
                 CountdownEvent c = new CountdownEvent(1);
-                Thread.Sleep(100);
+                Thread.Sleep(80);
                 Application.Current.Dispatcher.Invoke(new Action(() =>
                 {
-
                     try
                     {
                         LoadMessageAppend?.Invoke(this,
@@ -152,6 +110,8 @@
                                 }
                                 plugin.SetBaseFolder(configFolder);
                                 plugin.InitFinish();
+                                pluginContainer.OnCloseEvent += plugin.OnClose;
+                                pluginContainer.DoCloseEvent += plugin.DoClose;
                                 Type SettingType = ass.GetType($"{menu.ClassName}_Setting");
                                 if (SettingType != null)
                                 {
diff --git a/HMI.Core/Api/IPlugin.cs b/HMI.Core/Api/IPlugin.cs
index c4cc74d..3739a4e 100644
--- a/HMI.Core/Api/IPlugin.cs
+++ b/HMI.Core/Api/IPlugin.cs
@@ -1,4 +1,6 @@
 锘縰sing log4net;
+using System;
+using System.ComponentModel;
 
 namespace HMI.Core.Api
 {
@@ -10,12 +12,17 @@
         /// <returns></returns>
         object Init();
 
+        /// <summary>
+        /// 鍒濆鍖栧畬鎴愶紙鎵ц瀹屾瀯閫犲櫒锛�
+        /// </summary>
         void InitFinish();
+
         /// <summary>
         /// 鎻掍欢闈㈡澘琚縺娲绘椂鐨勬搷浣�
         /// </summary>
         /// <returns></returns>
         object Active();
+
         /// <summary>
         /// 澶辨椿鏃剁殑鎿嶄綔
         /// </summary>
@@ -30,5 +37,17 @@
         void OnTabChange(object sender, object args);
 
         void SetLogger(ILog logger);
+
+        /// <summary>
+        /// 灏嗚鍏抽棴绋嬪簭浜嬩欢
+        /// </summary>
+        void OnClose(object sender, CancelEventArgs e);
+
+        /// <summary>
+        /// 鍏抽棴绋嬪簭浜嬩欢
+        /// </summary>
+        /// <param name="sender"></param>
+        /// <param name="e"></param>
+        void DoClose(object sender, EventArgs e);
     }
 }
diff --git a/HMI.Core/Api/IPluginContainer.cs b/HMI.Core/Api/IPluginContainer.cs
new file mode 100644
index 0000000..ec509e6
--- /dev/null
+++ b/HMI.Core/Api/IPluginContainer.cs
@@ -0,0 +1,12 @@
+锘縰sing System;
+using System.ComponentModel;
+
+namespace HMI.Core.Api
+{
+    public interface IPluginContainer
+    {
+        event CancelEventHandler OnCloseEvent;
+        event EventHandler DoCloseEvent;
+        event EventHandler LoadComplateEvent;
+    }
+}
diff --git a/HMI.Core/Entity/UserInfo.cs b/HMI.Core/Entity/UserInfo.cs
index 1407e73..6357850 100644
--- a/HMI.Core/Entity/UserInfo.cs
+++ b/HMI.Core/Entity/UserInfo.cs
@@ -2,17 +2,19 @@
 {
     public class UserInfo
     {
-        public static UserInfo User { get; private set; } = new UserInfo("", "admin", "");
-        public UserInfo(string id, string username, string name)
+        public static UserInfo User { get; private set; }
+        public UserInfo(string id, string username, string name, string password = null)
         {
             Id = id;
             Username = username;
             Name = name;
+            Password = password;
         }
 
         public string Id { get; private set; }
         public string Username { get; private set; }
         public string Name { get; private set; }
+        public string Password { get; private set; }
 
         public static void Login(UserInfo info)
         {
diff --git a/HMI.Core/HMI.Core.csproj b/HMI.Core/HMI.Core.csproj
index 183dbdb..d309f71 100644
--- a/HMI.Core/HMI.Core.csproj
+++ b/HMI.Core/HMI.Core.csproj
@@ -90,6 +90,7 @@
     <Compile Include="Api\IConfigRefresh.cs" />
     <Compile Include="Api\IFolderOwner.cs" />
     <Compile Include="Api\IPlugin.cs" />
+    <Compile Include="Api\IPluginContainer.cs" />
     <Compile Include="Api\IProgram.cs" />
     <Compile Include="Api\ISequnce.cs" />
     <Compile Include="Api\IStep.cs" />
diff --git a/HMI.Core/Setting/SystemSetting.cs b/HMI.Core/Setting/SystemSetting.cs
index 37257fb..da6a63b 100644
--- a/HMI.Core/Setting/SystemSetting.cs
+++ b/HMI.Core/Setting/SystemSetting.cs
@@ -47,6 +47,11 @@
             }
         }
 
+        public static SystemSettingModel GetModel()
+        {
+            return XmlHelper.DeserializeFromXml<SystemSettingModel>(FilePath);
+        }
+
         public static void ReLoad()
         {
             if (!File.Exists(FilePath))
diff --git a/HMI.Core/Util/JsonUtil.cs b/HMI.Core/Util/JsonUtil.cs
index 11c3141..c2def8e 100644
--- a/HMI.Core/Util/JsonUtil.cs
+++ b/HMI.Core/Util/JsonUtil.cs
@@ -1,11 +1,26 @@
 锘縰sing Newtonsoft.Json;
-using Newtonsoft.Json.Linq;
-using System.IO;
 
 namespace HMI.Core.Util
 {
     public class JsonUtil
     {
-       
+        static readonly JsonSerializerSettings JsSetting = new JsonSerializerSettings()
+        {
+            NullValueHandling = NullValueHandling.Ignore
+        };
+        public static T Deserialize<T>(string jsonStr)
+        {
+            return JsonConvert.DeserializeObject<T>(jsonStr);
+        }
+
+        public static string Serialize(object obj)
+        {
+            return JsonConvert.SerializeObject(obj);
+        }
+
+        public static string Stringfiy(object obj)
+        {
+            return JsonConvert.SerializeObject(obj, Formatting.Indented, JsSetting);
+        }
     }
 }
diff --git a/HMI.Core/Util/LangUtil.cs b/HMI.Core/Util/LangUtil.cs
index a204034..64c004c 100644
--- a/HMI.Core/Util/LangUtil.cs
+++ b/HMI.Core/Util/LangUtil.cs
@@ -6,7 +6,6 @@
 using System.Collections.Generic;
 using System.Globalization;
 using System.IO;
-using System.Runtime.InteropServices.ComTypes;
 using System.Windows;
 using System.Windows.Markup;
 
diff --git a/HMI.MenuManage/MenuManage.xaml.cs b/HMI.MenuManage/MenuManage.xaml.cs
index 7ed3438..fa38a3a 100644
--- a/HMI.MenuManage/MenuManage.xaml.cs
+++ b/HMI.MenuManage/MenuManage.xaml.cs
@@ -5,6 +5,7 @@
 using HMI.UI.Util;
 using System;
 using System.Collections.Generic;
+using System.ComponentModel;
 using System.Linq;
 using System.Windows;
 using System.Windows.Controls;
@@ -133,6 +134,18 @@
             }
         }
 
+        public override void DoClose(object sender, EventArgs e)
+        {
+            try
+            {
+                Model?.sqliteHelper?.Close();
+            }
+            catch (Exception ex)
+            {
+                log.Warn("Do Close Failed.", ex);
+            }
+        }
+
         private void DataGrid_Loaded(object sender, RoutedEventArgs e)
         {
             Model.LoadData();
diff --git a/HMI.MenuManage/Modal/IconSelector.xaml.cs b/HMI.MenuManage/Modal/IconSelector.xaml.cs
index 4d18399..f1e5609 100644
--- a/HMI.MenuManage/Modal/IconSelector.xaml.cs
+++ b/HMI.MenuManage/Modal/IconSelector.xaml.cs
@@ -1,22 +1,13 @@
-锘縰sing HMI.Core.Annotation;
-using HMI.Core.Util;
-using HMI.MenuManage.Entity;
-using HMI.UI.DpUtil;
-using Microsoft.Win32;
+锘縰sing HMI.MenuManage.Entity;
 using Newtonsoft.Json;
-using Newtonsoft.Json.Linq;
 using Panuon.WPF.UI;
 using System;
 using System.Collections.Generic;
-using System.Collections.ObjectModel;
 using System.ComponentModel;
 using System.Data;
 using System.IO;
 using System.Linq;
 using System.Reflection;
-using System.Windows;
-using System.Windows.Controls;
-using System.Xml.Linq;
 
 namespace HMI.MenuManage
 {
diff --git a/HMI.MenuManage/RoleManage.xaml.cs b/HMI.MenuManage/RoleManage.xaml.cs
index 2b7efc9..2de1f01 100644
--- a/HMI.MenuManage/RoleManage.xaml.cs
+++ b/HMI.MenuManage/RoleManage.xaml.cs
@@ -35,6 +35,17 @@
             this.Loaded -= RoleManage_Loaded;
         }
 
+        public override void DoClose(object sender, EventArgs e)
+        {
+            try
+            {
+                Model?.sqliteHelper?.Close();
+            }
+            catch (Exception ex)
+            {
+                log.Warn("Do Close Failed.", ex);
+            }
+        }
         private void Button_Click(object sender, RoutedEventArgs e)
         {
             try
diff --git a/HMI.MenuManage/UserManage.xaml.cs b/HMI.MenuManage/UserManage.xaml.cs
index ceceba8..44a6dde 100644
--- a/HMI.MenuManage/UserManage.xaml.cs
+++ b/HMI.MenuManage/UserManage.xaml.cs
@@ -32,6 +32,18 @@
             this.Loaded -= UserManage_Loaded;
         }
 
+        public override void DoClose(object sender, EventArgs e)
+        {
+            try
+            {
+                Model?.sqliteHelper?.Close();
+            }
+            catch (Exception ex)
+            {
+                log.Warn("Do Close Failed.", ex);
+            }
+        }
+
         private void Button_Click(object sender, RoutedEventArgs e)
         {
             Button btn = (Button)sender;
diff --git a/HMI.UI/HMI.UI.csproj b/HMI.UI/HMI.UI.csproj
index 704497e..4f9d89f 100644
--- a/HMI.UI/HMI.UI.csproj
+++ b/HMI.UI/HMI.UI.csproj
@@ -161,7 +161,7 @@
       <Generator>SettingsSingleFileGenerator</Generator>
       <LastGenOutput>Settings.Designer.cs</LastGenOutput>
     </None>
-    <Resource Include="Resources\Font\iconfont.json" />
+    <EmbeddedResource Include="Resources\Font\iconfont.json" />
     <Resource Include="Resources\Font\iconfont.ttf" />
   </ItemGroup>
   <ItemGroup>
diff --git a/HMI.UI/Resources/Font/iconfont.json b/HMI.UI/Resources/Font/iconfont.json
index 97a8666..6056704 100644
--- a/HMI.UI/Resources/Font/iconfont.json
+++ b/HMI.UI/Resources/Font/iconfont.json
@@ -6,6 +6,34 @@
   "description": "",
   "glyphs": [
     {
+      "icon_id": "1376055",
+      "name": "閫�鍑虹櫥褰�",
+      "font_class": "tuichudenglu",
+      "unicode": "e61d",
+      "unicode_decimal": 58909
+    },
+    {
+      "icon_id": "7095289",
+      "name": "閲嶅惎",
+      "font_class": "zhongqi",
+      "unicode": "e867",
+      "unicode_decimal": 59495
+    },
+    {
+      "icon_id": "30661341",
+      "name": "PLC淇℃伅绠$悊",
+      "font_class": "a-PLCinformationmanagement",
+      "unicode": "e63e",
+      "unicode_decimal": 58942
+    },
+    {
+      "icon_id": "12889884",
+      "name": "绯荤粺绠$悊-鑿滃崟绠$悊",
+      "font_class": "xitongguanli-caidanguanli",
+      "unicode": "e628",
+      "unicode_decimal": 58920
+    },
+    {
       "icon_id": "12939974",
       "name": "缂栬緫",
       "font_class": "bianji1",
diff --git a/HMI.UI/Resources/Font/iconfont.ttf b/HMI.UI/Resources/Font/iconfont.ttf
index 89b2e50..e7b61af 100644
--- a/HMI.UI/Resources/Font/iconfont.ttf
+++ b/HMI.UI/Resources/Font/iconfont.ttf
Binary files differ
diff --git a/HMI.UI/Style/PanuonResource.xaml b/HMI.UI/Style/PanuonResource.xaml
index 01337d5..b587af8 100644
--- a/HMI.UI/Style/PanuonResource.xaml
+++ b/HMI.UI/Style/PanuonResource.xaml
@@ -281,9 +281,9 @@
         <Setter Property="pu:WindowXCaption.Buttons" Value="Close"/>
         <Setter Property="pu:WindowXModalDialog.Buttons" Value="OKCancel"/>
         <Setter Property="pu:WindowXModalDialog.CancelButtonContent" 
-                Value="{Binding Converter={StaticResource lc},ConverterParameter='App.Cancel'}"/>
+                Value="{DynamicResource App.Cancel}"/>
         <Setter Property="pu:WindowXModalDialog.OKButtonContent" 
-                Value="{Binding Converter={StaticResource lc},ConverterParameter='App.Save'}"/>
+                Value="{DynamicResource App.Save}"/>
         <Setter Property="pu:WindowXModalDialog.ButtonPanelMargin" Value="0,30,10,10"/>
     </Style>
 </ResourceDictionary>
\ No newline at end of file
diff --git a/HMI.UI/Util/MsgBoxHelper.cs b/HMI.UI/Util/MsgBoxHelper.cs
index 8c01e3f..6d5d49d 100644
--- a/HMI.UI/Util/MsgBoxHelper.cs
+++ b/HMI.UI/Util/MsgBoxHelper.cs
@@ -71,7 +71,7 @@
             MessageBoxResult res = MessageBoxResult.None;
             if (!(owner is Window window))
             {
-                window = Window.GetWindow(owner);
+                window = Window.GetWindow(owner) ?? Application.Current.MainWindow;
             }
             window.Dispatcher.Invoke(() =>
             {
diff --git a/HMI.Util/HMI.Util.csproj b/HMI.Util/HMI.Util.csproj
index b50268a..6c81f83 100644
--- a/HMI.Util/HMI.Util.csproj
+++ b/HMI.Util/HMI.Util.csproj
@@ -30,6 +30,24 @@
     <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="ICSharpCode.SharpZipLib, Version=1.0.0.999, Culture=neutral, PublicKeyToken=1b03e6acf1164f73, processorArchitecture=MSIL">
       <HintPath>..\WPFProject\packages\SharpZipLib.1.0.0\lib\net45\ICSharpCode.SharpZipLib.dll</HintPath>
diff --git a/Plugin.TestResult/DataTable.xaml.cs b/Plugin.TestResult/DataTable.xaml.cs
index 884fc6c..aa7f621 100644
--- a/Plugin.TestResult/DataTable.xaml.cs
+++ b/Plugin.TestResult/DataTable.xaml.cs
@@ -1,4 +1,5 @@
 锘縰sing HMI.Core.Util;
+using HMI.Util;
 using Plugin.TestResult_WPF.Model;
 using SuperSocket.SocketBase.Protocol;
 using System;
@@ -73,9 +74,13 @@
         private SingleCell[] cells;
 
         private TcpServer tcpServer;
+        private string CountIniPath;
 
-        public DataTable(TestResultView parent,int index, string title, BaseConfig config)
+        public DataTable(TestResultView parent, int index, string title,
+            string countIniPath,
+            BaseConfig config)
         {
+            this.CountIniPath = countIniPath;
             this.config = config;
             this.index = index;
             this.parent = parent;
@@ -86,8 +91,9 @@
             DataContext = this;
             InitializeComponent();
             this.chart = new Chart(this.chartTitle, false, config.MaxSize, config.RefreshInterval);
-            this.multiChart = new Chart(config.DetailChartTitle, true, config.MaxSize, config.RefreshInterval) { 
-            BorderThickness = new Thickness(0)
+            this.multiChart = new Chart(config.DetailChartTitle, true, config.MaxSize, config.RefreshInterval)
+            {
+                BorderThickness = new Thickness(0)
             };
             this.grid.Children.Add(this.chart);
             Grid.SetRow(this.chart, 2);
@@ -99,6 +105,7 @@
                 mainGrid.Children.Add(cell);
                 cells[i] = cell;
             }
+            ReadCount();
         }
 
         public int GetSize()
@@ -196,6 +203,9 @@
             multiChart.Clear();
         }
 
+        /// <summary>
+        /// 鍚姩Tcp鏈嶅姟鍣�
+        /// </summary>
         private void StartTcpServer()
         {
             try
@@ -204,6 +214,20 @@
                 {
                     tcpServer = new TcpServer();
                     tcpServer.NewRequestReceived += TcpServer_NewRequestReceived;
+                    if (!tcpServer.Setup(config.PortStart + index))
+                    {
+                        ChangeTcpServerStatus("ERROR", this.lang("Tr.TcpServer.SettingFailed"));
+                        return;
+                    }
+                    if (!tcpServer.Start())
+                    {
+                        ChangeTcpServerStatus("ERROR", this.lang("Tr.TcpServer.StartFailed"));
+                        return;
+                    }
+                    ChangeTcpServerStatus("RUNNING", this.lang("Tr.TcpServer.Running"));
+                }
+                else if (tcpServer.State != SuperSocket.SocketBase.ServerState.Running)
+                {
                     if (!tcpServer.Setup(config.PortStart + index))
                     {
                         ChangeTcpServerStatus("ERROR", this.lang("Tr.TcpServer.SettingFailed"));
@@ -225,20 +249,11 @@
             }
         }
 
-        private void StopTcpServer()
-        {
-            if (tcpServer == null) return;
-            try
-            {
-                tcpServer.Stop();
-                tcpServer = null;
-            }
-            catch (Exception ex)
-            {
-
-            }
-        }
-
+        /// <summary>
+        /// 鏇存柊Tcp鏈嶅姟鍣ㄧ姸鎬�
+        /// </summary>
+        /// <param name="status"></param>
+        /// <param name="info"></param>
         private void ChangeTcpServerStatus(string status, string info)
         {
             Dispatcher.Invoke(() =>
@@ -248,6 +263,35 @@
             });
         }
 
+        /// <summary>
+        /// 鍋滄Tcp鏈嶅姟鍣�
+        /// </summary>
+        private void StopTcpServer()
+        {
+            try
+            {
+                tcpServer?.Stop();
+                tcpServer = null;
+            }
+            catch (Exception ex)
+            {
+
+            }
+        }
+
+        /// <summary>
+        /// 閲婃斁璧勬簮
+        /// </summary>
+        public void RelaseResource()
+        {
+            StopTcpServer();
+        }
+
+        /// <summary>
+        /// Tcp娑堟伅澶勭悊
+        /// </summary>
+        /// <param name="session"></param>
+        /// <param name="requestInfo"></param>
         private void TcpServer_NewRequestReceived(TcpSession session, StringRequestInfo requestInfo)
         {
             string msg = requestInfo.Body;
@@ -268,7 +312,45 @@
                 condition = values[4],
                 updateTime = values[5]
             });
-            session.Send("1");
+            //session.Send("1");
+        }
+
+        public void RefreshCount(int ok, int ng)
+        {
+            infoTable.UpdateNumber(infoTable.okCount + ok, infoTable.ngCount + ng);
+            IniFileHelper.WriteIniData($"Oven_{index}", "Pass", infoTable.okCount.ToString(),
+                this.CountIniPath);
+            IniFileHelper.WriteIniData($"Oven_{index}", "Fail", infoTable.ngCount.ToString(),
+                this.CountIniPath);
+        }
+
+        /// <summary>
+        /// 璇诲彇淇濆瓨鐨勬暟鎹�
+        /// </summary>
+        public void ReadCount()
+        {
+            try
+            {
+                int ok = Convert.ToInt32(IniFileHelper.ReadIniData($"Oven_{index}", "Pass", "", CountIniPath));
+                int ng = Convert.ToInt32(IniFileHelper.ReadIniData($"Oven_{index}", "Fail", "", CountIniPath));
+                infoTable.UpdateNumber(ok, ng);
+            }
+            catch (Exception ex)
+            {
+                //do nothing
+            }
+        }
+
+        /// <summary>
+        /// 璁℃暟娓呴浂
+        /// </summary>
+        public void ClearNumber()
+        {
+            infoTable.UpdateNumber(0, 0);
+            IniFileHelper.WriteIniData($"Oven_{index}", "Pass", infoTable.okCount.ToString(),
+                this.CountIniPath);
+            IniFileHelper.WriteIniData($"Oven_{index}", "Fail", infoTable.ngCount.ToString(),
+                this.CountIniPath);
         }
 
         private void UserControl_Loaded(object sender, RoutedEventArgs e)
diff --git a/Plugin.TestResult/InfoTable.xaml.cs b/Plugin.TestResult/InfoTable.xaml.cs
index 4bff3b1..b2a7f26 100644
--- a/Plugin.TestResult/InfoTable.xaml.cs
+++ b/Plugin.TestResult/InfoTable.xaml.cs
@@ -1,4 +1,5 @@
-锘縰sing Plugin.TestResult_WPF.Constant;
+锘縰sing HMI.Util;
+using Plugin.TestResult_WPF.Constant;
 using System;
 using System.ComponentModel;
 using System.Runtime.CompilerServices;
@@ -66,7 +67,6 @@
             set
             {
                 RaiseAndSetIfchanged(ref _okCount, value);
-                RefreshCount();
             }
         }
 
@@ -78,7 +78,6 @@
             set
             {
                 RaiseAndSetIfchanged(ref _ngCount, value);
-                RefreshCount();
             }
         }
 
@@ -137,10 +136,11 @@
             set => RaiseAndSetIfchanged(ref _endTimeStr, value);
         }
 
-
-        private void RefreshCount()
+        public void UpdateNumber(int ok, int ng)
         {
-            this.allCount = this.okCount + this.ngCount;
+            this._okCount = ok;
+            this._ngCount = ng;
+            this._allCount = this._okCount + this._ngCount;
             RaisePropertyChanged("countStr");
         }
 
diff --git a/Plugin.TestResult/Plugin.TestResult.csproj b/Plugin.TestResult/Plugin.TestResult.csproj
index 349c3a6..404f92f 100644
--- a/Plugin.TestResult/Plugin.TestResult.csproj
+++ b/Plugin.TestResult/Plugin.TestResult.csproj
@@ -52,9 +52,6 @@
     <ErrorReport>prompt</ErrorReport>
   </PropertyGroup>
   <ItemGroup>
-    <Reference Include="CommonLibs">
-      <HintPath>..\Libs\CommonLibs.dll</HintPath>
-    </Reference>
     <Reference Include="GLWpfControl, Version=3.3.0.0, Culture=neutral, processorArchitecture=MSIL">
       <HintPath>..\WPFProject\packages\OpenTK.GLWpfControl.3.3.0\lib\net452\GLWpfControl.dll</HintPath>
     </Reference>
@@ -317,6 +314,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" />
   <Import Project="..\WPFProject\packages\HarfBuzzSharp.NativeAssets.macOS.7.3.0.3\build\net462\HarfBuzzSharp.NativeAssets.macOS.targets" Condition="Exists('..\WPFProject\packages\HarfBuzzSharp.NativeAssets.macOS.7.3.0.3\build\net462\HarfBuzzSharp.NativeAssets.macOS.targets')" />
diff --git a/Plugin.TestResult/SingleCell.xaml.cs b/Plugin.TestResult/SingleCell.xaml.cs
index 60b55c7..981c6c9 100644
--- a/Plugin.TestResult/SingleCell.xaml.cs
+++ b/Plugin.TestResult/SingleCell.xaml.cs
@@ -145,31 +145,34 @@
             if (window != null) window.UpdateSource(this.infos);
         }
 
-        public void SetInfos(string[,] infos)
-        {
-            var data = new ObservableCollection<Info>();
-            for (int i = 0; i < infos.GetLength(0); i++)
-            {
-                data.Add(new Info
-                {
-                    step = infos[i, 0],
-                    result = infos[i, 1]
-                });
-            }
-            this.infos = data;
-            if (window != null) window.UpdateSource(this.infos);
-        }
-
         public void SetInfos(Info info)
         {
             if (queue.Count >= maxSize)
             {
                 queue.TryDequeue(out var first);
+                queue.Enqueue(info);
+                this.infos = new ObservableCollection<Info>(queue);
+                if (window != null) window.UpdateSource(this.infos);
             }
-            queue.Enqueue(info);
-            var list = new ObservableCollection<Info>(queue);
-            this.infos = list;
-            if (window != null) window.UpdateSource(this.infos);
+            else
+            {
+                queue.Enqueue(info);
+                if (window != null)
+                {
+                    window.Dispatcher.Invoke(() =>
+                    {
+                        this.infos.Add(info);
+                    });
+                }
+                else
+                {
+                    Dispatcher.Invoke(() =>
+                    {
+                        this.infos.Add(info);
+                    });
+                }
+                return;
+            }
         }
 
         public void ClearInfo()
@@ -180,16 +183,5 @@
             if (window != null) window.UpdateSource(this.infos);
         }
 
-        public void SetInfos(string step, string result)
-        {
-            if (queue.Count >= maxSize)
-            {
-                queue.TryDequeue(out var info);
-            }
-            queue.Enqueue(new Info() { step = step, result = result });
-            var list = new ObservableCollection<Info>(queue);
-            this.infos = list;
-            if (window != null) window.UpdateSource(this.infos);
-        }
     }
 }
diff --git a/Plugin.TestResult/TestResultView.xaml.cs b/Plugin.TestResult/TestResultView.xaml.cs
index 8c6cd63..c1d5f03 100644
--- a/Plugin.TestResult/TestResultView.xaml.cs
+++ b/Plugin.TestResult/TestResultView.xaml.cs
@@ -72,7 +72,7 @@
             ((System.ComponentModel.ISupportInitialize)(this.testApp)).BeginInit();//myClient涓虹涓夋柟鎺т欢鍚�
             host.Child = testApp;
             ((System.ComponentModel.ISupportInitialize)(this.testApp)).EndInit();
-            InitMainGrid();
+
             //InitTestStandEngine();
             //StartTestSequence();
         }
@@ -106,11 +106,21 @@
 
                     var cell = table.GetSingleCell(random.Next(table.GetSize()));
                     cell.SetStatus(CommonStatus.GetRandomStatus());
-                    cell.SetInfos(random.Next(1000) + "", random.Next(1000) + "");
+                    cell.SetInfos(new Info
+                    {
+                        step = random.Next(1000) + "",
+                        value = random.Next(1000) + ""
+                    });
                     table.SetStatusText(DateTime.Now + "");
                 };
                 sb.Start();
             }
+        }
+
+        private string CountIniPath;
+        public void SetCountIniPath(string countIniPath)
+        {
+            CountIniPath = countIniPath;
         }
 
         public void InitMainGrid()
@@ -131,7 +141,7 @@
             for (int n = 0; n < cellCount; n++)
             {
                 string name = n < names.Length ? names[n] : "";
-                DataTable child = new DataTable(this, n, name, config);
+                DataTable child = new DataTable(this, n, name, CountIniPath, config);
                 mainGrid.Children.Add(child);
                 tables[n] = child;
                 nameTables[name] = child;
@@ -319,6 +329,16 @@
                     int cellIndex = int.Parse(datas_10015[1]);
                     string status_10015 = datas_10015[2];
                     tables[ovenIndex].GetSingleCell(cellIndex).SetStatus(status_10015);
+
+                    if (status_10015 == "OK" || status_10015 == "ok")
+                    {
+                        tables[ovenIndex].RefreshCount(1, 0);
+                    }
+                    else if (status_10015 == "ng" || status_10015 == "NG")
+                    {
+                        tables[ovenIndex].RefreshCount(0, 1);
+                    }
+
                     return;
                 }
                 if (MethodCode == 7)
@@ -381,9 +401,23 @@
             //bool val = this.SequenceContext.StationGlobals.GetValBoolean(key, 0);
         }
 
-        public void SetSnrToSeqContext(int index, string snr)
+        public void ReleaseResource()
         {
+            foreach (var table in tables)
+            {
+                table.RelaseResource();
+            }
+        }
 
+        /// <summary>
+        /// 璁℃暟娓呴浂
+        /// </summary>
+        public void ClearNumber()
+        {
+            foreach (var table in tables)
+            {
+                table.ClearNumber();
+            }
         }
     }
 }
diff --git a/Plugin.TestResult/ViewControl.xaml b/Plugin.TestResult/ViewControl.xaml
index d87530e..7778d89 100644
--- a/Plugin.TestResult/ViewControl.xaml
+++ b/Plugin.TestResult/ViewControl.xaml
@@ -100,6 +100,9 @@
                     <Button x:Name="btnStop" Click="Stop_Click" Style="{StaticResource DangerButton}"
                             Content="{DynamicResource App.Stop}"/>
 
+                    <Button x:Name="btnClear" Click="BtnClear_Click" Style="{StaticResource PrimaryButton}"
+                            Content="{DynamicResource Ts.BtnClearNum}"/>
+
                     <Button x:Name="btnScan1" Click="Scan_Click_1" Style="{StaticResource PrimaryButton}"
                             Content="{DynamicResource Tr.Zone1Scan}"/>
 
diff --git a/Plugin.TestResult/ViewControl.xaml.cs b/Plugin.TestResult/ViewControl.xaml.cs
index be38c85..ef1b83e 100644
--- a/Plugin.TestResult/ViewControl.xaml.cs
+++ b/Plugin.TestResult/ViewControl.xaml.cs
@@ -2,6 +2,7 @@
 using HMI.Core.Util;
 using HMI.ParameterManage.Entity;
 using HMI.ParameterManage.Util;
+using HMI.UI;
 using HMI.UI.Util;
 using log4net;
 using NationalInstruments.TestStand.Interop.API;
@@ -10,6 +11,8 @@
 using Plugin.TestResult_WPF.Util;
 using System;
 using System.Collections.Generic;
+using System.ComponentModel;
+using System.IO;
 using System.Linq;
 using System.Windows;
 using System.Windows.Controls;
@@ -70,6 +73,21 @@
             testResultView.testApp.ExecutionClosed += new _ApplicationMgrEvents_ExecutionClosedEventHandler(this.axApplicationMgr_ExecutionClosed);
         }
 
+        public override void InitFinish()
+        {
+            base.InitFinish();
+            string path = Path.Combine(BaseFolder, "Count.ini");
+            testResultView.SetCountIniPath(path);
+            testResultView.InitMainGrid();
+            LoadProduct();
+            testResultView.InitTestStandEngine();
+        }
+
+        public override void DoClose(object sender, EventArgs e)
+        {
+            testResultView.ReleaseResource();
+        }
+
         private void axApplicationMgr_ExitApplication(object sender, EventArgs e)
         {
         }
@@ -116,17 +134,31 @@
             { }
         }
 
+        private bool isLoaded;
         private void UserControl_Loaded(object sender, RoutedEventArgs e)
         {
-            LoadProduct();
-            testResultView.InitTestStandEngine();
-            Start_Click(null, null);
-            Loaded -= UserControl_Loaded;
+            new MaskWindow(Window.GetWindow(this),
+                (win) =>
+                {
+                    Dispatcher.Invoke(() =>
+                    {
+                        try
+                        {
+                            Start_Click(null, null);
+                            Loaded -= UserControl_Loaded;
+                        }
+                        finally
+                        {
+                            win.Close();
+                        }
+                    });
+                }).Info(LangUtil.Get("Ts.SequenceIsStarting"));
         }
 
         public void LoadProduct()
         {
-            List<ProductData> products = ParameterManager.GetProducts<List<ProductData>>();
+            List<ProductData> products = ParameterManager.GetProducts<List<ProductData>>()
+                ?? new List<ProductData>();
             this.modelSelect.Items.Clear();
             foreach (var prd in products)
             {
@@ -270,6 +302,16 @@
         {
         }
 
+        /// <summary>
+        /// 璁℃暟娓呴浂鎸夐挳浜嬩欢
+        /// </summary>
+        /// <param name="sender"></param>
+        /// <param name="e"></param>
+        private void BtnClear_Click(object sender, RoutedEventArgs e)
+        {
+            testResultView.ClearNumber();
+        }
+
 
     }
 }
diff --git a/Plugin.TsExecution/Plugin.TsExecution.csproj b/Plugin.TsExecution/Plugin.TsExecution.csproj
index 10b8b98..4e19d52 100644
--- a/Plugin.TsExecution/Plugin.TsExecution.csproj
+++ b/Plugin.TsExecution/Plugin.TsExecution.csproj
@@ -196,6 +196,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/Plugin.TsExecution/SingleControl.xaml b/Plugin.TsExecution/SingleControl.xaml
index cf3ad24..bea0f07 100644
--- a/Plugin.TsExecution/SingleControl.xaml
+++ b/Plugin.TsExecution/SingleControl.xaml
@@ -29,6 +29,8 @@
             <RowDefinition Height="Auto"/>
             <RowDefinition Height="Auto"/>
             <RowDefinition/>
+            <RowDefinition Height="Auto"/>
+            <RowDefinition Height="Auto"/>
         </Grid.RowDefinitions>
 
         <Grid dp:GridHelper.InnerBorder="1" dp:GridHelper.Space="5" 
@@ -181,5 +183,18 @@
                 </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" 
+                   VerticalContentAlignment="Center"/>
+        </Grid>
+        <DockPanel Grid.Row="4">
+            <StatusBar>
+                <StatusBarItem>
+                    <Label Padding="0" VerticalContentAlignment="Center" Content="{Binding CurrentStep}"/>
+                </StatusBarItem>
+            </StatusBar>
+        </DockPanel>
+
     </Grid>
 </UserControl>
diff --git a/Plugin.TsExecution/SingleControl.xaml.cs b/Plugin.TsExecution/SingleControl.xaml.cs
index 9367bb2..2dc2d8e 100644
--- a/Plugin.TsExecution/SingleControl.xaml.cs
+++ b/Plugin.TsExecution/SingleControl.xaml.cs
@@ -1,4 +1,5 @@
 锘縰sing HMI.Core.Util;
+using HMI.Util;
 using NationalInstruments.TestStand.Interop.API;
 using System;
 using System.ComponentModel;
@@ -35,7 +36,7 @@
         #endregion
         private bool isLoadOK = false;
         private int index;
-        private string counterFilePath;
+        private string CountIniPath;
 
         private string status = "Ready";
 
@@ -89,13 +90,6 @@
             get => this.lang("Ts.TestInfo", Barcode, ElapseTime);
         }
 
-        private string currentStep;
-        public string CurrentStep
-        {
-            get { return currentStep; }
-            set { Set(ref currentStep, value); }
-        }
-
         private string barcode = string.Empty;
         public string Barcode
         {
@@ -133,8 +127,19 @@
 
         private DataTable dt;
 
-        public SingleControl(int Index, int passNum, int failNum)
+        private string currentStep = "绛夊緟鎵爜";
+        public string CurrentStep
         {
+            get => currentStep;
+            set
+            {
+                Set(ref currentStep, value);
+            }
+        }
+
+        public SingleControl(int Index, string CountIniPath)
+        {
+            this.CountIniPath = CountIniPath;
             DataContext = this;
             this.index = Index;
             PassNum = passNum;
@@ -219,13 +224,13 @@
         /// <param name="ProcessVal"></param>
         public void SetTestProcess(int ProcessVal)
         {
-            //this.TestProcess.Value = ProcessVal;
-            //this.TestProcessLabel.Text = ProcessVal.ToString() + "%";
-            //this.TestProcessLabel.BackColor = Color.Transparent;
+            this.testProcess.Value = ProcessVal;
+            this.testProcessLabel.Content = ProcessVal.ToString() + "%";
         }
 
         private void UserControl_Loaded(object sender, RoutedEventArgs e)
         {
+            ReadCounter();
             this.UpdateTestCounter(null);
         }
 
@@ -255,14 +260,31 @@
 
         private void WriteCounter()
         {
+            IniFileHelper.WriteIniData("Socket" + this.index.ToString(), "Pass",
+                this.passNum.ToString(), this.CountIniPath);
+            IniFileHelper.WriteIniData("Socket" + this.index.ToString(), "Fail", this.failNum.ToString(),
+                this.CountIniPath);
+        }
 
+        private void ReadCounter()
+        {
+            try
+            {
+                this.passNum = Convert.ToInt32(IniFileHelper.ReadIniData("Socket" + this.index.ToString(), "Pass", "", CountIniPath));
+                this.failNum = Convert.ToInt32(IniFileHelper.ReadIniData("Socket" + this.index.ToString(), "Fail", "", CountIniPath));
+            }
+            catch (Exception ex)
+            {
+                //do nothing
+            }
         }
 
         public void ResetCounter()
         {
-            PassNum = 0;
-            FailNum = 0;
+            this.passNum = 0;
+            this.failNum = 0;
             this.UpdateTestCounter(null);
         }
+
     }
 }
diff --git a/Plugin.TsExecution/TsExecution.xaml b/Plugin.TsExecution/TsExecution.xaml
index ced5ec4..4472a40 100644
--- a/Plugin.TsExecution/TsExecution.xaml
+++ b/Plugin.TsExecution/TsExecution.xaml
@@ -24,7 +24,7 @@
         <WindowsFormsHost x:Name="host" Visibility="Collapsed"></WindowsFormsHost>
         <Border>
             <UniformGrid x:Name="panel" Columns="2" dp:UniformGridHelper.InnerBorder="1" 
-                         dp:UniformGridHelper.InnerBorderBrush="{StaticResource CommonBoarder}">
+                         dp:UniformGridHelper.InnerBorderBrush="#3a3a3a">
 
             </UniformGrid>
         </Border>
diff --git a/Plugin.TsExecution/TsExecution.xaml.cs b/Plugin.TsExecution/TsExecution.xaml.cs
index df139b3..4e71b42 100644
--- a/Plugin.TsExecution/TsExecution.xaml.cs
+++ b/Plugin.TsExecution/TsExecution.xaml.cs
@@ -5,6 +5,7 @@
 using HMI.ParameterManage.Util;
 using HMI.UI;
 using HMI.UI.Util;
+using HMI.Util;
 using NationalInstruments.TestStand.Interop.API;
 using NationalInstruments.TestStand.Interop.UI.Ax;
 using Panuon.WPF.UI;
@@ -14,6 +15,7 @@
 using System;
 using System.Collections.Generic;
 using System.Collections.ObjectModel;
+using System.ComponentModel;
 using System.Drawing;
 using System.IO;
 using System.Linq;
@@ -101,6 +103,8 @@
             }
         }
 
+        private Pie pie;
+        private string CountIniPath;
 
         public TsExecution()
         {
@@ -111,63 +115,29 @@
             InitTsEngine();
             //鍒濆鍖朤estStand寮曟搸
             this.InitTestStandEngine();
+
+        }
+
+        public override void SetBaseFolder(string folder)
+        {
+            CountIniPath = Path.Combine(folder, "Count.ini");
+            base.SetBaseFolder(folder);
+        }
+
+        public override void InitFinish()
+        {
             this.ReadCount(out int pass, out int fail);
             this.DisplayCount(pass, fail);
             UpdateChart(pass, fail);
             InitProducts();
+            base.InitFinish();
         }
 
-        private Pie pie;
         /// <summary>
         /// 鍒濆鍖朩inform鍥捐〃
         /// </summary>
         private void InitChart()
         {
-            //ChartArea chartArea1 = new ChartArea();
-            //Legend legend1 = new Legend();
-            //Series series1 = new Series();
-            //DataPoint dataPoint1 = new DataPoint(0D, 10D);
-            //DataPoint dataPoint2 = new DataPoint(0D, 10D);
-
-            //chart = new Chart();
-            //// 
-            //// chart
-            //// 
-            //this.chart.BackColor = System.Drawing.SystemColors.Control;
-            //chartArea1.BackColor = System.Drawing.SystemColors.Control;
-            //chartArea1.Name = "ChartArea1";
-            //this.chart.ChartAreas.Add(chartArea1);
-            //this.chart.Dock = System.Windows.Forms.DockStyle.Fill;
-            //legend1.BackColor = System.Drawing.SystemColors.Control;
-            //legend1.Name = "Legend1";
-            //this.chart.Legends.Add(legend1);
-            //this.chart.Location = new System.Drawing.Point(0, 0);
-            //this.chart.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
-            //this.chart.Name = "chart";
-            //this.chart.Palette = ChartColorPalette.None;
-            //series1.BorderColor = System.Drawing.SystemColors.Control;
-            //series1.ChartArea = "ChartArea1";
-            //series1.ChartType = SeriesChartType.Pie;
-            //series1.Color = System.Drawing.SystemColors.Control;
-            //series1.LabelBackColor = System.Drawing.Color.White;
-            //series1.Legend = "Legend1";
-            //series1.Name = "Series1";
-            //dataPoint1.AxisLabel = "OK";
-            //dataPoint1.Color = Color.Green;
-            //dataPoint1.IsVisibleInLegend = true;
-            //dataPoint1.Label = "";
-            //dataPoint2.AxisLabel = "NG";
-            //dataPoint2.Color = Color.FromArgb(230, 0, 18);
-            //series1.Points.Add(dataPoint1);
-            //series1.Points.Add(dataPoint2);
-            //this.chart.Series.Add(series1);
-            //this.chart.Size = new System.Drawing.Size(215, 191);
-            //this.chart.TabIndex = 0;
-            //this.chart.Text = "chart1";
-            //((System.ComponentModel.ISupportInitialize)(chart)).BeginInit();
-            //chartHost.Child = chart;
-            //((System.ComponentModel.ISupportInitialize)(chart)).EndInit();
-
             pieChart.Plot.Axes.Frameless();
             pieChart.Plot.HideGrid();
             pieChart.Plot.DataBackground.Color = Colors.Transparent;
@@ -274,7 +244,7 @@
                 //閲嶆柊鐢熸垚瀛愰潰鏉�
                 for (int i = 0; i < this.socketNum; i++)
                 {
-                    SingleControl control = new SingleControl(i, 0, 0);
+                    SingleControl control = new SingleControl(i, CountIniPath);
                     panel.Children.Add(control);
                     tsSockets.Add(control);
                 }
@@ -308,14 +278,14 @@
         /// <param name="fail">涓嶅悎鏍兼暟</param>
         private void ReadCount(out int pass, out int fail)
         {
-            pass = 0; //Convert.ToInt32(OperateIniFile.ReadIniData("Count", "Pass", "", CountiniPath));
-            fail = 0; //Convert.ToInt32(OperateIniFile.ReadIniData("Count", "Fail", "", CountiniPath));
+            pass = Convert.ToInt32(IniFileHelper.ReadIniData("Count", "Pass", "", CountIniPath));
+            fail = Convert.ToInt32(IniFileHelper.ReadIniData("Count", "Fail", "", CountIniPath));
         }
 
         private void WriteCount(int pass, int fail)
         {
-            //OperateIniFile.WriteIniData("Count", "Pass", pass.ToString(), CountiniPath);
-            //OperateIniFile.WriteIniData("Count", "Fail", fail.ToString(), CountiniPath);
+            IniFileHelper.WriteIniData("Count", "Pass", pass.ToString(), CountIniPath);
+            IniFileHelper.WriteIniData("Count", "Fail", fail.ToString(), CountIniPath);
         }
 
         /// <summary>
@@ -400,6 +370,26 @@
             }
         }
 
+        public void StopSequenceClick()
+        {
+            new MaskWindow((WindowX)Window.GetWindow(this),
+                    (win) =>
+                    {
+                        Dispatcher.Invoke(() =>
+                        {
+                            try
+                            {
+                                StopSequence();
+                            }
+                            finally
+                            {
+                                win.Close();
+                            }
+                        });
+                    }
+                    ).Info(LangUtil.Get("App.SequenceIsStopping"));
+        }
+
         /// <summary>
         /// 鍋滄娴嬭瘯搴忓垪
         /// </summary>
@@ -407,15 +397,14 @@
         {
             try
             {
-                if (this.tsSockets.Count == 0)
-                    return;
-                if (this.tsSockets[0].SequenceFileContext == null)
-                    return;
-                bool val = this.tsSockets[0].SequenceFileContext.StationGlobals.Exists("StationState.StopTest", 0);
-                if (val)
+                if (this.tsSockets.Count > 0 && this.tsSockets[0]?.SequenceFileContext != null)
                 {
-                    tsSockets[0].SequenceFileContext.StationGlobals.SetValBoolean("StationState.StopTest", 0, true);
-                    bool isStop = this.execution.WaitForEndEx(2000, true, null, null);
+                    bool val = this.tsSockets[0].SequenceFileContext.StationGlobals.Exists("StationState.StopTest", 0);
+                    if (val)
+                    {
+                        tsSockets[0].SequenceFileContext.StationGlobals.SetValBoolean("StationState.StopTest", 0, true);
+                        bool isStop = this.execution.WaitForEndEx(2000, true, null, null);
+                    }
                 }
                 this.CbProduct.IsEnabled = true;
                 this.BtnStartSeq.IsEnabled = true;
@@ -435,10 +424,9 @@
             {
                 case "BtnStartSeq":
                     StartClick();
-
                     break;
                 case "BtnStopSeq":
-                    StopSequence();
+                    StopSequenceClick();
                     break;
                 case "BtnClearNum":
                     ClearNum();
@@ -627,13 +615,6 @@
             pie.Slices[0].Value = pass;
             pie.Slices[1].Value = fail;
             pieChart.Refresh();
-
-            //Dispatcher.Invoke(() => {
-            //    ((System.ComponentModel.ISupportInitialize)(chart)).BeginInit();
-            //    chart.Series[0].Points[0].YValues[0] = pass;
-            //    chart.Series[0].Points[1].YValues[0] = fail;
-            //    ((System.ComponentModel.ISupportInitialize)(chart)).EndInit();
-            //});
         }
 
         private void SetTestSeqFile(ProductData product)
@@ -659,6 +640,17 @@
             }
         }
 
+        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);
+        }
+
         private void PluginControl_SizeChanged(object sender, SizeChangedEventArgs e)
         {
 
diff --git a/WPFProject/App.xaml b/WPFProject/App.xaml
index 940c25c..74e1851 100644
--- a/WPFProject/App.xaml
+++ b/WPFProject/App.xaml
@@ -6,6 +6,7 @@
              xmlns:pu="https://opensource.panuon.com/wpf-ui"
              xmlns:uic="clr-namespace:HMI.UI.Converter;assembly=HMI.UI"
              xmlns:hc="clr-namespace:HMI.Core;assembly=HMI.Core"
+             ShutdownMode="OnExplicitShutdown"
              Startup="Application_Startup">
     <Application.Resources>
         <ResourceDictionary>
diff --git a/WPFProject/App.xaml.cs b/WPFProject/App.xaml.cs
index 8a44641..cfb8c29 100644
--- a/WPFProject/App.xaml.cs
+++ b/WPFProject/App.xaml.cs
@@ -1,8 +1,10 @@
-锘縰sing HMI.Core.Util;
+锘縰sing HMI.Core.Setting;
+using HMI.Core.Util;
 using HMI.UI.Util;
 using log4net;
 using Panuon.WPF.UI;
 using System;
+using System.Threading;
 using System.Windows;
 using WPFProject.Utils;
 
@@ -29,13 +31,29 @@
                     MessageBoxIcon.Error, DefaultButton.YesOK, 5);
         }
 
+        private static Mutex mutex;
         private void Application_Startup(object sender, StartupEventArgs e)
         {
-            LangUtil.Load();
-            ToastHelper.Register(new MainToastHandler());
-            LoadingWindow window = new LoadingWindow();
-            //Login window = new Login();
-            window.Show();
+            mutex = new System.Threading.Mutex(true, "TestPlatform_OnlyRun_CRNS");
+            if (mutex.WaitOne(0, false))
+            {
+                LangUtil.Load();
+                SystemSetting.SystemSettingChanged += SystemSetting_SystemSettingChanged;
+                ToastHelper.Register(new MainToastHandler());
+                //LoadingWindow window = new LoadingWindow();
+                Login window = new Login();
+                window.Show();
+            }
+            else
+            {
+                MessageBox.Show("绋嬪簭宸茬粡鍦ㄨ繍琛岋紒", "鎻愮ず");
+                this.Shutdown();
+            }
+        }
+
+        private void SystemSetting_SystemSettingChanged(object sender, SystemSetting e)
+        {
+            LangUtil.LoadLang();
         }
     }
 }
diff --git a/WPFProject/Lang/en-US/ParamManage.json b/WPFProject/Lang/en-US/ParamManage.json
index 092f078..08fab99 100644
--- a/WPFProject/Lang/en-US/ParamManage.json
+++ b/WPFProject/Lang/en-US/ParamManage.json
@@ -10,9 +10,24 @@
   "Pm.Parameter.Name": "Name",
   "Pm.Parameter.Value": "Value",
   "Pm.Parameter.Unit": "Unit",
+  "Pm.Parameter.Type": "Type",
   "Pm.Parameter.Desc": "Description",
   "Pm.Parameter.Group": "Group",
+  "Pm.Parameter.Source": "Source",
   "Pm.Parameter.Add": "New parameter",
   "Pm.Parameter.Edit": "Edit parameter",
-  "Pm.PleaseSelectProduct": "Please select a product"
+  "Pm.PleaseSelectProduct": "Please select a product",
+
+  "Pm.Setting.Title": "Parameter manage",
+
+  "Pm.Ti.Title": "Test item manage",
+  "Pm.Ti.Name": "Name",
+  "Pm.Ti.Type": "Type",
+  "Pm.Ti.Shineng": "Enable",
+  "Pm.Ti.LimitDown": "Limit down",
+  "Pm.Ti.LimitUp": "Limit up",
+  "Pm.Ti.StrValue": "String",
+  "Pm.Ti.Offset": "Offset",
+  "Pm.Ti.Source": "Source",
+  "Pm.Ti.PleaseSelectProduct": "Please select a product"
 }
\ No newline at end of file
diff --git a/WPFProject/Lang/en-US/TestExecution.json b/WPFProject/Lang/en-US/TestExecution.json
index c895f05..4f54ed0 100644
--- a/WPFProject/Lang/en-US/TestExecution.json
+++ b/WPFProject/Lang/en-US/TestExecution.json
@@ -17,6 +17,7 @@
   "Ts.StopSequenceFail": "Stop sequence failed",
   "Ts.MakeSureClearNum": "Are you sure you want to clear the number锛�",
   "Ts.SequenceIsStarting": "Sequence is starting...",
+  "Ts.SequenceIsStopping": "Sequence is stopping...",
   "Ts.CurrentSnr": "Current serial",
   "Ts.TimeSpan": "Elapsed time"
 }
\ No newline at end of file
diff --git a/WPFProject/Lang/en-US/app.json b/WPFProject/Lang/en-US/app.json
index eb4e147..4d1da20 100644
--- a/WPFProject/Lang/en-US/app.json
+++ b/WPFProject/Lang/en-US/app.json
@@ -98,6 +98,10 @@
   "App.MenuLoadSuccess": "{0} is loaded!",
   "App.MenuLoadFailed": "{0} load failed: {1} {2}",
   "App.MenuCount": "There are {0} menus is loading...",
+  "App.SelectIcon": "Select icon",
+  "App.Welcome": "Welcom! {0}",
+  "App.MakeSureLogout": "Are you sure you want to logout?",
+  "App.MakeSureRestartApp": "Are you sure you want to restart锛�",
 
 
   "HMI.NoDeleteData": "Please select items you want to delete",
@@ -135,5 +139,8 @@
   "HMI.Role.Name": "Name",
   "HMI.Role.Permission": "Permission",
   "HMI.Role.Add": "New role",
-  "HMI.Role.Edit": "Edit role"
+  "HMI.Role.Edit": "Edit role",
+
+  "HMI.Line": "Line",
+  "HMI.Station": "Station"
 }
\ No newline at end of file
diff --git a/WPFProject/Lang/zh-CN/ParamManage.json b/WPFProject/Lang/zh-CN/ParamManage.json
index 8f0519a..23cf1c2 100644
--- a/WPFProject/Lang/zh-CN/ParamManage.json
+++ b/WPFProject/Lang/zh-CN/ParamManage.json
@@ -18,7 +18,6 @@
   "Pm.Parameter.Edit": "缂栬緫鍙傛暟",
   "Pm.PleaseSelectProduct": "璇峰厛閫夋嫨涓�椤逛骇鍝�",
 
-
   "Pm.Setting.Title": "鍙傛暟绠$悊",
 
   "Pm.Ti.Title": "娴嬭瘯椤圭鐞�",
diff --git a/WPFProject/Lang/zh-CN/TestExecution.json b/WPFProject/Lang/zh-CN/TestExecution.json
index dcf9319..2999d7b 100644
--- a/WPFProject/Lang/zh-CN/TestExecution.json
+++ b/WPFProject/Lang/zh-CN/TestExecution.json
@@ -17,6 +17,7 @@
   "Ts.StopSequenceFail": "鍋滄娴嬭瘯搴忓垪澶辫触",
   "Ts.MakeSureClearNum": "纭畾瑕佹竻绌鸿鏁板悧锛�",
   "Ts.SequenceIsStarting": "姝e湪鍚姩娴嬭瘯搴忓垪...",
+  "Ts.SequenceIsStopping": "姝e湪鍋滄娴嬭瘯搴忓垪...",
   "Ts.CurrentSnr": "褰撳墠搴忓垪鍙�",
   "Ts.TimeSpan": "娴嬭瘯鑰楁椂"
 }
\ No newline at end of file
diff --git a/WPFProject/Lang/zh-CN/app.json b/WPFProject/Lang/zh-CN/app.json
index dcf2132..deacc3e 100644
--- a/WPFProject/Lang/zh-CN/app.json
+++ b/WPFProject/Lang/zh-CN/app.json
@@ -98,6 +98,10 @@
   "App.MenuLoadSuccess": "{0}鍔犺浇瀹屾垚!",
   "App.MenuLoadFailed": "{0}鍔犺浇澶辫触: {1} {2}",
   "App.MenuCount": "鍏辨湁{0}鏉¤彍鍗曞緟鍔犺浇...",
+  "App.SelectIcon": "閫夋嫨鍥炬爣",
+  "App.Welcome": "娆㈣繋鎮紝{0}",
+  "App.MakeSureLogout": "纭畾瑕侀��鍑虹櫥闄嗗悧锛�",
+  "App.MakeSureRestartApp": "纭畾瑕侀噸鏂板惎鍔ㄥ悧锛�",
 
 
   "HMI.NoDeleteData": "璇烽�夋嫨瑕佸垹闄ょ殑鏁版嵁",
diff --git a/WPFProject/Libs/SuperSocket.SocketEngine.dll b/WPFProject/Libs/SuperSocket.SocketEngine.dll
new file mode 100644
index 0000000..a7ae3dc
--- /dev/null
+++ b/WPFProject/Libs/SuperSocket.SocketEngine.dll
Binary files differ
diff --git a/WPFProject/LoadingWindow.xaml.cs b/WPFProject/LoadingWindow.xaml.cs
index e9d50a4..e0c4fc1 100644
--- a/WPFProject/LoadingWindow.xaml.cs
+++ b/WPFProject/LoadingWindow.xaml.cs
@@ -1,4 +1,5 @@
 锘縰sing HMI.Base;
+using HMI.Core.Api;
 using HMI.UI.Util;
 using Panuon.WPF.UI;
 using System;
@@ -37,7 +38,8 @@
             }
         }
 
-        public Visibility ConsoleVisiable {
+        public Visibility ConsoleVisiable
+        {
             get => showConsole ? Visibility.Visible : Visibility.Collapsed;
         }
 
@@ -65,7 +67,7 @@
                     Add(msg.Message, null);
                     break;
                 case LoadMsgType.LOADING:
-                    HasErr = msg.Result == false;
+                    HasErr = HasErr || msg.Result == false;
                     if (msg.Result != null)
                     {
                         processBar.Value = ++finished / total * 100;
@@ -83,21 +85,21 @@
 
         private void Button_Click(object sender, RoutedEventArgs e)
         {
-            PanuonWindow window = new PanuonWindow();
             window.Show();
             this.Close();
         }
-
+        private PanuonWindow window;
         private double total;
         private double finished;
 
         private void WindowX_ContentRendered(object sender, System.EventArgs e)
         {
+            window = new PanuonWindow();
             PluginLoader loader = PluginLoader.GetLoader();
             loader.LoadMessageAppend += Loader_LoadMessageAppend;
             Task.Run(() =>
             {
-                loader.LoadMenuAndPlugin(Dispatcher);
+                loader.LoadMenuAndPlugin(window, Dispatcher);
                 //
             });
         }
@@ -132,7 +134,7 @@
             };
             //涔熷彲浠ユ彃鍏ュ埌鏈�鍚庨潰
             Console.Document.Blocks.InsertAfter(Console.Document.Blocks.LastBlock, paragraph);
-            
+
             //鎻掑叆鍒版渶鍓嶉潰
             //rtxtLog.Document.Blocks.Add(paragraph);
 
diff --git a/WPFProject/Login.xaml b/WPFProject/Login.xaml
index d1b05a0..f656f1f 100644
--- a/WPFProject/Login.xaml
+++ b/WPFProject/Login.xaml
@@ -13,7 +13,10 @@
             Height="420"
             Width="380"
             pu:WindowXCaption.Background="Transparent"
+            WindowStartupLocation="CenterScreen"
             Background="{StaticResource VerBar}"
+            Closed="window_Closed"
+            ContentRendered="WindowX_ContentRendered"
             pu:WindowXCaption.Buttons="Close">
     <Window.Resources>
         <ResourceDictionary>
@@ -124,10 +127,13 @@
         <pu:Switch Grid.Row="3"
                HorizontalAlignment="Right"
                Margin="0,25,0,0"
+                   x:Name="SwRememberMe"
                    Cursor="Hand"
+                  Unchecked="SwRememberMe_Unchecked"
                Foreground="{StaticResource PrimaryColorContract}"
                Style="{StaticResource RememberMeSwitchStyle}"
                Content="{DynamicResource App.RememberMe}" />
+        
         <Button Grid.Row="4"
             Margin="0,20,0,0"
             Height="40"
diff --git a/WPFProject/Login.xaml.cs b/WPFProject/Login.xaml.cs
index 4999c8c..bf04d2c 100644
--- a/WPFProject/Login.xaml.cs
+++ b/WPFProject/Login.xaml.cs
@@ -1,4 +1,5 @@
 锘縰sing HMI.Core.Entity;
+using HMI.Core.Setting;
 using HMI.Core.Util;
 using HMI.UI.DpUtil;
 using HMI.UI.Util;
@@ -6,14 +7,17 @@
 using Panuon.WPF.UI;
 using System;
 using System.Data;
+using System.IO;
 using System.Threading.Tasks;
 using System.Windows;
+using WPFProject.Utils;
+using static System.Windows.Forms.VisualStyles.VisualStyleElement.StartPanel;
 
 namespace WPFProject
 {
     public class LoginException : Exception
     {
-        public LoginException(string message):base(message)
+        public LoginException(string message) : base(message)
         {
         }
     }
@@ -30,6 +34,9 @@
             InitializeComponent();
         }
 
+        private static readonly Encryptor encryptor = new Encryptor("aB3dE5fG7hI9jjK1lMnOpQrS2tUvWxYz",
+            "8Wq9RsT4vYx6ZuB1");
+
         private void Button_Click(object sender, RoutedEventArgs e)
         {
             if (FormGroupHelper.ValidateForm(formPanel))
@@ -37,29 +44,22 @@
                 ButtonHelper.SetIsPending(BtnLogin, true);
                 string username = TbAccount.Text;
                 string password = PwdPassword.Password;
+                bool rememberMe = SwRememberMe.IsChecked == true;
                 Task.Run(() =>
                 {
-                    DoLogin(username, password);
+                    DoLogin(username, password, rememberMe);
                 });
             }
-
-            //try
-            //{
-
-            //}
-            //finally {
-            //    ButtonHelper.SetIsPending(BtnLogin, false);
-            //}
-
         }
 
-        private void DoLogin(string username, string password)
+        private void DoLogin(string username, string password, bool rememberMe)
         {
             SqliteHelper helper = null;
+
             try
             {
                 helper = new SqliteHelper(PathHelper.DatabasePath);
-                string sql = $"SELECT id,username,name From user WHERE username='{username}' and password='{password}'";
+                string sql = $"SELECT id,username,password,name From user WHERE username='{username}' and password='{password}'";
                 DataTable dt = helper.GetDataTable(sql);
                 if (dt == null || dt.Rows.Count <= 0)
                 {
@@ -70,10 +70,22 @@
                     dt.Rows[0]["username"].ToString(),
                     dt.Rows[0]["name"].ToString());
                 UserInfo.Login(user);
+                if (rememberMe)
+                {
+                    // 璁颁綇鐧诲綍鎿嶄綔
+                    string userToken =
+                        JsonUtil.Serialize(
+                            new UserInfo(null, user.Username, user.Name, dt.Rows[0]["password"].ToString()));
+                    // 瀛樺偍鏁版嵁
+                    BinUtil.SaveStrToBin(encryptor.Encrypt(userToken),
+                        Path.Combine(PathHelper.DatabaseFolder, "user.bin"));
+                }
+
                 Dispatcher.Invoke(() =>
                 {
                     LoadingWindow loading = new LoadingWindow();
                     loading.Show();
+                    this.Closed -= window_Closed;
                     this.Close();
                 });
             }
@@ -93,5 +105,37 @@
                 });
             }
         }
+
+        /// <summary>
+        /// 娓叉煋瀹屾垚鍚庯紝灏濊瘯鑷姩鐧诲綍
+        /// </summary>
+        /// <param name="sender"></param>
+        /// <param name="e"></param>
+        private void WindowX_ContentRendered(object sender, EventArgs e)
+        {
+            string userToken = string.Empty;
+            if (BinUtil.LoadStrFromBin(ref userToken, Path.Combine(PathHelper.DatabaseFolder, "user.bin")))
+            {
+                userToken = encryptor.Decrypt(userToken);
+                UserInfo userInfo = JsonUtil.Deserialize<UserInfo>(userToken);
+                TbAccount.Text = userInfo.Username;
+                PwdPassword.Password = userInfo.Password;
+                SwRememberMe.IsChecked = true;
+                Button_Click(null, null);
+            }
+        }
+        private void window_Closed(object sender, EventArgs e)
+        {
+            Application.Current.Shutdown();
+        }
+
+        private void SwRememberMe_Unchecked(object sender, RoutedEventArgs e)
+        {
+            string path = Path.Combine(PathHelper.DatabaseFolder, "user.bin");
+            if (File.Exists(path))
+            {
+                File.Delete(path);
+            }
+        }
     }
 }
diff --git a/WPFProject/PanuonWindow.xaml b/WPFProject/PanuonWindow.xaml
index f2d416d..4e6bdc8 100644
--- a/WPFProject/PanuonWindow.xaml
+++ b/WPFProject/PanuonWindow.xaml
@@ -13,14 +13,29 @@
             x:Name="window"
             BorderBrush="Transparent"
             Loaded="window_Loaded"
+            pu:WindowXCaption.Buttons="All"
             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"
+            Closed="window_Closed"
             pu:WindowXCaption.Background="Transparent"
             pu:WindowXCaption.Height="80"
             pu:WindowXCaption.Foreground="#FFF" >
     <Window.Resources>
         <hc:LangConverter x:Key="lc"/>
+        <Style x:Key="UserMenu" TargetType="Menu" BasedOn="{StaticResource {x:Static pu:StyleKeys.MenuStyle}}">
+            <Setter Property="Background" Value="{StaticResource PrimaryColor}"/>
+            <Setter Property="Foreground" Value="#FFF"/>
+            <Setter Property="pu:IconHelper.FontFamily" Value="{StaticResource iconfont}"/>
+            <Setter Property="pu:IconHelper.Margin" Value="0,0,10,0"/>
+            <Setter Property="BorderThickness" Value="0"/>
+            <Setter Property="Cursor" Value="Hand"/>
+            <Setter Property="pu:MenuHelper.SubmenuItemsForeground" Value="#3a3a3a"/>
+            <Setter Property="pu:MenuHelper.SubmenuItemsBackground" Value="White"/>
+            <Setter Property="pu:MenuHelper.TopLevelItemsHoverBackground" Value="{x:Null}"/>
+            <Setter Property="pu:MenuHelper.TopLevelDropDownPlacement" Value="BottomLeft"/>
+        </Style>
+
     </Window.Resources>
     <Window.Background>
         <ImageBrush Stretch="Uniform" AlignmentX="Left" AlignmentY="Top"  
@@ -49,6 +64,24 @@
 
     <pu:WindowXCaption.ExtendControl>
         <StackPanel Orientation="Horizontal">
+            <Menu Margin="0" Padding="0" VerticalAlignment="Top"
+                  VerticalContentAlignment="Center" Style="{StaticResource UserMenu}"
+                  
+                  Background="Transparent">
+                <MenuItem x:Name="userMenu" FontSize="16">
+                    <MenuItem 
+                        Icon="&#xe61d;"
+                        Cursor="Hand" FontSize="14" Header="閫�鍑虹櫥褰�"
+                              x:Name="MiLogout"
+                              Click="MenuItem_Click"/>
+                    <MenuItem Cursor="Hand" FontSize="14" 
+                        Icon="&#xe867;"
+                              Header="閲嶆柊鍚姩"
+                              x:Name="MiRestart"
+                              Click="MenuItem_Click"/>
+                </MenuItem>
+            </Menu>
+
             <Button x:Name="BtnSwitchTheme"
                 FontSize="18"
                 FontFamily="/HMI.UI;component/Resources/Font/#iconfont"
@@ -68,7 +101,7 @@
                         </Style.Triggers>
                     </Style>
                 </Button.Style>
-                
+
             </Button>
         </StackPanel>
     </pu:WindowXCaption.ExtendControl>
diff --git a/WPFProject/PanuonWindow.xaml.cs b/WPFProject/PanuonWindow.xaml.cs
index 717a7c8..375df69 100644
--- a/WPFProject/PanuonWindow.xaml.cs
+++ b/WPFProject/PanuonWindow.xaml.cs
@@ -1,9 +1,16 @@
-锘縰sing HMI.Core.Setting;
+锘縰sing HMI.Base;
+using HMI.Core.Api;
+using HMI.Core.Entity;
+using HMI.Core.Setting;
 using HMI.Core.Util;
+using HMI.UI.Util;
 using Panuon.WPF.UI;
 using System;
+using System.ComponentModel;
+using System.Diagnostics;
 using System.IO;
 using System.Windows;
+using System.Windows.Controls;
 using System.Windows.Media.Imaging;
 
 namespace WPFProject
@@ -11,13 +18,29 @@
     /// <summary>
     /// PanuonWindow.xaml 鐨勪氦浜掗�昏緫
     /// </summary>
-    public partial class PanuonWindow : WindowX
+    public partial class PanuonWindow : WindowX, IPluginContainer
     {
         public PanuonWindow()
+        {
+            Loaded += PanuonWindow_Loaded;
+        }
+
+        private UserInfo currentUser;
+        public UserInfo CurrentUser
+        {
+            get => currentUser;
+            set
+            {
+                Set(ref currentUser, value);
+            }
+        }
+
+        private void PanuonWindow_Loaded(object sender, RoutedEventArgs e)
         {
             DataContext = this;
             RefrshConfig();
             InitializeComponent();
+            userMenu.Header = this.lang("App.Welcome", UserInfo.User.Name);
             ShowMenu = true;
         }
 
@@ -53,10 +76,15 @@
 
         private bool showMenu;
 
+        public event CancelEventHandler OnCloseEvent;
+        public event EventHandler DoCloseEvent;
+        public event EventHandler LoadComplateEvent;
+
         public bool ShowMenu
         {
             get { return showMenu; }
-            set { 
+            set
+            {
                 Set(ref showMenu, value);
                 menuTab.ChangeItemVisibility(value);
             }
@@ -82,7 +110,67 @@
         private void SystemSetting_SystemSettingChanged(object sender, SystemSetting e)
         {
             RefrshConfig();
-            LangUtil.LoadLang();
+        }
+
+        protected override void OnClosing(CancelEventArgs e)
+        {
+            OnCloseEvent?.Invoke(this, e);
+            base.OnClosing(e);
+        }
+
+        protected override void OnClosed(EventArgs e)
+        {
+            DoCloseEvent?.Invoke(this, e);
+            base.OnClosed(e);
+        }
+
+        private void MenuItem_Click(object sender, RoutedEventArgs e)
+        {MenuItem mi = sender as MenuItem;
+            switch (mi.Name) {
+                case "MiLogout":
+                    Logout();
+                    break;
+                case "MiRestart":
+                    RestartApp();
+                    break;
+                default:
+                    break;
+            }
+        }
+
+        private void Logout()
+        {
+            if (MsgBoxHelper.WarnMessage(this, this.lang("App.MakeSureLogout")) == MessageBoxResult.OK)
+            {
+                string path = Path.Combine(PathHelper.DatabaseFolder, "user.bin");
+                if (File.Exists(path)) { 
+                    File.Delete(path);
+                }
+                this.Closed -= window_Closed;
+                this.Close();
+                PluginLoader.Reset();
+                Login login = new Login();
+                login.Show();
+            }
+        }
+
+        private void RestartApp()
+        {
+            if (MsgBoxHelper.WarnMessage(this, this.lang("App.MakeSureRestartApp")) == MessageBoxResult.OK)
+            {
+                Application.Current.Exit += Current_Exit;
+                this.Close();
+            }
+        }
+
+        private void Current_Exit(object sender, ExitEventArgs e)
+        {
+            System.Windows.Forms.Application.Restart();
+        }
+
+        private void window_Closed(object sender, EventArgs e)
+        {
+            Application.Current.Shutdown();
         }
 
         //private void BaseWindow_Loaded(object sender, RoutedEventArgs e)
diff --git a/WPFProject/TextFile1.txt b/WPFProject/TextFile1.txt
index 1a38b45..680b8c0 100644
--- a/WPFProject/TextFile1.txt
+++ b/WPFProject/TextFile1.txt
@@ -17,16 +17,17 @@
 set "source_folder=$(TargetDir)"
 set "destination_folder=$(TargetDir)3rds"
 
-if not exist "%destination_folder%" mkdir "%destination_folder%"
+rmdir /S /Q "%destination_folder%"
+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" (
-        move "%%D" "%destination_folder%"
+        move /Y "%%D" "%destination_folder%"
     )
 )
 
 for %%F in ("%source_folder%\*") do (
     if /I not "%%~nxF"=="$(TargetFileName)" if /I not "%%~nxF"=="$(TargetFileName).config" if /I not "%%~nxF"=="$(TargetName).pdb" if /I not "%%~nxF"=="clear.bat" (
-        move "%%F" "%destination_folder%"
+        move /Y "%%F" "%destination_folder%"
     )
 )
\ No newline at end of file
diff --git a/WPFProject/Utils/BinUtil.cs b/WPFProject/Utils/BinUtil.cs
new file mode 100644
index 0000000..264d59c
--- /dev/null
+++ b/WPFProject/Utils/BinUtil.cs
@@ -0,0 +1,58 @@
+锘縰sing System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace WPFProject.Utils
+{
+    public class BinUtil
+    {
+        /// <summary>
+        /// 鎶婂瓧绗︿覆浠ヤ簩杩涘埗瀛樺叆bin鏂囦欢涓�
+        /// </summary>
+        /// <param name="content">瀛楃鍐呭</param>
+        /// <param name="filePath">瀛樻斁璺緞</param>
+        public static void SaveStrToBin(string content, string filePath = null)
+        {
+            if (string.IsNullOrEmpty(filePath))
+            {
+                filePath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().FullName), "bin");
+            }
+            byte[] bytes = Convert.FromBase64String(content);
+            using (Stream stream = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.ReadWrite))
+            using (BinaryWriter writer = new BinaryWriter(stream))
+            {
+                writer.Write(bytes);
+            }
+        }
+
+        /// <summary>
+        /// 浠巄in涓姞杞藉瓧绗︿覆
+        /// </summary>
+        /// <param name="content">瀛楃涓插唴瀹�</param>
+        /// <param name="filePath">bin鏂囦欢璺緞</param>
+        /// <returns>璇诲彇鏄惁鎴愬姛</returns>
+        public static bool LoadStrFromBin(ref string content, string filePath = null)
+        {
+            if (string.IsNullOrEmpty(filePath))
+            {
+                filePath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().FullName), "bin");
+            }
+            if (!File.Exists(filePath))
+            {
+                return false;
+            }
+            using (FileStream stream = new FileStream(filePath, FileMode.Open, FileAccess.Read))
+            using (BinaryReader reader = new BinaryReader(stream))
+            {
+                byte[] bytes = new byte[stream.Length];
+                reader.Read(bytes, 0, bytes.Length);
+                content = Convert.ToBase64String(bytes);
+                return true;
+            }
+        }
+    }
+}
diff --git a/WPFProject/Utils/Encryptor.cs b/WPFProject/Utils/Encryptor.cs
new file mode 100644
index 0000000..ab91d77
--- /dev/null
+++ b/WPFProject/Utils/Encryptor.cs
@@ -0,0 +1,63 @@
+锘縰sing System;
+using System.IO;
+using System.Security.Cryptography;
+using System.Text;
+
+namespace WPFProject.Utils
+{
+    public class Encryptor
+    {
+        private readonly byte[] key;
+        private readonly byte[] iv;
+
+        public Encryptor(string keyString, string ivString)
+        {
+            key = Encoding.UTF8.GetBytes(keyString);
+            iv = Encoding.UTF8.GetBytes(ivString);
+        }
+
+        public string Encrypt(string plainText)
+        {
+            using (var aes = Aes.Create())
+            {
+                aes.Key = key;
+                aes.IV = iv;
+                ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, aes.IV);
+
+                using (MemoryStream msEncrypt = new MemoryStream())
+                {
+                    using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
+                    {
+                        using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
+                        {
+                            swEncrypt.Write(plainText);
+                        }
+                        return Convert.ToBase64String(msEncrypt.ToArray());
+                    }
+                }
+            }
+        }
+
+
+        public string Decrypt(string cipherText)
+        {
+            using (var aes = Aes.Create())
+            {
+                aes.Key = key;
+                aes.IV = iv;
+                ICryptoTransform decryptor = aes.CreateDecryptor(aes.Key, aes.IV);
+                byte[] arr = Convert.FromBase64String(cipherText);
+                using (MemoryStream msDecrypt = new MemoryStream(arr))
+                {
+                    using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
+                    {
+                        using (StreamReader srDecrypt = new StreamReader(csDecrypt))
+                        {
+                            return srDecrypt.ReadToEnd();
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/WPFProject/WPFProject.csproj b/WPFProject/WPFProject.csproj
index 813964a..0276b9d 100644
--- a/WPFProject/WPFProject.csproj
+++ b/WPFProject/WPFProject.csproj
@@ -250,9 +250,11 @@
     <Compile Include="UI\WorkWindow.xaml.cs">
       <DependentUpon>WorkWindow.xaml</DependentUpon>
     </Compile>
+    <Compile Include="Utils\BinUtil.cs" />
     <Compile Include="Utils\CanvasHelper.cs" />
     <Compile Include="Utils\DdDragHandler.cs" />
     <Compile Include="Utils\DdDropHandler.cs" />
+    <Compile Include="Utils\Encryptor.cs" />
     <Compile Include="Utils\MainToastHandler.cs" />
     <Compile Include="Utils\Resize.cs" />
     <Compile Include="Utils\Win32Helper.cs" />
@@ -378,6 +380,9 @@
     <Content Include="x64\SQLite.Interop.dll">
       <CopyToOutputDirectory>Always</CopyToOutputDirectory>
     </Content>
+    <Content Include="Libs\SuperSocket.SocketEngine.dll">
+      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+    </Content>
     <Resource Include="Icon\b1.jpg" />
     <Resource Include="Icon\b2.jpg" />
     <Resource Include="Icon\b3.jpg" />
@@ -406,9 +411,6 @@
       <Generator>ResXFileCodeGenerator</Generator>
       <LastGenOutput>Resources.Designer.cs</LastGenOutput>
     </EmbeddedResource>
-    <Content Include="Libs\x64\SQLite.Interop.dll">
-      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
-    </Content>
     <Content Include="Config\log4net.config">
       <CopyToOutputDirectory>Always</CopyToOutputDirectory>
     </Content>
@@ -421,9 +423,7 @@
     <Content Include="Lang\zh-CN\Setting.json">
       <CopyToOutputDirectory>Always</CopyToOutputDirectory>
     </Content>
-    <Content Include="Config\Database\database.db">
-      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
-    </Content>
+    <Content Include="Config\Database\database.db" />
     <Content Include="Lang\zh-CN\DataViewer.json">
       <CopyToOutputDirectory>Always</CopyToOutputDirectory>
     </Content>
@@ -561,17 +561,18 @@
 set "source_folder=$(TargetDir)"
 set "destination_folder=$(TargetDir)3rds"
 
-if not exist "%25destination_folder%25" mkdir "%25destination_folder%25"
+rmdir /S /Q "%25destination_folder%25"
+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" (
-        move "%25%25D" "%25destination_folder%25"
+        move /Y "%25%25D" "%25destination_folder%25"
     )
 )
 
 for %25%25F in ("%25source_folder%25\*") do (
     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 "%25%25F" "%25destination_folder%25"
+        move /Y "%25%25F" "%25destination_folder%25"
     )
 )</PostBuildEvent>
   </PropertyGroup>
diff --git a/WPFProject/WPFProject.sln b/WPFProject/WPFProject.sln
index a6bc49f..c443e15 100644
--- a/WPFProject/WPFProject.sln
+++ b/WPFProject/WPFProject.sln
@@ -185,8 +185,8 @@
 		{202D9A7B-96B3-4AD4-9977-FC4263207A02}.Release|x64.Build.0 = Release|Any CPU
 		{AFEA100A-9223-448D-AB10-3015D93DD98C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{AFEA100A-9223-448D-AB10-3015D93DD98C}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{AFEA100A-9223-448D-AB10-3015D93DD98C}.Debug|x64.ActiveCfg = Debug|Any CPU
-		{AFEA100A-9223-448D-AB10-3015D93DD98C}.Debug|x64.Build.0 = Debug|Any CPU
+		{AFEA100A-9223-448D-AB10-3015D93DD98C}.Debug|x64.ActiveCfg = Debug|x64
+		{AFEA100A-9223-448D-AB10-3015D93DD98C}.Debug|x64.Build.0 = Debug|x64
 		{AFEA100A-9223-448D-AB10-3015D93DD98C}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{AFEA100A-9223-448D-AB10-3015D93DD98C}.Release|Any CPU.Build.0 = Release|Any CPU
 		{AFEA100A-9223-448D-AB10-3015D93DD98C}.Release|x64.ActiveCfg = Release|Any CPU

--
Gitblit v1.9.1