alielie
2025-10-13 e3868d4d61ded0e76fc8fe3fdacdd9e6d9113cf1
优化HMI;
12个文件已修改
888 ■■■■■ 已修改文件
HMI.ParameterManage/Model/XmlModel.cs 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.ParameterManage/ParameterManage.xaml.cs 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.ParameterManage/ParameterModal.xaml 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.ParameterManage/ParameterModal.xaml.cs 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.ParameterManage/Properties/AssemblyInfo.cs 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.ParameterManage/SettingModal.xaml.cs 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.UnitTest/MainWindow.xaml.cs 91 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.DataBoard/DataBoard.xaml.cs 413 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.DataBoard/Libs/Static/test.html 244 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.TsExecution/TsExecution.xaml.cs 85 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SimpleUtil/HMI.Simple.Util/ParameterUtil.cs 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SimpleUtil/HMI.Simple.Util/ProductUtil.cs 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.ParameterManage/Model/XmlModel.cs
@@ -53,6 +53,8 @@
            }
        }
        public List<string> groups { get; private set; }
        public bool ValidateProductName(object value, out string msg)
        {
            msg = null;
@@ -98,6 +100,7 @@
            }
            parameters = parameters ?? new ObservableCollection<ParameterData>();
            Parameters = parameters;
            groups = Parameters.Where(p => !string.IsNullOrEmpty(p.PGroup)).Select(p => p.PGroup).Distinct().ToList();
        }
        public void SaveOrUpdateData(ProductData data)
HMI.ParameterManage/ParameterManage.xaml.cs
@@ -101,7 +101,7 @@
                Title = data == null ? this.lang("Pm.Parameter.Add") : this.lang("Pm.Parameter.Edit"),
                Owner = Window.GetWindow(this),
            };
            if (win.Open(Model.SelectedProduct.Name, data ?? new ParameterData { ProductName = Model.SelectedProduct.Name }) == true)
            if (win.Open(Model.SelectedProduct.Name, data ?? new ParameterData { ProductName = Model.SelectedProduct.Name }, Model.groups) == true)
            {
                Model.LoadParameters();
            }
HMI.ParameterManage/ParameterModal.xaml
@@ -83,7 +83,8 @@
            <pu:FormGroup x:Name="FgPGroup" Style="{StaticResource InputFormGroupStyle}" 
Header="{DynamicResource Pm.Parameter.Group}" 
dp:FormGroupHelper.IsRequired="False" GroupName="MyFormGroup">
                <TextBox x:Name="TbPGroup" Style="{StaticResource EditorTextBoxStyle}" />
                <!--<TextBox x:Name="TbPGroup" Style="{StaticResource EditorTextBoxStyle}" />-->
                <ComboBox x:Name="CbGroup" IsEditable="True" Style="{StaticResource FormComboxStyle}"/>
            </pu:FormGroup>
        </StackPanel>
    </Grid>
HMI.ParameterManage/ParameterModal.xaml.cs
@@ -84,22 +84,23 @@
                Data.Unit = TbUnit.Text;
                Data.Description = TbDescription.Text;
                Data.ProductName = TbProductName.Text;
                Data.PGroup = TbPGroup.Text;
                Data.PGroup = CbGroup.Text;
            }
            return flag;
        }
        public bool? Open(string productName, ParameterData data)
        public bool? Open(string productName, ParameterData data, List<string> groups)
        {
            Data = data;
            TbName.Text = Data.Name;
            CbType.SelectedValue = Data.Type;
            CbGroup.ItemsSource = groups;
            UpdateWindow(Data.Type);
            UpdateForm(Data);
            TbUnit.Text = Data.Unit;
            TbProductName.Text = productName;
            TbDescription.Text = Data.Description;
            TbPGroup.Text = Data.PGroup;
            CbGroup.Text = Data.PGroup;
            return this.ShowDialog();
        }
