From 9d1660270272180cd71147d6ddd53d1382643d2e Mon Sep 17 00:00:00 2001
From: wenxiangchao <wenxiangchao@ihengxin.com.cn>
Date: 星期一, 02 十一月 2026 18:00:14 +0800
Subject: [PATCH] merge: 并入远程 Initial commit 作为历史根节点

---
 Plugin.TestResult/LiveChart.xaml.cs |  519 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 519 insertions(+), 0 deletions(-)

diff --git a/Plugin.TestResult/LiveChart.xaml.cs b/Plugin.TestResult/LiveChart.xaml.cs
new file mode 100644
index 0000000..60fe146
--- /dev/null
+++ b/Plugin.TestResult/LiveChart.xaml.cs
@@ -0,0 +1,519 @@
+锘縰sing HMI.Base;
+using LiveChartsCore;
+using LiveChartsCore.Defaults;
+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 SkiaSharp;
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Linq;
+
+namespace Plugin.TestResult_WPF
+{
+    public class SeriesConfig
+    {
+        public static readonly float FontSize = 12;
+        /// <summary>
+        /// 鏁版嵁鍚�
+        /// </summary>
+        public string Name { get; set; }
+        public string YAxesName { get; set; }
+        public int YAxesIndex { get; set; }
+    }
+
+    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 = SeriesConfig.FontSize,
+                Paint = new SolidColorPaint(SKColors.Black){
+                    SKTypeface = SKFontManager.Default.MatchCharacter('姹�')
+                },
+                Padding = new Padding(5,0,10,0),
+                VerticalAlign = Align.Middle,
+                HorizontalAlign = Align.Start
+            }};
+        }
+    }
+
+    public partial class LiveChart : NotifyControl
+    {
+        private static SKColor[] colors = new SKColor[] {
+            //new SKColor(229, 84, 62),
+            //new SKColor(72, 143, 239),
+            //new SKColor(147, 196, 83),
+            new SKColor(255, 0, 0),
+            new SKColor(0, 0, 255),
+            new SKColor(0, 255, 0),
+            new SKColor(76, 184, 210),
+            new SKColor(246, 198, 50),
+        };
+
+        private DateTime startTime;
+        private string configText;
+        private bool showLegend;
+        private bool enableResize;
+
+        private SolidColorPaint tooltipTextPaint = new SolidColorPaint(SKColors.Black)
+        {
+            SKTypeface = SKFontManager.Default.MatchCharacter('姹�')
+        };
+        public SolidColorPaint TooltipTextPaint
+        {
+            get => tooltipTextPaint;
+            set
+            {
+                Set(ref tooltipTextPaint, value);
+            }
+        }
+
+        private double tooltipTextSize = SeriesConfig.FontSize;
+        public double TooltipTextSize
+        {
+            get => tooltipTextSize;
+            set
+            {
+                Set(ref tooltipTextSize, value);
+            }
+        }
+
+        private Dictionary<string, SeriesConfig> configCache = new Dictionary<string, SeriesConfig>();
+
+        private Dictionary<string, ISeries> seriesCache = new Dictionary<string, ISeries>();
+        private Dictionary<string, List<DateTimePoint>> valueCache = new Dictionary<string, List<DateTimePoint>>();
+
+        private Dictionary<string, double> minValueCache = new Dictionary<string, double>();
+        private Dictionary<string, double> maxValueCache = new Dictionary<string, double>();
+
+        private ObservableCollection<ISeries> series = new ObservableCollection<ISeries>();
+        public ObservableCollection<ISeries> Series
+        {
+            get => series;
+            set
+            {
+
+                Set(ref series, value);
+            }
+        }
+
+        public ObservableCollection<ICartesianAxis> YAxes { get; set; } = new ObservableCollection<ICartesianAxis>();
+
+        private Axis[] xAxes;
+        public Axis[] XAxes
+        {
+            get => xAxes; set
+            {
+                Set(ref xAxes, value);
+            }
+        }
+
+        public DrawMarginFrame DrawMarginFrame => new DrawMarginFrame()
+        {
+            Fill = new SolidColorPaint(new SKColor(223, 223, 223)),
+            Stroke = new SolidColorPaint(new SKColor(180, 180, 180), 1)
+        };
+
+        public object Sync { get; } = new object();
+
+        private int CreateYAxis(string name)
+        {
+            for (int i = 0; i < YAxes.Count; i++)
+            {
+                if (YAxes[i].Name == name)
+                {
+                    return i;
+                }
+            }
+            if (YAxes.Count == 0)
+            {
+                YAxes.Add(new Axis
+                {
+                    Name = name,
+                    MinStep = 1,
+                    TextSize = SeriesConfig.FontSize,
+                    NameTextSize = SeriesConfig.FontSize,
+                    NamePaint = new SolidColorPaint(new SKColor(0, 0, 0))
+                    {
+                        SKTypeface = SKFontManager.Default.MatchCharacter('姹�')
+                    },
+                    Padding = new Padding(0, 0, 5, 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
+                    },
+                });
+            }
+            else
+            {
+                YAxes.Add(new Axis
+                {
+                    Name = name,
+                    Position = LiveChartsCore.Measure.AxisPosition.End,
+                    MinStep = 1,
+                    TextSize = SeriesConfig.FontSize,
+                    NameTextSize = SeriesConfig.FontSize,
+                    NamePaint = new SolidColorPaint(new SKColor(0, 0, 0))
+                    {
+                        SKTypeface = SKFontManager.Default.MatchCharacter('姹�')
+                    },
+                    Padding = new Padding(5, 0, 0, 0),
+                    LabelsPaint = new SolidColorPaint(new SKColor(0, 0, 0)),
+                    TicksPaint = new SolidColorPaint
+                    {
+                        Color = SKColor.Empty,
+                        StrokeThickness = 0
+                    },
+                    ShowSeparatorLines = false,
+                });
+            }
+            return YAxes.Count - 1;
+        }
+
+        public void RefreshConfig(string configStr)
+        {
+            configText = configStr;
+            configCache.Clear();
+            seriesCache.Clear();
+            valueCache.Clear();
+            minValueCache.Clear();
+            maxValueCache.Clear();
+            Series.Clear();
+            YAxes.Clear();
+            startTime = DateTime.MinValue;
+            try
+            {
+                string[] arr = configStr.Split(';');
+                for (int i = 0; i < arr.Length; i++)
+                {
+                    var config = arr[i];
+                    string[] datas = config.Split(',');
+                    var sConfig = new SeriesConfig();
+                    if (datas.Length >= 2)
+                    {
+                        sConfig.YAxesName = datas[1];
+                    }
+                    else
+                    {
+                        sConfig.YAxesName = datas[0];
+                    }
+
+                    sConfig.Name = datas[0];
+                    sConfig.YAxesIndex = CreateYAxis(sConfig.YAxesName);
+                    if (!string.IsNullOrEmpty(sConfig.Name))
+                    {
+                        configCache[sConfig.Name] = sConfig;
+                        var values = new List<DateTimePoint>();
+                        valueCache[sConfig.Name] = values;
+
+                        var ls = new LineSeries<DateTimePoint>
+                        {
+                            Name = sConfig.Name,
+                            Values = values,
+                            ScalesYAt = sConfig.YAxesIndex,
+                            Fill = null,
+                            GeometryFill = null,
+                            GeometryStroke = null,
+                            LineSmoothness = 0,
+                            Stroke = new SolidColorPaint(GetColor(i)) { StrokeThickness = 0.8f },
+                        };
+                        seriesCache[sConfig.Name] = ls;
+                        Series.Add(ls);
+                    }
+                }
+                if (showLegend)
+                {
+                    chart.LegendPosition = LiveChartsCore.Measure.LegendPosition.Top;
+                    chart.Legend = new CustomLegend();
+                }
+                chart.TooltipTextPaint = new SolidColorPaint(SKColors.Black)
+                {
+                    SKTypeface = SKFontManager.Default.MatchCharacter('姹�')
+                };
+            }
+            catch (Exception ex)
+            {
+
+            }
+        }
+
+        public LiveChart(string configStr, bool showLegend = true, bool enableResize = false)
+        {
+            DataContext = this;
+            this.showLegend = showLegend;
+            this.enableResize = enableResize;
+
+            var xAxis = new DateTimeAxis(TimeSpan.FromSeconds(10), Formatter)
+            {
+                Padding = new Padding(0),
+                TextSize = SeriesConfig.FontSize,
+                NameTextSize = SeriesConfig.FontSize,
+                CustomSeparators = GetSeparators(),
+                AnimationsSpeed = TimeSpan.FromMilliseconds(0),
+                SeparatorsPaint = new SolidColorPaint(SKColors.Black.WithAlpha(30)),
+            };
+
+            XAxes = new Axis[] { xAxis };
+            InitializeComponent();
+            RefreshConfig(configStr);
+        }
+
+        private double[] GetSeparators()
+        {
+            if (valueCache.Count <= 0)
+            {
+                return new double[] { DateTime.Now.Ticks };
+            }
+            if (valueCache.Values.First().Count <= 5)
+            {
+                //return new double[] { now.Ticks };
+                return valueCache.Values.First().Select(e => (double)e.DateTime.Ticks).ToArray();
+            }
+            var now = valueCache.Values.First().Last().DateTime;
+
+            var step = (now - startTime).TotalSeconds / 6;
+
+            return
+            new double[] {
+                now.AddSeconds(-5*step).Ticks,
+                now.AddSeconds(-4*step).Ticks,
+                now.AddSeconds(-3*step).Ticks,
+                now.AddSeconds(-2*step).Ticks,
+                now.AddSeconds(-step).Ticks,
+            };
+        }
+
+        private static string Formatter(DateTime date)
+        {
+            //var secsAgo = (DateTime.Now - date).TotalSeconds;
+            //return secsAgo < 1
+            //    ? "now"
+            //    : $"{secsAgo:N0}s ago";
+            return date.ToString("HH:mm:ss");
+        }
+
+        private void RefreshChart()
+        {
+            XAxes[0].CustomSeparators = GetSeparators();
+        }
+
+        private double GetMaxByName(string name)
+        {
+            return maxValueCache.ContainsKey(name) ? maxValueCache[name] : double.MinValue;
+        }
+
+        private double GetMinByName(string name)
+        {
+            return minValueCache.ContainsKey(name) ? minValueCache[name] : double.MaxValue;
+        }
+
+        //public void AddData(string name, double value)
+        //{
+        //    var now = DateTime.Now;
+        //    if (valueCache.ContainsKey(name))
+        //    {
+        //        var config = configCache[name];
+        //        valueCache[name].Add(new DateTimePoint(now, value));
+
+
+        //        minValueCache[config.YAxesName] = Math.Min(GetMinByName(config.YAxesName), value);
+        //        maxValueCache[config.YAxesName] = Math.Max(GetMaxByName(config.YAxesName), value);
+
+        //        YAxes[config.YAxesIndex].MinLimit = minValueCache[config.YAxesName] - 1;
+        //        YAxes[config.YAxesIndex].MaxLimit = maxValueCache[config.YAxesName] + 1;
+
+        //        RefreshChart();
+        //    }
+        //    if (startTime == DateTime.MinValue)
+        //    {
+        //        startTime = now;
+        //    }
+        //}
+
+        private SKColor GetColor(int index)
+        {
+            if (index >= colors.Length)
+            {
+                return colors[index % colors.Length];
+            }
+            return colors[index];
+        }
+
+        private void AddNewSeries(string name, string YName)
+        {
+            var config = new SeriesConfig
+            {
+                Name = name,
+                YAxesName = YName,
+                YAxesIndex = CreateYAxis(YName)
+            };
+            configCache[name] = config;
+
+            var values = new List<DateTimePoint>();
+            valueCache[name] = values;
+
+            var ls = new LineSeries<DateTimePoint>
+            {
+                Name = name,
+                Values = values,
+                ScalesYAt = config.YAxesIndex,
+                Fill = null,
+                GeometryFill = null,
+                GeometryStroke = null,
+                LineSmoothness = 0,
+                Stroke = new SolidColorPaint(GetColor(Series.Count)) { StrokeThickness = 0.8f },
+            };
+            seriesCache[Name] = ls;
+            Series.Add(ls);
+        }
+
+        public void AddDynamicData(string name, string YName, DateTime now, double value)
+        {
+            if (!valueCache.ContainsKey(name))
+            {
+                AddNewSeries(name, YName);
+            }
+
+            var config = configCache[name];
+            valueCache[name].Add(new DateTimePoint(now, value));
+
+
+            minValueCache[config.YAxesName] = Math.Min(GetMinByName(config.YAxesName), value);
+            maxValueCache[config.YAxesName] = Math.Max(GetMaxByName(config.YAxesName), value);
+
+            YAxes[config.YAxesIndex].MinLimit = minValueCache[config.YAxesName] - 1;
+            YAxes[config.YAxesIndex].MaxLimit = maxValueCache[config.YAxesName] + 1;
+
+            RefreshChart();
+
+
+            if (startTime == DateTime.MinValue)
+            {
+                startTime = now;
+            }
+        }
+
+        public void AddData(string name, DateTime now, double value)
+        {
+            if (valueCache.ContainsKey(name))
+            {
+                var config = configCache[name];
+                valueCache[name].Add(new DateTimePoint(now, value));
+
+
+                minValueCache[config.YAxesName] = Math.Min(GetMinByName(config.YAxesName), value);
+                maxValueCache[config.YAxesName] = Math.Max(GetMaxByName(config.YAxesName), value);
+
+                YAxes[config.YAxesIndex].MinLimit = minValueCache[config.YAxesName] - 1;
+                YAxes[config.YAxesIndex].MaxLimit = maxValueCache[config.YAxesName] + 1;
+
+                RefreshChart();
+            }
+            if (startTime == DateTime.MinValue)
+            {
+                startTime = now;
+            }
+        }
+
+        public void Clear()
+        {
+            RefreshConfig(configText);
+        }
+
+        public bool CanHandle(string name)
+        {
+            return configCache.ContainsKey(name);
+        }
+
+        public List<double> GetSingleData(string name)
+        {
+            if (valueCache.ContainsKey(name))
+            {
+                return valueCache[name].Select(d => d.Value.Value).ToList();
+            }
+            return null;
+        }
+
+        private void UserControl_Loaded(object sender, System.Windows.RoutedEventArgs e)
+        {
+            //refreshTimer.Start();
+            //timer?.Start();
+        }
+
+        private void UserControl_Unloaded(object sender, System.Windows.RoutedEventArgs e)
+        {
+            //timer?.Stop();
+        }
+
+        private void UserControl_SizeChanged(object sender, System.Windows.SizeChangedEventArgs e)
+        {
+            if (enableResize)
+            {
+                var size = FontSizeConverter.CountFontSize(12, ActualHeight, 92);
+                foreach (var y in YAxes)
+                {
+                    y.TextSize = size;
+                    y.NameTextSize = size;
+                }
+
+                foreach (var x in XAxes)
+                {
+                    x.TextSize = size;
+                    x.NameTextSize = size;
+                }
+                TooltipTextSize = size;
+                //TooltipTextPaint = new SolidColorPaint(SKColors.Black)
+                //{
+                //    SKTypeface = SKFontManager.Default.MatchCharacter('姹�')
+                //};
+            }
+        }
+    }
+}

--
Gitblit v1.9.1