alielie
2026-06-04 771a0e7b6b1276087515bbe94de28358cb92963b
Plugin.DataBoard/DataBoard.xaml.cs
@@ -5,12 +5,23 @@
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;
@@ -19,6 +30,54 @@
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>
@@ -28,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" },
@@ -49,6 +408,7 @@
        private string DataFolder;
        public override void InitFinish()
        {
            chart2.Legend = new CustomLegend();
            iniFile = Path.Combine(BaseFolder, "Config.ini");
            if (!File.Exists(iniFile))
            {
@@ -60,7 +420,10 @@
        private void PluginControl_Loaded(object sender, RoutedEventArgs e)
        {
            cb0.SelectedIndex = 0;
            cb1.SelectedIndex = 0;
            cb2.SelectedIndex = 0;
            cb3.SelectedIndex = 0;
        }
        private void LoadProduct()
@@ -223,7 +586,7 @@
                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};";
            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)
@@ -240,22 +603,23 @@
        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)
@@ -305,7 +669,7 @@
                {
                    Dispatcher.BeginInvoke(new Action(() =>
                    {
                        webview2.CoreWebView2?.ExecuteScriptAsync($"loading0('加载中,请稍后...')");
                        // webview2.CoreWebView2?.ExecuteScriptAsync($"loading0('加载中,请稍后...')");
                    }));
                    List<SerialData> datas = SelectResultDataByTime(model, start, end);
                    datas.OrderByDescending(e => e.time);
@@ -321,7 +685,8 @@
                    var fields = $"['{this.lang("bd.input")}','{this.lang("bd.output")}']";
                    Dispatcher.BeginInvoke(new Action(() =>
                    {
                        webview2.CoreWebView2?.ExecuteScriptAsync($"drawChart0({fields}, [{all},{output}])");
                        Chart0_series[0].Values = new int[] { all, output };
                        //webview2.CoreWebView2?.ExecuteScriptAsync($"drawChart0({fields}, [{all},{output}])");
                    }));
                }
                catch (Exception ex)
@@ -343,12 +708,12 @@
                {
                    Dispatcher.BeginInvoke(new Action(() =>
                    {
                        webview2.CoreWebView2?.ExecuteScriptAsync($"loading1('加载中,请稍后...')");
                        //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?> {
                    Dictionary<string, int> pieData = new Dictionary<string, int> {
                { "Passed",0},
                { "Failed",0},
                //{ "Action",0},
@@ -380,7 +745,9 @@
                    res += "]";
                    Dispatcher.BeginInvoke(new Action(() =>
                    {
                        webview2.CoreWebView2?.ExecuteScriptAsync($"drawChart1({res})");
                        Chart1_series[0].Values = new int[] { pieData["Passed"] };
                        Chart1_series[1].Values = new int[] { pieData["Failed"] };
                        // webview2.CoreWebView2?.ExecuteScriptAsync($"drawChart1({res})");
                    }));
                }
                catch (Exception ex)
@@ -468,7 +835,7 @@
                {
                    Dispatcher.BeginInvoke(new Action(() =>
                    {
                        webview2.CoreWebView2?.ExecuteScriptAsync($"loading2('加载中,请稍后...')");
                       // webview2.CoreWebView2?.ExecuteScriptAsync($"loading2('加载中,请稍后...')");
                    }));
                    List<SerialData> datas = SelectResultDataByTime(model, start, end);
@@ -513,20 +880,46 @@
                        });
                    }
                    string fields = "[", vals0 = "[", vals1 = "[";
                    foreach (var key in res.Keys)
                    {
                        fields += $"'{key}',";
                        vals0 += $"{res[key].onePassRate},";
                        vals1 += $"{res[key].passRate},";
                    }
                    fields += "]";
                    vals0 += "]";
                    vals1 += "]";
                    //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(() =>
                    {
                        webview2.CoreWebView2?.ExecuteScriptAsync($"drawChart2({fields},{names},{vals0},{vals1})");
                        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})");
                    }));
                }
                catch (Exception ex)
@@ -548,7 +941,7 @@
                {
                    Dispatcher.BeginInvoke(new Action(() =>
                    {
                        webview2.CoreWebView2?.ExecuteScriptAsync($"loading3('加载中,请稍后...')");
                        //webview2.CoreWebView2?.ExecuteScriptAsync($"loading3('加载中,请稍后...')");
                    }));
                    var st = DateTime.Now;
                    List<SerialData> datas = SelectSerialDataByTime(model, start, end);
@@ -585,15 +978,16 @@
                    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}])");
                        Chart3_series[0].Values = datasource.OrderBy(x => x.Value).ToList(); ;
                        //webview2.CoreWebView2?.ExecuteScriptAsync($"drawChart3([{names}],[{vals}])");
                    }));
                }
                catch (Exception ex)