HMI.ParameterManage/Properties/AssemblyInfo.cs
@@ -49,4 +49,4 @@
//      修订号
//
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.2")]
[assembly: AssemblyFileVersion("1.0.0.3")]
HMI.ParameterManage/SettingModal.xaml.cs
@@ -102,7 +102,7 @@
            return flag;
        }
        public bool? Open(ParameterData data, ObservableCollection<ParameterData> datasource,List<string> groups)
        public bool? Open(ParameterData data, ObservableCollection<ParameterData> datasource, List<string> groups)
        {
            Datasource = datasource;
            Data = data;
HMI.UnitTest/MainWindow.xaml.cs
@@ -8,10 +8,10 @@
using Plugin.TestResult_WPF;
using Plugin.TsExecution;
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
namespace UnitTest
{
    /// <summary>
@@ -25,6 +25,7 @@
            ProductModelHelper.GetModelBySNR("cc123", out string model, out string msg);
            LangUtil.Load();
            InitializeComponent();
            RebuildDb();
            //page.Children.Add(new UserControl1());
        }
@@ -32,6 +33,94 @@
        PipeClient2 client;
        public string Conv(object obj)
        {
            if (obj == null || obj is DBNull)
            {
                return "NULL";
            }
            if (obj is DateTime dt)
            {
                return $"'{dt.ToString("yyyy-MM-dd HH:mm:ss")}'";
            }
            return $"'{obj.ToString()}'";
        }
        public void RebuildDb()
        {
            string folder = "C:\\Workspace\\VisualStudio\\WPFProject\\WPFProject\\bin\\x64\\Debug\\Config\\DataBoard";
            var startDate = DateTime.Parse("2025-10-01");
            var endDate = startDate.AddDays(1);
            SqliteHelper sourceHelper = new SqliteHelper("C:\\Workspace\\VisualStudio\\WPFProject\\WPFProject\\bin\\x64\\Debug\\Config\\DataBoard\\DataLog.db");
            while (endDate < DateTime.Parse("2025-10-12"))
            {
                string targetFile = Path.Combine(folder, $"Data_{startDate.ToString("yyyyMMdd")}.db");
                SqliteHelper targetHelper = null;
                if (!File.Exists(targetFile))
                {
                    SqliteHelper.CreateDatabase(targetFile);
                    targetHelper = new SqliteHelper(targetFile);
                    targetHelper.ExecuteSql(@"CREATE TABLE IF NOT EXISTS serialData (
                    id INTEGER PRIMARY KEY AUTOINCREMENT,
                    sn TEXT NOT NULL,
                    model TEXT,
                    station TEXT NOT NULL,
                    step TEXT NOT NULL,
                    position TEXT,
                    limitDown TEXT,
                    limitUp TEXT,
                    unit TEXT,
                    offset TEXT,
                    value TEXT,
                    status TEXT,
                    finalStatus TEXT,
                    consumeTime TEXT,
                    totalConsumeTime TEXT,
                    time DATETIME NOT NULL);");
                    targetHelper.ExecuteSql(@"CREATE TABLE IF NOT EXISTS testResult (
                    id INTEGER PRIMARY KEY AUTOINCREMENT,
                    sn TEXT NOT NULL,
                    model TEXT,
                    station TEXT NOT NULL,
                    position TEXT,
                    status TEXT,
                    result TEXT NOT NULL,
                    time DATETIME NOT NULL);");
                    string sql = $@"SELECT sn,model,station,step,position,limitDown,limitUp,unit,offset,value,status,
finalStatus,consumeTime,totalConsumeTime,time
FROM serialData WHERE time>='{startDate.ToString("yyyy-MM-dd")}' AND time < '{endDate.ToString("yyyy-MM-dd")}';";
                    var dt = sourceHelper.GetDataTable(sql);
                    foreach (System.Data.DataRow dr in dt.Rows)
                    {
                        targetHelper.ExecuteSql($@"INSERT INTO serialData
(sn,model,station,step,position,limitDown,limitUp,unit,offset,value,status,finalStatus,consumeTime,totalConsumeTime,time) VALUES
({Conv(dr["sn"])}, {Conv(dr["model"])}, {Conv(dr["station"])}, {Conv(dr["step"])}, {Conv(dr["position"])}, {Conv(dr["limitDown"])}, {Conv(dr["limitUP"])},
{Conv(dr["unit"])}, {Conv(dr["offset"])}, {Conv(dr["value"])}, {Conv(dr["status"])}, {Conv(dr["finalStatus"])}, {Conv(dr["consumeTime"])},
{Conv(dr["totalConsumeTime"])}, {Conv(dr["time"])})");
                    }
                    sql = $@"SELECT sn,model,station,position,status,result,time FROM testResult WHERE time>='{startDate.ToString("yyyy-MM-dd")}'
AND time < '{endDate.ToString("yyyy-MM-dd")}';";
                    dt = sourceHelper.GetDataTable(sql);
                    foreach (System.Data.DataRow dr in dt.Rows)
                    {
                        targetHelper.ExecuteSql($@"INSERT INTO testResult
(sn,model,station,position,status,result,time) VALUES
({Conv(dr["sn"])}, {Conv(dr["model"])}, {Conv(dr["station"])}, {Conv(dr["position"])}, {Conv(dr["status"])}, {Conv(dr["result"])}, {Conv(dr["time"])})");
                    }
                    targetHelper.Dispose();
                    startDate = startDate.AddDays(1);
                    endDate = endDate.AddDays(1);
                }
            }
            sourceHelper.Dispose();
        }
        private void Button_Click(object sender, RoutedEventArgs e)
        {
            Task.Run(() =>
Plugin.DataBoard/DataBoard.xaml.cs
@@ -10,6 +10,7 @@
using System.Drawing;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
@@ -76,14 +77,37 @@
            }
        }
        public static string[] GetDbFiles(string folderPath, DateTime startTime, DateTime endTime)
        {
            if (!Directory.Exists(folderPath))
            {
                return new string[0];
            }
            string startName = $"Data_{startTime.ToString("yyyyMMdd")}";
            string endName = $"Data_{endTime.ToString("yyyyMMdd")}";
            var oldFiles = Directory.GetFiles(folderPath, "Data_*.db");
            oldFiles = oldFiles.Where(file =>
            {
                var fileName = Path.GetFileNameWithoutExtension(file);
                bool flag = true;
                if (startTime != null) flag &= (string.Compare(fileName, startName) == 1 || fileName == startName);
                if (endTime != null) flag &= (string.Compare(endName, fileName) == 1 || fileName == endName);
                return flag;
            }).ToArray();
            return oldFiles;
        }
        private List<SerialData> SelectSerialDataByTime(DateTime start, DateTime end)
        {
            string[] files = DbFileHelper.GetDbFiles(BaseFolder, start, end);
            var s1 = DateTime.Now;
            string[] files = GetDbFiles(BaseFolder, start, end);
            if (files == null || files.Length <= 0)
            {
                return new List<SerialData>();
            }
            string sql = $"SELECT * FROM serialData WHERE time BETWEEN '{start:yyyy-MM-dd HH:mm:ss}' AND '{end:yyyy-MM-dd HH:mm:ss}';";
            string sql = $"SELECT * FROM serialData WHERE time BETWEEN '{start:yyyy-MM-dd HH:mm:ss}' AND '{end:yyyy-MM-dd HH:mm:ss}' ORDER BY time DESC;";
            DataTable dt = new DataTable();
            foreach (string file in files)
@@ -93,12 +117,16 @@
                    dt.Merge(helper.GetDataTable(sql));
                }
            }
            return BeanUtil.ReadBeanFromDataTable<SerialData>(dt);
            var s2 = DateTime.Now;
            log.Info($"SelectSerialDataByTime Cast: {(s2 - s1).TotalMilliseconds}");
            var data = BeanUtil.ReadBeanFromDataTable<SerialData>(dt);
            log.Info($"Bean Cast: {(DateTime.Now - s2).TotalMilliseconds}");
            return data;
        }
        private List<SerialData> SelectResultDataByTime(DateTime start, DateTime end)
        {
            string[] files = DbFileHelper.GetDbFiles(BaseFolder, start, end);
            string[] files = GetDbFiles(BaseFolder, start, end);
            if (files == null || files.Length <= 0)
            {
                return new List<SerialData>();
@@ -110,7 +138,8 @@
            {
                using (SqliteHelper helper = new SqliteHelper(file))
                {
                    dt.Merge(helper.GetDataTable(sql));
                    var temp = helper.GetDataTable(sql);
                    dt.Merge(temp);
                }
            }
            return BeanUtil.ReadBeanFromDataTable<SerialData>(dt);
@@ -137,81 +166,135 @@
            webview2.CoreWebView2?.ExecuteScriptAsync($"resize({width},{height})");
        }
        //private void RefreshChart0(DateTime start, DateTime end)
        //{
        //    dtp00.SelectedDateTime = start;
        //    dtp01.SelectedDateTime = end;
        //    Task.Run(() =>
        //    {
        //        try
        //        {
        //            var st = DateTime.Now;
        //            List<SerialData> datas = SelectSerialDataByTime(start, end);
        //            log.Info($"0 Select Data cast: {(DateTime.Now-st).TotalMilliseconds}");
        //            datas.OrderByDescending(e => e.time);
        //            int all = 0, output = 0;
        //            HashSet<string> snCache = new HashSet<string>();
        //            foreach (var data in datas)
        //            {
        //                if (snCache.Contains(data.sn)) continue;
        //                snCache.Add(data.sn);
        //                all++;
        //                if (data.status == "Passed") output++;
        //            }
        //            var fields = $"['{this.lang("bd.input")}','{this.lang("bd.output")}']";
        //            Dispatcher.BeginInvoke(new Action(() =>
        //            {
        //                webview2.CoreWebView2?.ExecuteScriptAsync($"drawChart0({fields}, [{all},{output}])");
        //            }));
        //        }
        //        catch (Exception ex)
        //        {
        //            log.Error("RefreshChart0 Error.", ex);
        //            ToastHelper.Error("表1刷新失败!");
        //        }
        //    });
        //}
        private void RefreshChart0(DateTime start, DateTime end)
        {
            try
            dtp00.SelectedDateTime = start;
            dtp01.SelectedDateTime = end;
            Task.Run(() =>
            {
                dtp00.SelectedDateTime = start;
                dtp01.SelectedDateTime = end;
                List<SerialData> datas = SelectSerialDataByTime(start, end);
                datas.OrderByDescending(e => e.time);
                int all = 0, output = 0;
                HashSet<string> snCache = new HashSet<string>();
                foreach (var data in datas)
                try
                {
                    if (snCache.Contains(data.sn)) continue;
                    snCache.Add(data.sn);
                    all++;
                    if (data.status == "Passed") output++;
                    Dispatcher.BeginInvoke(new Action(() =>
                    {
                        webview2.CoreWebView2?.ExecuteScriptAsync($"loading0('加载中,请稍后...')");
                    }));
                    List<SerialData> datas = SelectResultDataByTime(start, end);
                    datas.OrderByDescending(e => e.time);
                    int all = 0, output = 0;
                    HashSet<string> snCache = new HashSet<string>();
                    foreach (var data in datas)
                    {
                        if (snCache.Contains(data.sn)) continue;
                        snCache.Add(data.sn);
                        all++;
                        if (data.result == "Passed") output++;
                    }
                    var fields = $"['{this.lang("bd.input")}','{this.lang("bd.output")}']";
                    Dispatcher.BeginInvoke(new Action(() =>
                    {
                        webview2.CoreWebView2?.ExecuteScriptAsync($"drawChart0({fields}, [{all},{output}])");
                    }));
                }
                var fields = $"['{this.lang("bd.input")}','{this.lang("bd.output")}']";
                webview2.CoreWebView2?.ExecuteScriptAsync($"drawChart0({fields}, [{all},{output}])");
            }
            catch (Exception ex)
            {
                log.Error("RefreshChart0 Error.", ex);
                ToastHelper.Error("表1刷新失败!");
            }
                catch (Exception ex)
                {
                    log.Error("RefreshChart0 Error.", ex);
                    ToastHelper.Error("表1刷新失败!");
                }
            });
        }
        private void RefreshChart1(DateTime start, DateTime end)
        {
            try
            dtp10.SelectedDateTime = start;
            dtp11.SelectedDateTime = end;
            Task.Run(() =>
            {
                dtp10.SelectedDateTime = start;
                dtp11.SelectedDateTime = end;
                List<SerialData> datas = SelectResultDataByTime(start, end);
                try
                {
                    Dispatcher.BeginInvoke(new Action(() =>
                    {
                        webview2.CoreWebView2?.ExecuteScriptAsync($"loading1('加载中,请稍后...')");
                    }));
                    List<SerialData> datas = SelectResultDataByTime(start, end);
                datas = datas.OrderByDescending(e => e.time).ToList();
                Dictionary<string, int?> pieData = new Dictionary<string, int?> {
                    datas = datas.OrderByDescending(e => e.time).ToList();
                    Dictionary<string, int?> pieData = new Dictionary<string, int?> {
                { "Passed",0},
                { "Failed",0},
                //{ "Action",0},
                //{ "Wait",0},
            };
                //if (datas == null || datas.Count <= 0)
                //{
                //    pieData = new Dictionary<string, int?> {
                //    { "Passed",null},
                //    { "Failed",null},
                //};
                //}
                    //if (datas == null || datas.Count <= 0)
                    //{
                    //    pieData = new Dictionary<string, int?> {
                    //    { "Passed",null},
                    //    { "Failed",null},
                    //};
                    //}
                HashSet<string> snCache = new HashSet<string>();
                foreach (var data in datas)
                {
                    if (snCache.Contains(data.sn)) continue;
                    snCache.Add(data.sn);
                    if (pieData.ContainsKey(data.result))
                    HashSet<string> snCache = new HashSet<string>();
                    foreach (var data in datas)
                    {
                        pieData[data.result]++;
                        if (snCache.Contains(data.sn)) continue;
                        snCache.Add(data.sn);
                        if (pieData.ContainsKey(data.result))
                        {
                            pieData[data.result]++;
                        }
                    }
                    string res = "[";
                    foreach (var key in pieData.Keys)
                    {
                        res += $"{{name:'{key}',value:{pieData[key]}}},";
                    }
                    res += "]";
                    Dispatcher.BeginInvoke(new Action(() =>
                    {
                        webview2.CoreWebView2?.ExecuteScriptAsync($"drawChart1({res})");
                    }));
                }
                string res = "[";
                foreach (var key in pieData.Keys)
                catch (Exception ex)
                {
                    res += $"{{name:'{key}',value:{pieData[key]}}},";
                    log.Error("RefreshChart1 Error.", ex);
                    ToastHelper.Error("表2刷新失败!");
                }
                res += "]";
                webview2.CoreWebView2?.ExecuteScriptAsync($"drawChart1({res})");
            }
            catch (Exception ex)
            {
                log.Error("RefreshChart1 Error.", ex);
                ToastHelper.Error("表2刷新失败!");
            }
            });
        }
        //private void RefreshChart2(DateTime start, DateTime end)
@@ -282,129 +365,147 @@
        private void RefreshChart2(DateTime start, DateTime end)
        {
            try
            dtp20.SelectedDateTime = start;
            dtp21.SelectedDateTime = end;
            Task.Run(() =>
            {
                dtp20.SelectedDateTime = start;
                dtp21.SelectedDateTime = end;
                List<SerialData> datas = SelectResultDataByTime(start, end);
                var groups =
                    datas.GroupBy(d => d.time.ToString("yyyy-MM-dd"));
                Dictionary<string, PassRateData> res = new Dictionary<string, PassRateData>();
                foreach (var group in groups)
                try
                {
                    Dictionary<string, string> snStatusCache = new Dictionary<string, string>();
                    double all = 0, oneTimePass = 0, passed = 0;
                    var groupDatas = group.ToList();
                    groupDatas = groupDatas.OrderByDescending(e => e.time).ToList();
                    foreach (var data in groupDatas)
                    Dispatcher.BeginInvoke(new Action(() =>
                    {
                        if (!snStatusCache.ContainsKey(data.sn))
                        webview2.CoreWebView2?.ExecuteScriptAsync($"loading2('加载中,请稍后...')");
                    }));
                    List<SerialData> datas = SelectResultDataByTime(start, end);
                    var groups =
                        datas.GroupBy(d => d.time.ToString("yyyy-MM-dd"));
                    Dictionary<string, PassRateData> res = new Dictionary<string, PassRateData>();
                    foreach (var group in groups)
                    {
                        Dictionary<string, string> snStatusCache = new Dictionary<string, string>();
                        double all = 0, oneTimePass = 0, passed = 0;
                        var groupDatas = group.ToList();
                        groupDatas = groupDatas.OrderByDescending(e => e.time).ToList();
                        foreach (var data in groupDatas)
                        {
                            snStatusCache.Add(data.sn, data.result);
                            all++;
                            if (data.result == "Passed")
                            if (!snStatusCache.ContainsKey(data.sn))
                            {
                                passed++;
                                oneTimePass++;
                                snStatusCache.Add(data.sn, data.result);
                                all++;
                                if (data.result == "Passed")
                                {
                                    passed++;
                                    oneTimePass++;
                                }
                            }
                            else
                            {
                                if (snStatusCache[data.sn] == "Passed")
                                {
                                    if (oneTimePass > 0) oneTimePass--;
                                    snStatusCache[data.sn] = "NoOnePass";
                                }
                            }
                        }
                        else
                        res.Add(group.Key, new PassRateData
                        {
                            if (snStatusCache[data.sn] == "Passed")
                            {
                                if (oneTimePass > 0) oneTimePass--;
                                snStatusCache[data.sn] = "NoOnePass";
                            }
                        }
                            time = group.Key,
                            onePassRate = oneTimePass / all * 100,
                            passRate = passed / all * 100,
                        });
                    }
                    res.Add(group.Key, new PassRateData
                    string fields = "[", vals0 = "[", vals1 = "[";
                    foreach (var key in res.Keys)
                    {
                        time = group.Key,
                        onePassRate = oneTimePass / all * 100,
                        passRate = passed / all * 100,
                    });
                        fields += $"'{key}',";
                        vals0 += $"{res[key].onePassRate},";
                        vals1 += $"{res[key].passRate},";
                    }
                    fields += "]";
                    vals0 += "]";
                    vals1 += "]";
                    var names = $"['{this.lang("bd.onePassRate")}','{this.lang("bd.passRate")}']";
                    Dispatcher.BeginInvoke(new Action(() =>
                    {
                        webview2.CoreWebView2?.ExecuteScriptAsync($"drawChart2({fields},{names},{vals0},{vals1})");
                    }));
                }
                string fields = "[", vals0 = "[", vals1 = "[";
                foreach (var key in res.Keys)
                catch (Exception ex)
                {
                    fields += $"'{key}',";
                    vals0 += $"{res[key].onePassRate},";
                    vals1 += $"{res[key].passRate},";
                    log.Error("RefreshChart2 Error.", ex);
                    ToastHelper.Error("表3刷新失败!");
                }
                fields += "]";
                vals0 += "]";
                vals1 += "]";
                var names = $"['{this.lang("bd.onePassRate")}','{this.lang("bd.passRate")}']";
                webview2.CoreWebView2?.ExecuteScriptAsync($"drawChart2({fields},{names},{vals0},{vals1})");
            }
            catch (Exception ex)
            {
                log.Error("RefreshChart2 Error.", ex);
                ToastHelper.Error("表3刷新失败!");
            }
            });
        }
        private void RefreshChart3(DateTime start, DateTime end)
        {
            try
            dtp30.SelectedDateTime = start;
            dtp31.SelectedDateTime = end;
            Task.Run(() =>
            {
                dtp30.SelectedDateTime = start;
                dtp31.SelectedDateTime = end;
                List<SerialData> datas = SelectSerialDataByTime(start, end);
                datas = datas.OrderByDescending(e => e.time).ToList();
                Dictionary<string, SimpleData> pieData = new Dictionary<string, SimpleData>();
                List<SimpleData> datasource = new List<SimpleData>();
                //HashSet<string> snCache = new HashSet<string>();
                foreach (var data in datas)
                try
                {
                    //if (snCache.Contains(data.sn)) continue;
                    if (data.status != "Failed") continue;
                    if (pieData.ContainsKey(data.step))
                    Dispatcher.BeginInvoke(new Action(() =>
                    {
                        pieData[data.step].Value++;
                    }
                    else
                        webview2.CoreWebView2?.ExecuteScriptAsync($"loading3('加载中,请稍后...')");
                    }));
                    var st = DateTime.Now;
                    List<SerialData> datas = SelectSerialDataByTime(start, end);
                    var s2 = DateTime.Now;
                    log.Info($"RefreshChart3 Data Cast {(s2 - st).TotalMilliseconds}");
                    Dictionary<string, SimpleData> pieData = new Dictionary<string, SimpleData>();
                    List<SimpleData> datasource = new List<SimpleData>();
                    //HashSet<string> snCache = new HashSet<string>();
                    foreach (var data in datas)
                    {
                        pieData[data.step] = new SimpleData
                        //if (snCache.Contains(data.sn)) continue;
                        if (data.status != "Failed") continue;
                        if (pieData.ContainsKey(data.step))
                        {
                            Name = data.step,
                            Value = 1
                        };
                        datasource.Add(pieData[data.step]);
                            pieData[data.step].Value++;
                        }
                        else
                        {
                            pieData[data.step] = new SimpleData
                            {
                                Name = data.step,
                                Value = 1
                            };
                            datasource.Add(pieData[data.step]);
                        }
                    }
                }
                datasource = datasource.OrderByDescending(e => e.Value).ToList();
                if (datasource.Count > 10)
                {
                    datasource = datasource.Take(10).ToList();
                }
                    datasource = datasource.OrderByDescending(e => e.Value).ToList();
                    if (datasource.Count > 10)
                    {
                        datasource = datasource.Take(10).ToList();
                    }
                    var s3 = DateTime.Now;
                    log.Info($"RefreshChart3 Build {(s3 - s2).TotalMilliseconds}");
                string names = string.Empty, vals = string.Empty;
                foreach (var data in datasource)
                {
                    names += $"'{data.Name}',";
                    vals += $"{data.Value},";
                    string names = string.Empty, vals = string.Empty;
                    foreach (var data in datasource)
                    {
                        names += $"'{data.Name}',";
                        vals += $"{data.Value},";
                    }
                    Dispatcher.BeginInvoke(new Action(() =>
                    {
                        webview2.CoreWebView2?.ExecuteScriptAsync($"drawChart3([{names}],[{vals}])");
                    }));
                }
                webview2.CoreWebView2?.ExecuteScriptAsync($"drawChart3([{names}],[{vals}])");
            }
            catch (Exception ex)
            {
                log.Error("RefreshChart3 Error.", ex);
                ToastHelper.Error("表4刷新失败!");
            }
                catch (Exception ex)
                {
                    log.Error("RefreshChart3 Error.", ex);
                    ToastHelper.Error("表4刷新失败!");
                }
            });
        }
        private void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
