alielie
2025-05-20 99df058a665ea04065a23e729ec79a2f23f79881
优化
57个文件已修改
22个文件已添加
2 文件已重命名
1个文件已删除
3679 ■■■■■ 已修改文件
HMI.Base/PluginLoader.cs 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.Core/Annotation/ExcelColumn.cs 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.Core/Entity/UserInfo.cs 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.Core/FontSizeConverter.cs 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.Core/Manager/ParameterManager.cs 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.Core/Util/BeanUtil.cs 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.Core/Util/LangUtil.cs 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.Core/Util/MSMQHelper.cs 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.Core/Util/PathHelper.cs 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.LogViewer/LogViewer.xaml.cs 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.MenuManage/Entity/UserData.cs 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.MenuManage/HMI.MenuManage.csproj 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.MenuManage/Modal/UserModal.xaml 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.MenuManage/Modal/UserModal.xaml.cs 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.MenuManage/Model/UserManageModel.cs 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.MenuManage/Properties/AssemblyInfo.cs 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.MenuManage/UserManage.xaml 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.MenuManage/UserManage.xaml.cs 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.UI/Controls/NumberPanel.xaml.cs 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.UI/DpUtil/UniformGridHelper.cs 106 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.UI/Entity/NotifyBase.cs 97 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.UI/Entity/ScannerConfig.cs 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.UI/HMI.UI.csproj 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.UI/MaskWindow.xaml.cs 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.UI/ScannerSetting.xaml 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.UI/ScannerSetting.xaml.cs 153 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.UI/Style/FormStyle.xaml 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.UI/Style/PanuonResource.xaml 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.UI/Util/MsgBoxHelper.cs 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.UnitTest/App.xaml 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.UnitTest/HMI.UnitTest.csproj 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.UnitTest/MainWindow.xaml.cs 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.Util/ExcelHelper.cs 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.DataBoard/DataBoard.xaml 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.DataBoard/DataBoard.xaml.cs 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.DataBoard/Plugin.DataBoard.csproj 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.JarUI/JarCell.xaml 80 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.JarUI/JarCell.xaml.cs 84 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.JarUI/JarUI.xaml 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.JarUI/JarUI.xaml.cs 498 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.JarUI/Plugin.JarUI.csproj 127 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.JarUI/Properties/AssemblyInfo.cs 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.JarUI/Properties/Resources.Designer.cs 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.JarUI/Properties/Resources.resx 117 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.JarUI/Properties/Settings.Designer.cs 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.JarUI/Properties/Settings.settings 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.JarUI/packages.config 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.SNGenerateTool/Form1.cs 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.SNGenerateTool/UserControl1.xaml.cs 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.TestResult/ViewControl.xaml.cs 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.TsExecution/SingleControl.xaml 223 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.TsExecution/SingleControl.xaml.cs 68 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.TsExecution/TsExecution.xaml 130 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.TsExecution/TsExecution.xaml.cs 335 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SimpleUtil/HMI.LogUtil/HMI.LogUtil.csproj 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SimpleUtil/HMI.LogUtil/LogHelper.cs 112 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SimpleUtil/HMI.LogUtil/Properties/AssemblyInfo.cs 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SimpleUtil/HMI.SerialData.Util/HMI.SerialData.Util.csproj 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SimpleUtil/HMI.SerialData.Util/SerialDataHelper.cs 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SimpleUtil/HMI.Simple.Util/HMI.Simple.Util.csproj 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SimpleUtil/HMI.Simple.Util/ParameterUtil.cs 161 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SimpleUtil/HMI.Simple.Util/ProductUtil.cs 70 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SimpleUtil/HMI.Util.Base/HMI.Util.Base.csproj 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SimpleUtil/HMI.Util.Base/Properties/AssemblyInfo.cs 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SimpleUtil/HMI.Util.Base/RegUtil.cs 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
VisionBuilder.Test/PluginOne.cs 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/Api/Api.txt 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/App.xaml 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/App.xaml.cs 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/BuildCmd.bat 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/Lang/en-US/TestExecution.json 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/Lang/en-US/app.json 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/Lang/zh-CN/TestExecution.json 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/Lang/zh-CN/app.json 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/Login.xaml.cs 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/PanuonWindow.xaml 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/PanuonWindow.xaml.cs 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/Properties/app.manifest 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/SuperSocket.SocketEngine.dll 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/WPFProject.csproj 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/WPFProject.sln 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/Window1.xaml.cs 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
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;";
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;
        }
    }
}
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; }
HMI.Core/FontSizeConverter.cs
@@ -1,14 +1,51 @@
using 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;
        }
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;
            }
        }
    }
}
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
{
HMI.Core/Util/LangUtil.cs
@@ -112,7 +112,7 @@
                        }
                    }
                }
                catch (Exception ex)
                catch
                {
                }
            }
