alielie
2026-06-04 771a0e7b6b1276087515bbe94de28358cb92963b
Plugin.DataBoard/DataBoard.xaml.cs
@@ -1,19 +1,83 @@
using HMI.Base;
using HMI.Core.Entity;
using HMI.Core.Manager;
using HMI.Core.Setting;
using HMI.Core.Util;
using HMI.UI.Util;
using HMI.Util;
using LiveChartsCore;
using LiveChartsCore.Drawing;
using LiveChartsCore.Drawing.Layouts;
using LiveChartsCore.Kernel.Sketches;
using LiveChartsCore.SkiaSharpView;
using LiveChartsCore.SkiaSharpView.Drawing;
using LiveChartsCore.SkiaSharpView.Drawing.Geometries;
using LiveChartsCore.SkiaSharpView.Drawing.Layouts;
using LiveChartsCore.SkiaSharpView.Painting;
using LiveChartsCore.SkiaSharpView.SKCharts;
using LiveChartsCore.SkiaSharpView.VisualElements;
using Microsoft.Web.WebView2.Core;
using SkiaSharp;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
namespace Plugin.DataBoard
{
    public class CustomLegend : SKDefaultLegend
    {
        protected override Layout<SkiaSharpDrawingContext> GetLayout(LiveChartsCore.Chart chart)
        {
            var stackLayout = new StackLayout
            {
                Orientation = ContainerOrientation.Horizontal,
                Padding = new Padding(0, 10, 0, 0),
                HorizontalAlignment = Align.Start,
                VerticalAlignment = Align.Middle,
            };
            foreach (var series in chart.Series.Where(x => x.IsVisibleAtLegend))
            {
                stackLayout.Children.Add(new LegendItem(series));
            }
            return stackLayout;
        }
    }
    public class LegendItem : StackLayout
    {
        public LegendItem(ISeries series)
        {
            Orientation = ContainerOrientation.Horizontal;
            Padding = new Padding(0);
            VerticalAlignment = Align.Start;
            HorizontalAlignment = Align.Middle;
            Opacity = series.IsVisible ? 1 : 0.5f;
            var miniature = (IDrawnElement<SkiaSharpDrawingContext>)series.GetMiniatureGeometry(null);
            Children = new List<IDrawnElement<SkiaSharpDrawingContext>> {
                miniature,
            new LabelGeometry
            {
                Text = series.Name,
                TextSize = 12,
                Paint = new SolidColorPaint(SKColors.Black){
                    SKTypeface = SKFontManager.Default.MatchCharacter('汉')
                },
                Padding = new Padding(5,0,10,0),
                VerticalAlign = Align.Middle,
                HorizontalAlign = Align.Start
            }};
        }
    }
    /// <summary>
    /// UserControl1.xaml 的交互逻辑
    /// </summary>
@@ -23,8 +87,308 @@
        {
            DataContext = this;
            InitializeComponent();
            webview2.DefaultBackgroundColor = Color.Transparent;//Color.FromArgb(243, 249, 255);
            //webview2.DefaultBackgroundColor = Color.Transparent;//Color.FromArgb(243, 249, 255);
        }
        private SolidColorPaint tooltipTextPaint = new SolidColorPaint(SKColors.Black)
        {
            SKTypeface = SKFontManager.Default.MatchCharacter('汉')
        };
        public SolidColorPaint TooltipTextPaint
        {
            get => tooltipTextPaint;
            set
            {
                Set(ref tooltipTextPaint, value);
            }
        }
        #region chart0
        public LabelVisual Chart0Title { get; set; } = new LabelVisual
       {
           Text = "投入产出统计",
           TextSize = 20,
           Paint = new SolidColorPaint(new SKColor(0, 0, 0))
           {
               SKTypeface = SKFontManager.Default.MatchCharacter('汉')
           },
           Padding = new LiveChartsCore.Drawing.Padding(15)
       };
        private ISeries[] chart0_series = new ISeries[] {
            new ColumnSeries<int> {
                Fill= new SolidColorPaint(new SKColor(34, 68, 139)),
                DataLabelsPaint  = new SolidColorPaint(new SKColor(0, 0, 0)),
                DataLabelsPosition = LiveChartsCore.Measure.DataLabelsPosition.Top,
                Values = new int[]{ 0,0} }
        };
        public ISeries[] Chart0_series
        {
            get => chart0_series;
            set
            {
                Set(ref chart0_series, value);
            }
        }
        private ObservableCollection<Axis> chart0_yAxes = new ObservableCollection<Axis> {
            new Axis
            {
                MinStep = 1,
                MinLimit = 0,
                LabelsPaint = new SolidColorPaint(new SKColor(0, 0, 0)),
                SeparatorsPaint = new SolidColorPaint
                {
                    Color = SKColors.Black.WithAlpha(30),
                    StrokeThickness = 0.5f
                },
                TicksPaint = new SolidColorPaint
                {
                    Color = SKColor.Empty,
                    StrokeThickness = 0
                },
            }
        };
        public ObservableCollection<Axis> Chart0_yAxes
        {
            get => chart0_yAxes;
            set
            {
                Set(ref chart0_yAxes, value);
            }
        }
        private Axis[] chart0_xAxes = new Axis[] {
            new Axis
        {
            Labels = new string[]{"投入", "产出" },
            LabelsPaint=new SolidColorPaint(new SKColor(0, 0, 0))
            {
                SKTypeface = SKFontManager.Default.MatchCharacter('汉')
            },
            LabelsRotation = 0,
            SeparatorsAtCenter = false,
            TicksPaint = new SolidColorPaint(new SKColor(35, 35, 35)),
            TicksAtCenter = true,
            ForceStepToMin = true,
            MinStep = 1
        }
        };
        public Axis[] Chart0_xAxes
        {
            get => chart0_xAxes;
            set
            {
                Set(ref chart0_xAxes, value);
            }
        }
        #endregion
        #region chart1
        public LabelVisual Chart1Title { get; set; } = new LabelVisual
        {
            Text = "产品状态统计",
            TextSize = 20,
            Paint = new SolidColorPaint(new SKColor(0, 0, 0))
            {
                SKTypeface = SKFontManager.Default.MatchCharacter('汉')
            },
            Padding = new LiveChartsCore.Drawing.Padding(15)
        };
        public ISeries[] Chart1_series { get; set; } = new PieSeries<int>[]
        {
            new PieSeries<int> {
                Name="Passed",
                Values = new int[]{ 0},
            DataLabelsPosition = LiveChartsCore.Measure.PolarLabelsPosition.Outer,
             DataLabelsSize = 15,
             DataLabelsPaint = new SolidColorPaint(new SKColor(30, 30, 30)){
                 SKTypeface = SKFontManager.Default.MatchCharacter('汉')
             },
             Fill = new SolidColorPaint(new SKColor(0, 128, 0)),
             DataLabelsFormatter =
                point =>point.StackedValue.Total>0?
                    $"Passed\r\n计数:{point.Coordinate.PrimaryValue}\r\n占比:{point.StackedValue.Share:P2}":"",
             ToolTipLabelFormatter = point => $"{point.StackedValue.Share:P2}"
            },
            new PieSeries<int> { Values = new int[]{ 0} ,
                Name="Failed",
            DataLabelsPosition = LiveChartsCore.Measure.PolarLabelsPosition.Outer,
             DataLabelsSize = 15,
             DataLabelsPaint = new SolidColorPaint(new SKColor(30, 30, 30)){
                 SKTypeface = SKFontManager.Default.MatchCharacter('汉')
             },
             Fill = new SolidColorPaint(new SKColor(255, 0, 0)),
             DataLabelsFormatter =
                point =>point.StackedValue.Total>0?
                    $"Failed\r\n计数:{point.Coordinate.PrimaryValue}\r\n占比:{point.StackedValue.Share:P2}":"",
             ToolTipLabelFormatter = point => $"{point.StackedValue.Share:P2}"},
        };
        #endregion
        #region chart2
        public LabelVisual Chart2Title { get; set; } = new LabelVisual
        {
            Text = "良率统计",
            TextSize = 20,
            Paint = new SolidColorPaint(new SKColor(0, 0, 0))
            {
                SKTypeface = SKFontManager.Default.MatchCharacter('汉')
            },
            Padding = new LiveChartsCore.Drawing.Padding(15)
        };
        public ISeries[] Chart2_series { get; set; } = new ISeries[]
        {
            new LineSeries<double> {
                Name="直通率",
                Values = new double[]{ 0},
                DataLabelsPosition = LiveChartsCore.Measure.DataLabelsPosition.Top,
                DataLabelsFormatter =
                    point => $"直通率:{point.Coordinate.PrimaryValue:f2}%",
                DataLabelsSize = 15,
                DataLabelsPaint = new SolidColorPaint(new SKColor(30, 30, 30)){
                    SKTypeface = SKFontManager.Default.MatchCharacter('汉')
                },
                Stroke = new SolidColorPaint(new SKColor(255,0,0)){
                    StrokeThickness = 2
                },
                Fill = null,
                GeometryStroke = new SolidColorPaint(new SKColor(255,0,0)){
                    StrokeThickness = 2
                },
                GeometrySize=5
            },
            new LineSeries<double> {
                Name="良率",
                Values = new double[]{ 0},
            DataLabelsPosition = LiveChartsCore.Measure.DataLabelsPosition.Bottom,
            DataLabelsFormatter =
                point => $"良率:{point.Coordinate.PrimaryValue:f2}%",
             DataLabelsSize = 10,
             DataLabelsPaint = new SolidColorPaint(new SKColor(30, 30, 30)){
                 SKTypeface = SKFontManager.Default.MatchCharacter('汉')
             },
                Stroke = new SolidColorPaint(new SKColor(34, 68, 139)){
                    StrokeThickness = 2
                },
                GeometryStroke = new SolidColorPaint(new SKColor(34, 68, 139)){
                    StrokeThickness = 2
                },
             Fill = null,
            },
        };
        private ObservableCollection<Axis> chart2_yAxes = new ObservableCollection<Axis>  {
            new Axis
            {
                LabelsPaint = new SolidColorPaint(new SKColor(0, 0, 0)),
                SeparatorsPaint = new SolidColorPaint
                {
                    Color = SKColors.Black.WithAlpha(30),
                    StrokeThickness = 0.5f
                },
                TicksPaint = new SolidColorPaint
                {
                    Color = SKColor.Empty,
                    StrokeThickness = 0
                },
                MinLimit = 0
            }
        };
        public ObservableCollection<Axis> Chart2_yAxes
        {
            get => chart2_yAxes;
            set
            {
                Set(ref chart2_yAxes, value);
            }
        }
        private ObservableCollection<ICartesianAxis> chart2_xAxes = new ObservableCollection<ICartesianAxis> {
            new Axis
            {
            Labels = new string[]{"" },
            }
        };
        public ObservableCollection<ICartesianAxis> Chart2_xAxes {
            get => chart2_xAxes;
            set {
                Set(ref chart2_xAxes, value);
            }
        }
        #endregion
        #region chart3
        public LabelVisual Chart3Title { get; set; } = new LabelVisual
        {
            Text = "TOP10不良统计",
            TextSize = 20,
            Paint = new SolidColorPaint(new SKColor(0, 0, 0))
            {
                SKTypeface = SKFontManager.Default.MatchCharacter('汉')
            },
            Padding = new LiveChartsCore.Drawing.Padding(15)
        };
        public ISeries[] Chart3_series { get; set; } = new ISeries[]
        {
            new RowSeries<SimpleData> {
                Values = new SimpleData[]{},
                DataLabelsPosition = LiveChartsCore.Measure.DataLabelsPosition.Left,
                DataLabelsFormatter =
                    point => $"{point.Model.Name}",
                DataLabelsSize = 15,
                DataLabelsPadding = new Padding(30,0,0,0),
                DataLabelsTranslate = new LvcPoint(1, 0),
                // DataLabelsTranslate = new LvcPoint(-1, 0),
                DataLabelsPaint = new SolidColorPaint(new SKColor(255,0,0)){
                    SKTypeface = SKFontManager.Default.MatchCharacter('汉')
                },
                YToolTipLabelFormatter = point => $"{point.Model.Name} {point.Model.Value}"
            },
        };
        public ObservableCollection<Axis> chart3_yAxes = new ObservableCollection<Axis>  {
            new Axis
            {
                IsVisible = false
            }
        };
        public ObservableCollection<Axis> Chart3_yAxes
        {
            get => chart3_yAxes;
            set
            {
                Set(ref chart3_yAxes, value);
            }
        }
        private ObservableCollection<ICartesianAxis> chart3_xAxes = new ObservableCollection<ICartesianAxis> {
            new Axis { SeparatorsPaint = new SolidColorPaint(new SKColor(220, 220, 220)) }
        };
        public ObservableCollection<ICartesianAxis> Chart3_xAxes
        {
            get => chart3_xAxes;
            set
            {
                Set(ref chart3_xAxes, value);
            }
        }
        #endregion
        private ObservableCollection<OptionData> _options = new ObservableCollection<OptionData> {
            new OptionData { Name = LangUtil.Get("bd.today"), Value = "today" },
@@ -39,9 +403,85 @@
            set { Set(ref _options, value); }
        }
        private string iniFile;
        private string DataFolder;
        public override void InitFinish()
        {
            chart2.Legend = new CustomLegend();
            iniFile = Path.Combine(BaseFolder, "Config.ini");
            if (!File.Exists(iniFile))
            {
                File.Create(iniFile);
            }
            DataFolder = ParamSetting.DataLogFolder;
            LoadProduct();
        }
        private void PluginControl_Loaded(object sender, RoutedEventArgs e)
        {
            cb0.SelectedIndex = 0;
            cb1.SelectedIndex = 0;
            cb2.SelectedIndex = 0;
            cb3.SelectedIndex = 0;
        }
        private void LoadProduct()
        {
            List<ProductData> products = ProductManager.GetProducts<List<ProductData>>()
               ?? new List<ProductData>();
            this.modelSelect.Items.Clear();
            List<string> productSource = new List<string>();
            foreach (var prd in products)
            {
                this.modelSelect.Items.Add(prd.Name);
                productSource.Add(prd.Name);
            }
            string model = IniFileHelper.ReadIniData("Config", "Model", "", iniFile);
            if (!string.IsNullOrEmpty(model))
            {
                this.modelSelect.SelectedItem = model;
            }
            else if (products.Count > 0)
            {
                this.modelSelect.SelectedIndex = 0;
            }
        }
        private void RefreshAll()
        {
            try
            {
                var start0 = dtp00.SelectedDateTime ?? DateTime.Now;
                var end0 = dtp01.SelectedDateTime ?? DateTime.Now;
                RefreshChart0(start0, end0);
            }
            catch { }
            try
            {
                var start1 = dtp10.SelectedDateTime ?? DateTime.Now;
                var end1 = dtp11.SelectedDateTime ?? DateTime.Now;
                RefreshChart1(start1, end1);
            }
            catch { }
            try
            {
                var start2 = dtp20.SelectedDateTime ?? DateTime.Now;
                var end2 = dtp21.SelectedDateTime ?? DateTime.Now;
                RefreshChart2(start2, end2);
            }
            catch { }
            try
            {
                var start3 = dtp30.SelectedDateTime ?? DateTime.Now;
                var end3 = dtp31.SelectedDateTime ?? DateTime.Now;
                RefreshChart3(start3, end3);
            }
            catch { }
        }
        private void Button_Click(object sender, RoutedEventArgs e)
@@ -50,6 +490,9 @@
            DateTime start, end;
            switch (btn.Name)
            {
                case "btnAll":
                    RefreshAll();
                    break;
                case "btn0":
                    start = dtp00.SelectedDateTime ?? DateTime.Now;
                    end = dtp01.SelectedDateTime ?? DateTime.Now;
@@ -75,14 +518,44 @@
            }
        }
        private List<SerialData> SelectSerialDataByTime(DateTime start, DateTime end)
        public string[] GetDbFiles(DateTime startTime, DateTime endTime)
        {
            string[] files = DbFileHelper.GetDbFiles(BaseFolder, start, end);
            if (!Directory.Exists(DataFolder))
            {
                return new string[0];
            }
            string startName = $"Data_{startTime.ToString("yyyyMMdd")}";
            string endName = $"Data_{endTime.ToString("yyyyMMdd")}";
            var oldFiles = Directory.GetFiles(DataFolder, "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(string model, DateTime start, DateTime end)
        {
            var s1 = DateTime.Now;
            string[] files = GetDbFiles(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 segment = string.Empty;
            if (!string.IsNullOrEmpty(model))
            {
                segment = $"AND model = '{model}'";
            }
            string sql = $@"SELECT * FROM serialData WHERE time BETWEEN '{start:yyyy-MM-dd HH:mm:ss}' AND '{end:yyyy-MM-dd HH:mm:ss}'
{segment} ORDER BY time DESC;";
            DataTable dt = new DataTable();
            foreach (string file in files)
@@ -92,182 +565,437 @@
                    dt.Merge(helper.GetDataTable(sql));
                }
            }
            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(string model, DateTime start, DateTime end)
        {
            string[] files = GetDbFiles(start, end);
            if (files == null || files.Length <= 0)
            {
                return new List<SerialData>();
            }
            string segment = string.Empty;
            if (!string.IsNullOrEmpty(model))
            {
                segment = $"AND model = '{model}'";
            }
            string sql = $@"SELECT * FROM testResult WHERE time BETWEEN '{start:yyyy-MM-dd HH:mm:ss}' AND '{end:yyyy-MM-dd HH:mm:ss}' {segment} Order BY id DESC;";
            DataTable dt = new DataTable();
            foreach (string file in files)
            {
                using (SqliteHelper helper = new SqliteHelper(file))
                {
                    var temp = helper.GetDataTable(sql);
                    dt.Merge(temp);
                }
            }
            return BeanUtil.ReadBeanFromDataTable<SerialData>(dt);
        }
        private void webview2_Loaded(object sender, RoutedEventArgs e)
        {
            string path = Path.Combine(PathHelper.AppFolder, "Libs", "Static", "test.html");
            webview2.Source = new Uri(path);
            //webview2.EnsureCoreWebView2Async();
            // webview2.Source = new Uri(path);
        }
        private void Webview2_NavigationCompleted(object sender, CoreWebView2NavigationCompletedEventArgs e)
        {
            cb0.SelectedIndex = 0;
            cb1.SelectedIndex = 0;
            cb2.SelectedIndex = 0;
            cb3.SelectedIndex = 0;
            //cb0.SelectedIndex = 0;
            //cb1.SelectedIndex = 0;
            //cb2.SelectedIndex = 0;
            //cb3.SelectedIndex = 0;
        }
        private void webview2_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            var width = this.webview2.ActualWidth - 100;
            var height = this.webview2.ActualHeight;
            webview2.CoreWebView2?.ExecuteScriptAsync($"resize({width},{height})");
            //var width = this.webview2.ActualWidth - 100;
            //var height = this.webview2.ActualHeight;
            //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)
        {
            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)
            string model = modelSelect.SelectedItem?.ToString();
            Task.Run(() =>
            {
                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")}']";
            webview2.CoreWebView2?.ExecuteScriptAsync($"drawChart0({fields}, [{all},{output}])");
                try
                {
                    Dispatcher.BeginInvoke(new Action(() =>
                    {
                        // webview2.CoreWebView2?.ExecuteScriptAsync($"loading0('加载中,请稍后...')");
                    }));
                    List<SerialData> datas = SelectResultDataByTime(model, 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(() =>
                    {
                        Chart0_series[0].Values = new int[] { all, output };
                        //webview2.CoreWebView2?.ExecuteScriptAsync($"drawChart0({fields}, [{all},{output}])");
                    }));
                }
                catch (Exception ex)
                {
                    log.Error("RefreshChart0 Error.", ex);
                    ToastHelper.Error("表1刷新失败!");
                }
            });
        }
        private void RefreshChart1(DateTime start, DateTime end)
        {
            dtp10.SelectedDateTime = start;
            dtp11.SelectedDateTime = end;
            List<SerialData> datas = SelectSerialDataByTime(start, end);
            string model = modelSelect.SelectedItem?.ToString();
            Task.Run(() =>
            {
                try
                {
                    Dispatcher.BeginInvoke(new Action(() =>
                    {
                        //webview2.CoreWebView2?.ExecuteScriptAsync($"loading1('加载中,请稍后...')");
                    }));
                    List<SerialData> datas = SelectResultDataByTime(model, 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},
                //{ "Action",0},
                //{ "Wait",0},
            };
            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.status))
                {
                    pieData[data.status]++;
                    //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))
                        {
                            pieData[data.result]++;
                        }
                    }
                    string res = "[";
                    foreach (var key in pieData.Keys)
                    {
                        res += $"{{name:'{key}',value:{pieData[key]}}},";
                    }
                    res += "]";
                    Dispatcher.BeginInvoke(new Action(() =>
                    {
                        Chart1_series[0].Values = new int[] { pieData["Passed"] };
                        Chart1_series[1].Values = new int[] { pieData["Failed"] };
                        // webview2.CoreWebView2?.ExecuteScriptAsync($"drawChart1({res})");
                    }));
                }
            }
            string res = "[";
            foreach (var key in pieData.Keys)
            {
                res += $"{{name:'{key}',value:{pieData[key]}}},";
            }
            res += "]";
            webview2.CoreWebView2?.ExecuteScriptAsync($"drawChart1({res})");
                catch (Exception ex)
                {
                    log.Error("RefreshChart1 Error.", ex);
                    ToastHelper.Error("表2刷新失败!");
                }
            });
        }
        //private void RefreshChart2(DateTime start, DateTime end)
        //{
        //    dtp20.SelectedDateTime = start;
        //    dtp21.SelectedDateTime = end;
        //    List<SerialData> datas = SelectSerialDataByTime(start, end);
        //    var groups =
        //        datas.Where(e => e.status == "Passed" || e.status == "Failed").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.OrderBy(e => e.time).ToList();
        //        foreach (var data in groupDatas)
        //        {
        //            if (snStatusCache.ContainsKey(data.sn))
        //            {
        //                if (snStatusCache[data.sn] != "Passed" && data.status == "Passed")
        //                {
        //                    passed++;
        //                }
        //                else if (snStatusCache[data.sn] == "Passed" && data.status == "Passed")
        //                {
        //                    if (oneTimePass > 0) oneTimePass--;
        //                }
        //            }
        //            else
        //            {
        //                all++;
        //                if (data.status == "Passed")
        //                {
        //                    passed++;
        //                    oneTimePass++;
        //                }
        //                snStatusCache.Add(data.sn, data.status);
        //            }
        //        }
        //        res.Add(group.Key, new PassRateData
        //        {
        //            time = group.Key,
        //            onePassRate = oneTimePass / all * 100,
        //            passRate = passed / all * 100,
        //        });
        //    }
        //    string fields = "[", vals0 = "[", vals1 = "[";
        //    foreach (var key in res.Keys)
        //    {
        //        fields += $"'{key}',";
        //        vals0 += $"{res[key].onePassRate},";
        //        vals1 += $"{res[key].passRate},";
        //    }
        //    fields += "]";
        //    vals0 += "]";
        //    vals1 += "]";
        //    var names = $"['{this.lang("bd.onePassRate")}','{this.lang("bd.passRate")}']";
        //    webview2.CoreWebView2?.ExecuteScriptAsync($"drawChart2({fields},{names},{vals0},{vals1})");
        //}
        private void RefreshChart2(DateTime start, DateTime end)
        {
            dtp20.SelectedDateTime = start;
            dtp21.SelectedDateTime = end;
            List<SerialData> datas = SelectSerialDataByTime(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)
            string model = modelSelect.SelectedItem?.ToString();
            Task.Run(() =>
            {
                Dictionary<string, string> snStatusCache = new Dictionary<string, string>();
                double all = 0, oneTimePass = 0, passed = 0;
                var groupDatas = group.ToList();
                groupDatas = groupDatas.OrderBy(e => e.time).ToList();
                foreach (var data in groupDatas)
                try
                {
                    if (snStatusCache.ContainsKey(data.sn))
                    Dispatcher.BeginInvoke(new Action(() =>
                    {
                        if (snStatusCache[data.sn] != "Passed" && data.status == "Passed")
                        {
                            passed++;
                        }
                        else if (snStatusCache[data.sn] == "Passed" && data.status == "Passed")
                        {
                            oneTimePass--;
                        }
                    }
                    else
                       // webview2.CoreWebView2?.ExecuteScriptAsync($"loading2('加载中,请稍后...')");
                    }));
                    List<SerialData> datas = SelectResultDataByTime(model, 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)
                    {
                        all++;
                        if (data.status == "Passed")
                        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)
                        {
                            passed++;
                            oneTimePass++;
                            if (!snStatusCache.ContainsKey(data.sn))
                            {
                                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";
                                }
                            }
                        }
                        snStatusCache.Add(data.sn, data.status);
                        res.Add(group.Key, new PassRateData
                        {
                            time = group.Key,
                            onePassRate = oneTimePass / all * 100,
                            passRate = passed / all * 100,
                        });
                    }
                    //string fields = "[", vals0 = "[", vals1 = "[";
                    //foreach (var key in res.Keys)
                    //{
                    //    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(() =>
                    {
                        if (res.Count == 0)
                        {
                            res[DateTime.Now.ToString("yyyy-MM-dd")] = new PassRateData
                            {
                                onePassRate = 0,
                                passRate = 0
                            };
                        }
                        string[] keys = new string[res.Count];
                        double[] val0 = new double[res.Count];
                        double[] val1 = new double[res.Count];
                        int i = 0;
                        foreach (var key in res.Keys)
                        {
                            keys[i] = key;
                            val0[i] = res[key].onePassRate;
                            val1[i] = res[key].passRate;
                            i++;
                        }
                        Chart2_xAxes[0] = new Axis
                        {
                            Labels = keys,
                        };
                        Chart2_series[0].Values = val0;
                        Chart2_series[1].Values = val1;
                        //webview2.CoreWebView2?.ExecuteScriptAsync($"drawChart2({fields},{names},{vals0},{vals1})");
                    }));
                }
                res.Add(group.Key, new PassRateData
                catch (Exception ex)
                {
                    time = group.Key,
                    onePassRate = oneTimePass / all * 100,
                    passRate = passed / all * 100,
                });
            }
            string fields = "[", vals0 = "[", vals1 = "[";
            foreach (var key in res.Keys)
            {
                fields += $"'{key}',";
                vals0 += $"{res[key].onePassRate},";
                vals1 += $"{res[key].passRate},";
            }
            fields += "]";
            vals0 += "]";
            vals1 += "]";
            var names = $"['{this.lang("bd.onePassRate")}','{this.lang("bd.passRate")}']";
            webview2.CoreWebView2?.ExecuteScriptAsync($"drawChart2({fields},{names},{vals0},{vals1})");
                    log.Error("RefreshChart2 Error.", ex);
                    ToastHelper.Error("表3刷新失败!");
                }
            });
        }
        private void RefreshChart3(DateTime start, DateTime end)
        {
            dtp30.SelectedDateTime = start;
            dtp31.SelectedDateTime = end;
            List<SerialData> datas = SelectSerialDataByTime(start, end);
            datas = datas.OrderByDescending(e => e.time).ToList();
            Dictionary<string, int> pieData = new Dictionary<string, int>();
            HashSet<string> snCache = new HashSet<string>();
            foreach (var data in datas)
            string model = modelSelect.SelectedItem?.ToString();
            Task.Run(() =>
            {
                //if (snCache.Contains(data.sn)) continue;
                if (data.status != "Failed") continue;
                snCache.Add(data.sn);
                if (pieData.ContainsKey(data.step))
                try
                {
                    pieData[data.step]++;
                }
                else
                {
                    pieData[data.step] = 1;
                }
            }
            string names = string.Empty, vals = string.Empty;
            foreach (var key in pieData.Keys)
            {
                names += $"'{key}',";
                vals += $"{pieData[key]},";
            }
                    Dispatcher.BeginInvoke(new Action(() =>
                    {
                        //webview2.CoreWebView2?.ExecuteScriptAsync($"loading3('加载中,请稍后...')");
                    }));
                    var st = DateTime.Now;
                    List<SerialData> datas = SelectSerialDataByTime(model, start, end);
                    var s2 = DateTime.Now;
                    log.Info($"RefreshChart3 Data Cast {(s2 - st).TotalMilliseconds}");
            webview2.CoreWebView2?.ExecuteScriptAsync($"drawChart3([{names}],[{vals}])");
                    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)
                    {
                        //if (snCache.Contains(data.sn)) continue;
                        if (data.status != "Failed") continue;
                        if (pieData.ContainsKey(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();
                    }
                    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},";
                    //}
                    Dispatcher.BeginInvoke(new Action(() =>
                    {
                        Chart3_series[0].Values = datasource.OrderBy(x => x.Value).ToList(); ;
                        //webview2.CoreWebView2?.ExecuteScriptAsync($"drawChart3([{names}],[{vals}])");
                    }));
                }
                catch (Exception ex)
                {
                    log.Error("RefreshChart3 Error.", ex);
                    ToastHelper.Error("表4刷新失败!");
                }
            });
        }
        private void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
@@ -306,5 +1034,13 @@
                    break;
            }
        }
        private void modelSelect_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (e.AddedItems.Count > 0)
            {
                IniFileHelper.WriteIniData("Config", "Model", e.AddedItems[0].ToString(), iniFile);
            }
        }
    }
}