using 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.Collections.ObjectModel;
namespace Plugin.BurnIn
{
public class SeriesConfig
{
public static readonly float FontSize = 12;
///
/// 数据名
///
public string Name { get; set; }
public string YAxesName { get; set; }
public int YAxesIndex { get; set; }
}
public class CustomLegend : SKDefaultLegend
{
protected override Layout 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)series.GetMiniatureGeometry(null);
Children = new List> {
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 configCache = new Dictionary();
private Dictionary seriesCache = new Dictionary();
private Dictionary> valueCache = new Dictionary>();
private Dictionary minValueCache = new Dictionary();
private Dictionary maxValueCache = new Dictionary();
private ObservableCollection series = new ObservableCollection();
public ObservableCollection Series
{
get => series;
set
{
Set(ref series, value);
}
}
public ObservableCollection YAxes { get; set; } = new ObservableCollection();
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();
valueCache[sConfig.Name] = values;
var ls = new LineSeries
{
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();
valueCache[name] = values;
var ls = new LineSeries
{
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 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('汉')
//};
}
}
}
}