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