Plugin.DataBoard/Libs/Static/test.html
@@ -6,6 +6,140 @@
<head>
    <title>Simple Surface</title>
    <meta charset="utf-8">
    <style type="text/css">
        /* 对 loading  整体布局*/
        .loading-wrapper {
            padding: 16px;
            background-color: rgba(0, 0, 0, 0.8);
            border-radius: 10px;
            color: #fff;
            text-align: center;
            display: flex;
            flex-direction: column;
            justify-content: space-evenly;
            position: absolute;
            left: 50%;
            top: 50%;
            transform: translate(-50%, -50%);
            background: rgba(0, 0, 0, 0.5);
        }
        /* loading 动画图像父级的布局 */
        .loading-view {
            height: 40px;
            position: relative;
        }
        .loading-msg {
            font-size: 14px;
            margin: 8px 0 0 0;
            position: relative;
        }
        .container1,
        .container2 {
            width: 40px;
            height: 40px;
            position: absolute;
            /* border: 1px solid #ff0000; */
            top: 0;
            left: 50%;
            margin-left: -20px;
            position: absolute;
        }
        .container2 {
            transform: rotate(45deg);
        }
        /* 第1种动画和第二种的相同部分,其中的 animation 也相同 */
        .circle,
        .bounce {
            width: 10px;
            height: 10px;
            background-color: #fff;
            border-radius: 50%;
            /* 设置小球动画 */
            animation: loading 1.2s both infinite;
        }
        .circle {
            position: absolute;
        }
        /* 设置小球的相对位置 */
        .circle1 {
            top: 0;
            left: 0;
        }
        .circle2 {
            top: 0;
            right: 0;
        }
        .circle3 {
            bottom: 0;
            right: 0;
        }
        .circle4 {
            bottom: 0;
            left: 0;
        }
        /* 设置延时 */
        .container1 .circle1 {
            animation-delay: 0s;
        }
        .container2 .circle1 {
            animation-delay: 0.2s;
        }
        .container1 .circle2 {
            animation-delay: 0.3s;
        }
        .container2 .circle2 {
            animation-delay: 0.4s;
        }
        .container1 .circle3 {
            animation-delay: 0.5s;
        }
        .container2 .circle3 {
            animation-delay: 0.6s;
        }
        .container1 .circle4 {
            animation-delay: 0.7s;
        }
        .container2 .circle5 {
            animation-delay: 0.8s;
        }
        @keyframes loading {
            0% {
                transform: scale(0);
            }
            40% {
                transform: scale(1);
            }
            80% {
                transform: scale(0);
            }
            100% {
                transform: scale(0);
            }
        }
    </style>
    <script src="echarts.min.js"></script>
    <script type="text/javascript">
        const color = ['#22448b'];