@@ -160,7 +160,7 @@
                        }
                    }
                }
                catch (Exception ex)
                catch
                {
                }
            }
HMI.Core/Util/MSMQHelper.cs
@@ -93,7 +93,7 @@
                {
                    MsgReceived = this.msgQueue.Receive(Time);
                }
                catch(Exception ex)
                catch
                {
                    // do nothing
                }
HMI.Core/Util/PathHelper.cs
@@ -174,7 +174,7 @@
                    Directory.CreateDirectory(path);
                }
            }
            catch (Exception ex)
            catch
            {
            }
HMI.LogViewer/LogViewer.xaml.cs
@@ -1,6 +1,7 @@
using 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;
            }
        }
    }
}
HMI.MenuManage/Entity/UserData.cs
@@ -1,8 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using 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;
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>
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">
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);
        }
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);
                }
            }
HMI.MenuManage/Properties/AssemblyInfo.cs
@@ -1,9 +1,7 @@
using System.Reflection;
using System.Resources;
using System.Runtime.CompilerServices;
using HMI.Core.Annotation;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Windows;
using HMI.Core.Annotation;
// 有关程序集的一般信息由以下
// 控制。更改这些特性值可修改
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}"/>
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}");
            }
        }
    }
}
HMI.UI/Controls/NumberPanel.xaml.cs
@@ -46,9 +46,8 @@
                {
                }
                catch (Exception ex)
                catch
                {
                    return;
                }
                finally
HMI.UI/DpUtil/UniformGridHelper.cs
@@ -108,6 +108,112 @@
                    control.BorderThickness = bt;
                }
            }
        }
        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);
                    }
                }
            }
        }
    }
HMI.UI/Entity/NotifyBase.cs
File was deleted
HMI.UI/Entity/ScannerConfig.cs
New file
@@ -0,0 +1,34 @@
using 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;
        }
    }
}
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>
HMI.UI/MaskWindow.xaml.cs
@@ -50,9 +50,19 @@
            //}));
            Task.Run(() =>
            {
                try
                {
                // 暂停线程令UI完全展示
                if (delay > 0) Thread.Sleep(delay);
                action(this);
                }
                finally
                {
                    Dispatcher.Invoke(() =>
                    {
                        this.Close();
                    });
                }
            });
        }