@@ -37,12 +171,15 @@
        .chart {
            height: 100%;
            width: 25%;
            position: relative;
        }
    </style>
</head>
<body>
    <div id="container">
        <div id="chart0" class="chart"></div>
        <div id="chart0" class="chart">
        </div>
        <div id="chart1" class="chart"></div>
        <div id="chart2" class="chart"></div>
        <div id="chart3" class="chart"></div>
@@ -75,9 +212,33 @@
                }
            ]
        };
        var chart0 = echarts.init(dom0);
        var chart0 = null;
        function loading0(msg) {
            if (chart0 != null) {
                chart0.dispose();
                chart0 = null;
            }
            dom0.innerHTML = `<div class="loading-wrapper">
    <div class="loading-view">
        <div class="container1">
            <div class="circle circle1"></div>
            <div class="circle circle2"></div>
            <div class="circle circle3"></div>
            <div class="circle circle4"></div>
        </div>
        <div class="container2">
            <div class="circle circle1"></div>
            <div class="circle circle2"></div>
            <div class="circle circle3"></div>
            <div class="circle circle4"></div>
        </div>
    </div>
    <h2 class="loading-msg">${msg}</h2>
</div>`
        }
        function drawChart0(fields, data) {
            chart0 = echarts.init(dom0);
            opt0.xAxis.data = fields;
            opt0.series[0].data = data;
            chart0.setOption(opt0);
@@ -125,9 +286,33 @@
                }
            ]
        };
        var chart1 = echarts.init(dom1);
        var chart1 = null;
        function loading1(msg) {
            if (chart1 != null) {
                chart1.dispose();
                chart1 = null;
            }
            dom1.innerHTML = `<div class="loading-wrapper">
    <div class="loading-view">
        <div class="container1">
            <div class="circle circle1"></div>
            <div class="circle circle2"></div>
            <div class="circle circle3"></div>
            <div class="circle circle4"></div>
        </div>
        <div class="container2">
            <div class="circle circle1"></div>
            <div class="circle circle2"></div>
            <div class="circle circle3"></div>
            <div class="circle circle4"></div>
        </div>
    </div>
    <h2 class="loading-msg">${msg}</h2>
</div>`
        }
        function drawChart1(data) {
            chart1 = echarts.init(dom1);
            opt1.series[0].data = data;
            chart1.setOption(opt1);
        }
@@ -175,9 +360,33 @@
                }
            ]
        };
        var chart2 = echarts.init(dom2);
        var chart2 = null;
        function loading2(msg) {
            if (chart2 != null) {
                chart2.dispose();
                chart2 = null;
            }
            dom2.innerHTML = `<div class="loading-wrapper">
    <div class="loading-view">
        <div class="container1">
            <div class="circle circle1"></div>
            <div class="circle circle2"></div>
            <div class="circle circle3"></div>
            <div class="circle circle4"></div>
        </div>
        <div class="container2">
            <div class="circle circle1"></div>
            <div class="circle circle2"></div>
            <div class="circle circle3"></div>
            <div class="circle circle4"></div>
        </div>
    </div>
    <h2 class="loading-msg">${msg}</h2>
</div>`
        }
        function drawChart2(label, names, data0, data1) {
            chart2 = echarts.init(dom2)
            opt2.xAxis.data = label;
            opt2.series[0].name = names[0];
            opt2.series[0].data = data0;
@@ -242,9 +451,34 @@
                }
            ]
        };
        var chart3 = echarts.init(dom3);
        var chart3 = null;
        function loading3(msg) {
            if (chart3 != null) {
                chart3.dispose();
                chart3 = null;
            }
            dom3.innerHTML = `<div class="loading-wrapper">
    <div class="loading-view">
        <div class="container1">
            <div class="circle circle1"></div>
            <div class="circle circle2"></div>
            <div class="circle circle3"></div>
            <div class="circle circle4"></div>
        </div>
        <div class="container2">
            <div class="circle circle1"></div>
            <div class="circle circle2"></div>
            <div class="circle circle3"></div>
            <div class="circle circle4"></div>
        </div>
    </div>
    <h2 class="loading-msg">${msg}</h2>
</div>`
        }
        function drawChart3(label, data) {
            chart3 = echarts.init(dom3);
            opt3.yAxis.data = label;
            opt3.series[0].data = data;
            chart3.setOption(opt3);
Plugin.TsExecution/TsExecution.xaml.cs
@@ -263,6 +263,7 @@
            }
        }
        private string tsModelPath;
        private string dataLogPath;
        private SqliteHelper sqliteHelper;