HMI.UI/ScannerSetting.xaml
New file
@@ -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>
HMI.UI/ScannerSetting.xaml.cs
New file
@@ -0,0 +1,153 @@
using 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);
            }
        }
    }
}
HMI.UI/Style/FormStyle.xaml
@@ -106,7 +106,6 @@
    <Style x:Key="InputFormGroupStyle" TargetType="pu:FormGroup">
        <Style.Resources>
            <Style TargetType="TextBox" BasedOn="{StaticResource EditorTextBoxStyle}">
            </Style>
        </Style.Resources>
        <Setter Property="HeaderTemplate">
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"/>
HMI.UI/Util/MsgBoxHelper.cs
@@ -71,12 +71,19 @@
            MessageBoxResult res = MessageBoxResult.None;
            if (!(owner is Window window))
            {
                owner.Dispatcher.Invoke(() =>
                {
                window = Window.GetWindow(owner) ?? Application.Current.MainWindow;
                    res = MessageBoxX.Show(window, message, caption, button, icon, defaultButton, setting, countdown);
                });
            }
            else
            {
            window.Dispatcher.Invoke(() =>
            {
                res = MessageBoxX.Show(window, message, caption, button, icon, defaultButton, setting, countdown);
            });
            }
            return res;
        }
    }
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>
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>
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(() =>
                {
                    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;
        }
    }
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;
Plugin.DataBoard/DataBoard.xaml
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)
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" />
Plugin.JarUI/JarCell.xaml
New file
@@ -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>
Plugin.JarUI/JarCell.xaml.cs
New file
@@ -0,0 +1,84 @@
using 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;
        }
    }
}
Plugin.JarUI/JarUI.xaml
New file
@@ -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>
Plugin.JarUI/JarUI.xaml.cs
New file
@@ -0,0 +1,498 @@
using 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>
        /// 初始化TestStand引擎
        /// </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
    }
}
Plugin.JarUI/Plugin.JarUI.csproj
New file
@@ -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>
Plugin.JarUI/Properties/AssemblyInfo.cs
New file
@@ -0,0 +1,52 @@
using 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 特性的注释。  更新
//以下行中的“en-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")]
Plugin.JarUI/Properties/Resources.Designer.cs
New file
@@ -0,0 +1,62 @@
//------------------------------------------------------------------------------
// <auto-generated>
//     此代码由工具生成。
//     运行时版本: 4.0.30319.42000
//
//     对此文件的更改可能导致不正确的行为,如果
//     重新生成代码,则所做更改将丢失。
// </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;
            }
        }
    }
}
Plugin.JarUI/Properties/Resources.resx
New file
@@ -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>
Plugin.JarUI/Properties/Settings.Designer.cs
New file
@@ -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;
            }
        }
    }
}
Plugin.JarUI/Properties/Settings.settings
New file
@@ -0,0 +1,7 @@
<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="uri:settings" CurrentProfile="(Default)">
  <Profiles>
    <Profile Name="(Default)" />
  </Profiles>
  <Settings />
</SettingsFile>
Plugin.JarUI/packages.config
New file
@@ -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>
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",
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)
                        {
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");
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,20 +52,45 @@
                <ColumnDefinition Width="Auto"/>
                <ColumnDefinition/>
            </Grid.ColumnDefinitions>
            <Label Grid.RowSpan="3">
            <StackPanel Grid.RowSpan="3">
                <StackPanel.Style>
                    <Style TargetType="StackPanel">
                        <Setter Property="Background" Value="DeepSkyBlue"/>
                        <Style.Triggers>
                            <DataTrigger Binding="{Binding Status}" Value="Passed">
                                <Setter Property="Background" Value="{StaticResource passedColor}"/>
                            </DataTrigger>
                            <DataTrigger Binding="{Binding Status}" Value="Testing">
                                <Setter Property="Background" Value="Gold"/>
                            </DataTrigger>
                            <DataTrigger Binding="{Binding Status}" Value="Failed">
                                <Setter Property="Background" Value="{StaticResource DangerColor}"/>
                            </DataTrigger>
                            <DataTrigger Binding="{Binding Status}" Value="Failed">
                                <Setter Property="Background" Value="{StaticResource DangerColor}"/>
                            </DataTrigger>
                            <DataTrigger Binding="{Binding Status}" Value="Ready">
                                <Setter Property="Background" Value="DeepSkyBlue"/>
                            </DataTrigger>
                        </Style.Triggers>
                    </Style>
                </StackPanel.Style>
                <Label>
                <Label.Style>
                    <Style TargetType="Label">
                        <Setter Property="Padding" Value="10"/>
                            <Setter Property="Padding" Value="10,10,10,0"/>
                            <Setter Property="Margin" Value="0"/>
                        <Setter Property="FontFamily" Value="{StaticResource iconfont}"/>
                        <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"/>
                            <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="Lime"/>
                                    <Setter Property="Background" Value="{StaticResource passedColor}"/>
                            </DataTrigger>
                            <DataTrigger Binding="{Binding Status}" Value="Testing">
                                <Setter Property="Content" Value="&#xeb61;"/>
@@ -80,41 +113,104 @@
                </Label.Style>
            </Label>
            <Label HorizontalContentAlignment="Left" Grid.Column="1" Content="{Binding Status}"/>
                <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 Style="{StaticResource baseLabel}" Grid.Row="1" Grid.Column="1">
            <!--<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="5,0,20,0" Text="{Binding TotalNum}"/>
                    <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="5,0,20,0" Text="{Binding PassNum}"/>
                    <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="5,0,20,0" Text="{Binding FailNum}"/>
                    <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="5,0,20,0" Text="{Binding PassRate,StringFormat={}{0:P2}}"/>
                    <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}" Grid.Row="2" Grid.Column="1">
            <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="5,0,20,0" Content="{Binding Barcode}"/>
                    <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="5,0,20,0" Content="{Binding ElapseTime}"/>
                    <Label Style="{StaticResource baseLabel}" Margin="2,0,18,0" Content="{Binding ElapseTime}"/>
                </StackPanel>
            </Label>
        </Grid>
        <DataGrid Grid.Row="2" Style="{StaticResource DefaultDataGrid}"
                  pu:DataGridHelper.ColumnHeaderFontSize="12"
        <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>
            <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>
        <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>
@@ -144,29 +240,35 @@
                </DataGridTemplateColumn>-->
                <DataGridTextColumn Header="#"
                                        ElementStyle="{StaticResource TableTextBlock}"
                                    Binding="{Binding Header, RelativeSource={RelativeSource AncestorType={x:Type DataGridRow}, Mode=FindAncestor}}" 
                                    Width="40"/>
                <DataGridTextColumn Width="*" Binding="{Binding Name}">
                                    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}">
                    <DataGridTextColumn Width="*" Binding="{Binding Unit}"
                                        ElementStyle="{StaticResource TableTextBlock}">
                    <DataGridTextColumn.Header>
                        <TextBlock Text="{DynamicResource Test.Unit}"/>
                    </DataGridTextColumn.Header>
                </DataGridTextColumn>
                <DataGridTextColumn Width="*" Binding="{Binding Value}">
                    <DataGridTextColumn Width="*" Binding="{Binding Value}"
                                        ElementStyle="{StaticResource TableTextBlock}">
                    <DataGridTextColumn.Header>
                        <TextBlock Text="{DynamicResource Test.Value}"/>
                    </DataGridTextColumn.Header>
                </DataGridTextColumn>
                <DataGridTextColumn Width="*" Binding="{Binding LimitDown}">
                    <DataGridTextColumn Width="*" Binding="{Binding LimitDown}"
                                        ElementStyle="{StaticResource TableTextBlock}">
                    <DataGridTextColumn.Header>
                        <TextBlock Text="{DynamicResource Test.LimitDown}"/>
                    </DataGridTextColumn.Header>
                </DataGridTextColumn>
                <DataGridTextColumn Width="*" Binding="{Binding LimitUp}">
                    <DataGridTextColumn Width="*" Binding="{Binding LimitUp}"
                                        ElementStyle="{StaticResource TableTextBlock}">
                    <DataGridTextColumn.Header>
                        <TextBlock Text="{DynamicResource Test.LimitUp}"/>
                    </DataGridTextColumn.Header>
@@ -177,13 +279,17 @@
                    </DataGridTemplateColumn.Header>
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <TextBlock x:Name="text" VerticalAlignment="Center" Text="{Binding Result}"/>
                                <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="text" Property="Foreground" Value="Green"/>
                                        <Setter TargetName="border" Property="Background" Value="{StaticResource passedColor}"/>
                                        <Setter TargetName="text" Property="Foreground" Value="#000"/>
                                </DataTrigger>
                                <DataTrigger Binding="{Binding Result}" Value="Failed">
                                    <Setter TargetName="text" Property="Foreground" Value="Red"/>
                                        <Setter TargetName="border" Property="Background" Value="{StaticResource DangerColor}"/>
                                        <Setter TargetName="text" Property="Foreground" Value="#000"/>
                                </DataTrigger>
                            </DataTemplate.Triggers>
                        </DataTemplate>