@@ -284,23 +285,25 @@
            base.SetBaseFolder(folder);
        }
        public override void InitFinish()
        private void CheckSqlHelper()
        {
            this.ReadCount(out int pass, out int fail);
            this.DisplayCount(pass, fail);
            InitProducts();
            ReadConfig();
            if (!File.Exists(dataLogPath))
            string today = DateTime.Now.ToString("yyyyMMdd");
            string dbPath = Path.Combine(dataLogPath, $"Data_{today}.db");
            if (!File.Exists(dbPath))
            {
                SqliteHelper.CreateDatabase(dataLogPath);
                SqliteHelper.CreateDatabase(dbPath);
            }
            sqliteHelper = new SqliteHelper(dataLogPath);
            sqliteHelper.ExecuteSql(@"CREATE TABLE IF NOT EXISTS serialData (
            if (sqliteHelper == null || sqliteHelper.DataBaseFile != dbPath)
            {
                sqliteHelper?.Dispose();
                sqliteHelper = new SqliteHelper(dbPath);
                sqliteHelper.ExecuteSql(@"CREATE TABLE IF NOT EXISTS serialData (
                    id INTEGER PRIMARY KEY AUTOINCREMENT,
                    sn TEXT NOT NULL,
                    model TEXT,
                    station TEXT NOT NULL,
                    step TEXT NOT NULL,
                    position TEXT,
                    limitDown TEXT,
                    limitUp TEXT,
                    unit TEXT,
@@ -311,48 +314,25 @@
                    consumeTime TEXT, 
                    totalConsumeTime TEXT, 
                    time DATETIME NOT NULL);");
            sqliteHelper.ExecuteSql(@"CREATE TABLE IF NOT EXISTS testResult (
                sqliteHelper.ExecuteSql(@"CREATE TABLE IF NOT EXISTS testResult (
                    id INTEGER PRIMARY KEY AUTOINCREMENT,
                    sn TEXT NOT NULL,
                    model TEXT,
                    station TEXT NOT NULL,
                    position TEXT,
                    status TEXT,
                    result TEXT NOT NULL, 
                    time DATETIME NOT NULL);");
            try
            {
                sqliteHelper.ExecuteSql("alter table serialData add column 'model' TEXT default NULL;");
            }
            catch
            {
        }
            }
            try
            {
                sqliteHelper.ExecuteSql("alter table serialData add column 'position' TEXT default NULL;");
            }
            catch
            {
            }
            try
            {
                sqliteHelper.ExecuteSql("alter table testResult add column 'position' TEXT default NULL;");
            }
            catch
            {
            }
            try
            {
                sqliteHelper.ExecuteSql("alter table testResult add column 'status' TEXT default NULL;");
            }
            catch
            {
            }
        public override void InitFinish()
        {
            this.ReadCount(out int pass, out int fail);
            this.DisplayCount(pass, fail);
            InitProducts();
            ReadConfig();
            CheckSqlHelper();
            base.InitFinish();
        }
@@ -865,8 +845,21 @@
            }
            catch
            {
                dataLogPath = Path.Combine(PathHelper.ConfigFolder, "DataBoard", "DataLog.db");
                dataLogPath = Path.Combine(PathHelper.ConfigFolder, "DataBoard");
            }
            try
            {
                value = ParameterManager.GetCommonParameterByName("TsModelPath");
                tsModelPath = value.Value;
            }
            catch
            {
                //@"C:\Program Files\National Instruments\TestStand 2020\Components\Models\TestStandModels\ParallelModel.seq"
                tsModelPath = @"C:\Users\Public\Documents\National Instruments\TestStand 2020 (64-bit)\Components\Models\ParallelModel.seq";
            }
            //Convert.ToBoolean(IniFileHelper.ReadIniData("Config", "EnableMes", "true", CountIniPath));
            //EnableCanLog = Convert.ToBoolean(IniFileHelper.ReadIniData("Config", "EnableCanLog", "true", CountIniPath));
@@ -1001,8 +994,7 @@
            {
                engine?.Globals.SetValString("StationState.ProductModel", 1, SelectedProduct?.Name);
                engine?.CommitGlobalsToDisk(promptOnSaveConflicts: false);
                //this.modeSeq = this.engine.GetSequenceFileEx(@"C:\Program Files\National Instruments\TestStand 2020\Components\Models\TestStandModels\ParallelModel.seq");
                this.modeSeq = this.engine.GetSequenceFileEx(@"C:\Users\Public\Documents\National Instruments\TestStand 2020 (64-bit)\Components\Models\ParallelModel.seq");
                this.modeSeq = this.engine.GetSequenceFileEx(tsModelPath);
                this.testSeq = this.engine.GetSequenceFileEx(this.testSeqFile, 2, TypeConflictHandlerTypes.ConflictHandler_UseGlobalType);
                this.execution = this.engine.NewExecution(this.testSeq, "Test UUTs", this.modeSeq, false, 0, null, null, null);
                IsTesting = true;
@@ -1180,6 +1172,7 @@
        public void SavePositionDB(int index, string status)
        {
            CheckSqlHelper();
            var socket = this.tsSockets[index];
            var time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
            string snr = socket.Barcode;
SimpleUtil/HMI.Simple.Util/ParameterUtil.cs
@@ -9,11 +9,11 @@
    public class ParameterUtil
    {
        /// <summary>
        ///
        /// 根据路径和参数名获取参数值
        /// </summary>
        /// <param name="path"></param>
        /// <param name="name"></param>
        /// <returns></returns>
        /// <param name="path">路径</param>
        /// <param name="name">参数名</param>
        /// <returns>参数值</returns>
        public static string GetCommonParameterValue(string path, string name)
        {
            if (!File.Exists(path)) return null;
@@ -26,8 +26,8 @@
        /// <summary>
        /// 获取HMI里配置的参数
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        /// <param name="name">参数名</param>
        /// <returns>参数值</returns>
        public static string GetCommonParameterValue(string name, out string errMsg)
        {
            errMsg = string.Empty;
@@ -101,8 +101,10 @@
        /// <summary>
        /// 将参数写进Engine的StationGlobals.StationState
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        /// <param name="xmlPath">绝对路径</param>
        /// <param name="instance">TestStand上下文</param>
        /// <param name="errMsg">异常信息</param>
        /// <returns>是否成功</returns>
        public static bool WriteConfigToContext(string xmlPath, object instance, out string errMsg)
        {
            errMsg = string.Empty;
SimpleUtil/HMI.Simple.Util/ProductUtil.cs
@@ -28,13 +28,20 @@
            }
        }
        /// <summary>
        /// 根据产品型号写配置
        /// </summary>
        /// <param name="model">产品型号</param>
        /// <param name="instance">TestStand上下文</param>
        /// <param name="errMsg">异常信息</param>
        /// <returns>是否成功</returns>
        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", $"Parameter_{model}.data");
                string path = Path.Combine(dir, "Config", "Product", "Parameter", $"Parameter_{model}.data");
                return WriteConfigToContextByPath(path, instance, out errMsg);
            }
            catch (Exception ex)
@@ -44,6 +51,13 @@
            }
        }
        /// <summary>
        /// 根据路径写配置
        /// </summary>
        /// <param name="path">绝对路径</param>
        /// <param name="instance">TestStand上下文</param>
        /// <param name="errMsg">异常信息</param>
        /// <returns>是否成功</returns>
        public static bool WriteConfigToContextByPath(string path, object instance, out string errMsg)
        {
            errMsg = string.Empty;
@@ -51,7 +65,7 @@
            {
                if (!File.Exists(path))
                {
                    throw new Exception("未找到该配置,请检查");
                    throw new Exception($"未找到该配置[{path}],请检查");
                }
                List<ParameterData> datas = XmlHelper.DeserializeFromXml<List<ParameterData>>(path);
                foreach (var data in datas)