@@ -191,15 +297,17 @@
                </DataGridTemplateColumn>
            </DataGrid.Columns>
        </DataGrid>
        <Grid Grid.Row="3" Height="20" Margin="0">
            <ProgressBar Grid.Row="3" x:Name="testProcess" />
            <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 Background="#f1eded" Grid.Row="4" Height="25">
            <Grid Background="#f1eded" Grid.Row="2" Height="25">
            <DockPanel>
                <Label Padding="0" VerticalContentAlignment="Center" Content="{Binding CurrentStep}"/>
                    <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" 
@@ -218,7 +326,7 @@
VerticalContentAlignment="Center" Content="{Binding ProbeWarn}"/>
                    </StackPanel>
                    <Button Style="{StaticResource LinkButton}" x:Name="clear" Margin="10,0,0,0" Click="Button_Click"
                        <Button FontSize="12" Style="{StaticResource LinkButton}" x:Name="clear" Margin="10,0,0,0" Click="Button_Click"
                            Content="{DynamicResource App.Clear}"/>
                </StackPanel>
@@ -227,6 +335,51 @@
VerticalContentAlignment="Center" Content="{DynamicResource Ts.ProbeAlarm}"/>
            </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>
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);
        }
    }
}
Plugin.TsExecution/TsExecution.xaml
@@ -18,27 +18,111 @@
         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>
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
            {
                if (WriteConfig("EnableMes", value))
                {
                Set(ref _enableMes, value);
                WriteConfig("EnableMes", value);
                }
            }
        }
@@ -141,8 +151,10 @@
            get => _enableCanLog;
            set
            {
                if (WriteConfig("EnableCanLog", value))
                {
                Set(ref _enableCanLog, value);
                WriteConfig("EnableCanLog", value);
                }
            }
        }
@@ -152,8 +164,51 @@
            get => _enableDebugMode;
            set
            {
                if (WriteConfig("EnableDebugMode", value))
                {
                Set(ref _enableDebugMode, value);
                WriteConfig("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,16 +334,23 @@
            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();
                            //连接OPChandle
                            //this.OpcHandle.Connect2("127.0.0.1", 6543, 18081, out string ErrMsg, null);
                            //启动测试进程
                            StartSequence();
                        });
                            isLoadOK = true;
                        StartSequence();
                        }
                        catch (Exception ex)
                        {
@@ -277,12 +368,30 @@
                            log.Error("TsExecution GCTimer Start Failed", ex);
                            MsgBoxHelper.Error(this, $"{LangUtil.Get("Ts.LoadFailed")}: {ex.Message}");
                        }
                        finally
                    Dispatcher.Invoke(() =>
                        {
                            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;
            }
            //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));
                _enableMes = false;
                Set("EnableMes");
        }
        private void WriteConfig(string name, bool value)
            try
        {
            IniFileHelper.WriteIniData("Config", name, value.ToString(), CountIniPath);
            //this.engine.Globals.SetValBoolean($"StationState.{name}", 1, value);
                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));
        }
        //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);
            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
                        {
                            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://传递SocketContext
@@ -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,14 +996,23 @@
        public override void OnClose(object sender, CancelEventArgs e)
        {
            try
            {
            StopSequence();
            this.engine?.ReleaseSequenceFileEx(this.testSeq, 0);
            this.engine?.ReleaseSequenceFileEx(this.modeSeq, 0);
            this.engine?.UnloadAllModules();
            this.testSeq = null;
            this.modeSeq = null;
                // 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)
        {
SimpleUtil/HMI.LogUtil/HMI.LogUtil.csproj
New file
@@ -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>
SimpleUtil/HMI.LogUtil/LogHelper.cs
New file
@@ -0,0 +1,112 @@
using 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>
        /// 防止多线程创建文件(但是不知道teststand会不会多进程调用)
        /// </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);
        }
    }
}
SimpleUtil/HMI.LogUtil/Properties/AssemblyInfo.cs
New file
@@ -0,0 +1,33 @@
using 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")]
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>
SimpleUtil/HMI.SerialData.Util/SerialDataHelper.cs
@@ -6,13 +6,19 @@
{
    public class SerialDataHelper
    {
        private static string FileName = "SerialData";
        /// <summary>
        /// 防止多线程创建文件(但是不知道teststand会不会多进程调用)
        /// </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)
        {
@@ -20,6 +26,8 @@
        }
        private static void CreateDbFile(string dbFile)
        {
            lock (fileLock)
        {
            if (!File.Exists(dbFile))
            {
@@ -40,23 +48,32 @@
                }
            }
        }
        }
        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;
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>
SimpleUtil/HMI.Simple.Util/ParameterUtil.cs
@@ -1,4 +1,4 @@
using Microsoft.Win32;
using 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,22 +33,7 @@
            errMsg = string.Empty;
            try
            {
                string registryPath = $@"SOFTWARE\{Company}\{Program}";
                // 尝试打开注册表键
                using (RegistryKey key = Registry.LocalMachine.OpenSubKey(registryPath))
                {
                    if (key == null)
                    {
                        errMsg = "注册表不存在,请先运行一次HMI";
                        return null;
                    }
                    string value = key.GetValue("Location")?.ToString();
                    if (string.IsNullOrEmpty(value))
                    {
                        errMsg = "注册表不存在,请先运行一次HMI";
                        return null;
                    }
                string value = RegUtil.GetLocaltion();
                    string path = Path.Combine(value, "Config", "Parameter", "CommonParameter.data");
                    if (!File.Exists(path))
                    {
@@ -58,9 +47,7 @@
                        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>
        /// 将参数写进Engine的StationGlobals.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>
        /// 将参数写进Engine的StationGlobals.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>
        /// 将参数写进Engine的StationGlobals.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>
        /// 将参数写进Engine的StationGlobals.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;
            }
        }
    }
}
SimpleUtil/HMI.Simple.Util/ProductUtil.cs
New file
@@ -0,0 +1,70 @@
using 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;
            }
        }
    }
}
SimpleUtil/HMI.Util.Base/HMI.Util.Base.csproj
New file
@@ -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>
SimpleUtil/HMI.Util.Base/Properties/AssemblyInfo.cs
New file
@@ -0,0 +1,33 @@
using 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")]
SimpleUtil/HMI.Util.Base/RegUtil.cs
New file
@@ -0,0 +1,31 @@
using 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("注册表不存在,请先运行一次HMI");
                }
                string value = key.GetValue("Location")?.ToString();
                if (string.IsNullOrEmpty(value))
                {
                    throw new Exception("注册表不存在,请先运行一次HMI");
                }
                return value;
            }
        }
    }
}
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" };
        }
    }
}
WPFProject/Api/Api.txt
New file
@@ -0,0 +1 @@

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>
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());
WPFProject/BuildCmd.bat
File was renamed from WPFProject/BuildCmd.txt
@@ -1,6 +1,8 @@
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"
@@ -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%"
    )
)
@@ -35,3 +38,5 @@
        move /Y "%%F" "%destination_folder%"
    )
)
del /s /q "%source_folder%\*.pdb"
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:",
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"
}
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": "正在启动测试序列...",
  "Ts.SequenceIsStopping": "正在停止测试序列...",
  "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": "预警次数:",
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": "正在停止测试,请稍后...",
  "App.StopSequence": "停止测试序列",
  "App.StartSequence": "启动测试序列",
  "App.ScannerSetting": "扫码枪设置",
  "App.SequenceIsStarting": "正在启动测试序列,请稍后...",
  "App.Status": "状态",
  "App.Info": "信息"
}
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"));
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>
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"];
WPFProject/Properties/app.manifest
@@ -16,7 +16,7 @@
            如果你的应用程序需要此虚拟化来实现向后兼容性,则移除此
            元素。
        -->
        <requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
                <requestedExecutionLevel level="asInvoker" uiAccess="false" />
      </requestedPrivileges>
      <applicationRequestMinimum>
        <defaultAssemblyRequest permissionSetReference="Custom" />
WPFProject/SuperSocket.SocketEngine.dll
Binary files differ
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>
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}
WPFProject/Window1.xaml.cs
@@ -20,7 +20,6 @@
    /// </summary>
    public partial class Window1 : Window
    {
        private LoadingWindow loading;
        public Window1(LoadingWindow loading)
        {
            InitializeComponent();