using Arction.Wpf.Charting;
using Arction.Wpf.Charting.Annotations;
using Arction.Wpf.Charting.Axes;
using Arction.Wpf.Charting.SeriesXY;
using Arction.Wpf.Charting.Views.ViewXY;
using CapMachine.Model;
using CapMachine.Wpf.Models;
using CapMachine.Wpf.PrismEvent;
using CapMachine.Wpf.Services;
using CapMachine.Wpf.ViewModels;
using ImTools;
using NLog;
using Prism.Events;
using System.Text;
using System.Timers;
using System.Windows.Controls;
using System.Windows.Media;
namespace CapMachine.Wpf.Views
{
///
/// RealTimeChartView.xaml 的交互逻辑
///
public partial class RealTimeChartView : UserControl
{
///
/// 定时器
///
static System.Timers.Timer CurTimer { get; set; }
private IEventAggregator _EventAggregator { get; set; }
public MachineRtDataService MachineRtDataService { get; }
public IFreeSql FreeSql { get; }
///
/// 曲线配置
///
public ChartConfig ChartConfigInfo { get; set; } = new ChartConfig();
///
/// 实例化函数
///
///
///
public RealTimeChartView(IEventAggregator eventAggregator, MachineRtDataService machineRtDataService, IFreeSql freeSql)
{
InitializeComponent();
LoadLighingChart();
LvChartRtValue.ItemsSource = ListChartRtSeries;
//事件服务
_EventAggregator = eventAggregator;
MachineRtDataService = machineRtDataService;
FreeSql = freeSql;
_EventAggregator.GetEvent().Subscribe(GetChartRtEvent);
_EventAggregator.GetEvent().Subscribe(ReLoadData);
}
///
/// 重新加载数据
/// 更新曲线和Y轴设置
///
///
///
private void ReLoadData(ChartActionType type)
{
//
switch (type)
{
case ChartActionType.ReLoadChart:
var ChartSelectedData = FreeSql.Select().Include(a => a.ConfigChartYAxis).ToList();
LoadChartConfigSelect(ChartSelectedData);
break;
default:
break;
}
}
///
/// 获取曲线的实时值
/// 数据是发布过来
///
///
///
private void GetChartRtEvent(List RtData)
{
ListChartRtValue = RtData;
//UpdateLightningChartData();
App.Current.Dispatcher.Invoke(() =>
{
foreach (var item in ListChartRtValue)
{
var Data = ListChartRtSeries.Find(a => a.Name == item.Name);
if (Data != null)
{
Data.AddValue(item.Value);
Data.Value = item.Value;
}
}
});
}
///
/// 时间序列
///
///
///
private void CurTimer_Elapsed(object? sender, ElapsedEventArgs e)
{
}
#region 开始测试
#endregion
#region 曲线配置
///
/// 当前曲线配置信息
///
public List CurListConfigChartSelect { get; set; }
///
/// 加载曲线配置信息
///
private void LoadChartConfigSelect(List configChartSelects)
{
//从配置入手对比目前的状态,针对配置新增
foreach (var itemConfig in configChartSelects)
{
//曲线增加时
var data = ListChartRtSeries.FindFirst(a => a.Name == itemConfig.Name);
if (data == null)
{
ListChartRtSeries.Add(new ChartRtSeries(lightningChart1, ChartConfigInfo, ListChartRtSeries, itemConfig));
}
else
{
//修改
}
}
////从目前的状态对比配置,针对删除项目
//foreach (var itemSeries in ListChartRtSeries)
//{
// var data = configChartSelects.FindFirst(a => a.Name == itemSeries.Name);
// if (data == null)
// {
// //查找删除的系列的Y轴是否还有其他的配置使用
// var configChartYAxis = configChartSelects.Find(a => a.ConfigChartYAxis.Name == itemSeries.ChartAxisY.Name);
// if (configChartYAxis == null)
// {
// //Y轴没有使用,则删除
// itemSeries.DeleteAxisY();
// }
// //未找到,则删除
// ListChartRtSeries.Remove(itemSeries);
// }
// else
// {
// //存在就不需要操作
// }
//}
//从目前的状态对比配置,针对删除项目
for (int i = 0; i < ListChartRtSeries.Count; i++)
{
var data = configChartSelects.FindFirst(a => a.Name == ListChartRtSeries[i].Name);
if (data == null)
{
//查找删除的系列的Y轴是否还有其他的配置使用-先删除Y轴
var configChartYAxis = configChartSelects.Find(a => a.ConfigChartYAxis.Name == ListChartRtSeries[i].ChartAxisY.Name);
if (configChartYAxis == null)
{
//Y轴没有使用,则删除
ListChartRtSeries[i].DeleteAxisY();
}
//未找到,则删除 曲线实例删除
ListChartRtSeries[i].DeleteSeries();
}
else
{
//存在就不需要操作
}
}
}
///
/// 实时曲线数据集合
///
public List ListChartRtValue { get; set; }
///
/// 曲线数据系列集合
///
public List ListChartRtSeries { get; set; } = new List();
///
/// Chart图表X轴的跨度-秒
///
private int ChartXDateTimeRange = 3600;
///
/// X轴滚动
///
private bool ScrollEnable = true;
///
/// 更新图表信息
///
private void UpdateLightningChartData()
{
//timeStamp = timeStamp.AddSeconds(-60);
foreach (var item in lightningChart1.ViewXY.PointLineSeries)
{
//Disable updates, to prevent several extra refreshes
lightningChart1.BeginUpdate();
//Array for 1 point
SeriesPoint[] points = new SeriesPoint[1];
//Convert 'Now' to X value
var _previousX = lightningChart1.ViewXY.XAxes[0].DateTimeToAxisValue(DateTime.Now);
points[0].X = _previousX;
points[0].Y = GetChartRtValue(item.Title.Text);
//Add the new point into end of first PointLineSeries
item.AddPoints(points, false);
item.DeletePointsBeforeX(_previousX - ChartXDateTimeRange);
//item.DeletePointsBeforeX(_previousX - 100);
if (ScrollEnable)
{
lightningChart1.ViewXY.XAxes[0].ScrollPosition = _previousX;
}
//Allow updates again, and update
lightningChart1.EndUpdate();
}
}
///
/// 根据名称获取曲线所需的数据
///
private double GetChartRtValue(string Name)
{
try
{
var Data = ListChartRtValue.Find(a => a.Name == Name);
if (Data != null)
{
return Data.Value;
}
return 0;
}
catch (Exception ex)
{
//logger.Error(String.Format("ErrSource : {0} ErrMsg : {1}", ex.StackTrace.ToString(), ex.Message.ToString()));
return 0;
}
finally
{
}
}
///
/// 曲线个数
///
private int MainRealTimeChartSeriesCount = 0;
///
/// LightingChart图标的初始化
///
private void LoadLighingChart()
{
//Disable rendering, strongly recommended before updating chart properties
lightningChart1.BeginUpdate();
//Reduce memory usage and increase performance. Destroys out-scrolled data.
lightningChart1.ViewXY.DropOldSeriesData = false;
//多个Y曲线摆放形式
lightningChart1.ViewXY.AxisLayout.YAxesLayout = YAxesLayout.Layered;
//网格带
lightningChart1.ViewXY.AxisLayout.AxisGridStrips = XYAxisGridStrips.X;
//y轴的在左边还是右边
lightningChart1.ViewXY.AxisLayout.YAxisAutoPlacement = YAxisAutoPlacement.AllLeft;
//lightningChart1.Parent = this.panel1;
//lightningChart1.Name = "Temperature measurement _chart";
lightningChart1.Title.Text = "实时曲线";
//lightningChart1.ViewXY.
//lightningChart1.Dock = DockStyle.Fill;
//清除Y轴系列
//lightningChart1.ViewXY.YAxes.Clear();
var InitAxisY = new AxisY(lightningChart1.ViewXY);
InitAxisY.Visible = false;
InitAxisY.ZoomingEnabled = false;
lightningChart1.ViewXY.YAxes.Add(InitAxisY);
//Configure x-axis
AxisX xAxis = lightningChart1.ViewXY.XAxes[0];
xAxis.ValueType = AxisValueType.DateTime;
xAxis.Title.Text = "时间";
xAxis.AutoFormatLabels = true;
xAxis.LabelsTimeFormat = "MM/dd \n HH:mm.ss";
//xAxis.LabelsTimeFormat = "dd/MM/yyyy\nHH:mm.ss";
xAxis.LabelsAngle = 45;
//xAxis.SteppingInterval = 0.1;
//xAxis.ScrollingGap = 15;
//xAxis.AllowScaling = true;
xAxis.ScrollMode = XAxisScrollMode.Scrolling;
xAxis.ZoomingEnabled = true;
//Convert DateTime values to axis values
DateTime now = DateTime.Now;
double minX = xAxis.DateTimeToAxisValue(now);
double maxX = xAxis.DateTimeToAxisValue(now.AddMinutes(60));
xAxis.SetRange(minX, maxX);
//DateTime now = DateTime.Now;
//xAxis.SetDateTimeRange(now,now.AddMinutes(60));
//光标数据
//Add an annotation to show the cursor values
//AnnotationXY cursorValueDisplay = new AnnotationXY();
AnnotationXY cursorValueDisplay = new AnnotationXY(lightningChart1.ViewXY, lightningChart1.ViewXY.XAxes[0], lightningChart1.ViewXY.YAxes[0]);
cursorValueDisplay.Style = AnnotationStyle.RoundedCallout;
cursorValueDisplay.LocationCoordinateSystem = CoordinateSystem.RelativeCoordinatesToTarget;
cursorValueDisplay.LocationRelativeOffset.X = 130;
cursorValueDisplay.LocationRelativeOffset.Y = -100;
cursorValueDisplay.Sizing = AnnotationXYSizing.Automatic;
//cursorValueDisplay.TextStyle.Font = new WpfFont("Lucida console", 10f, FontStyle.Regular);
cursorValueDisplay.TextStyle.Color = System.Windows.Media.Color.FromRgb(0, 0, 0);
cursorValueDisplay.Text = "";
cursorValueDisplay.Fill.Color = System.Windows.Media.Color.FromRgb(255, 255, 255);
cursorValueDisplay.Fill.GradientColor = System.Windows.Media.Color.FromRgb(120, 120, 120);
cursorValueDisplay.BorderVisible = false;
cursorValueDisplay.AllowTargetMove = false;
cursorValueDisplay.Visible = false;
lightningChart1.ViewXY.Annotations.Add(cursorValueDisplay);
//Add cursor
LineSeriesCursor cursor = new LineSeriesCursor(lightningChart1.ViewXY, lightningChart1.ViewXY.XAxes[0]);
lightningChart1.ViewXY.LineSeriesCursors.Add(cursor);
cursor.PositionChanged += cursor_PositionChanged;
cursor.ValueAtXAxis = lightningChart1.ViewXY.XAxes[0].DateTimeToAxisValue(DateTime.Now.AddMinutes(30));
cursor.LineStyle.Color = System.Windows.Media.Color.FromRgb(200, 200, 200);
cursor.SnapToPoints = false;
cursor.TrackPoint.Color1 = System.Windows.Media.Color.FromRgb(255, 255, 255);
cursor.Style = CursorStyle.PointTracking;
lightningChart1.Loaded += LightningChart1_Loaded;
lightningChart1.SizeChanged += LightningChart1_SizeChanged;
//lightningChart1.Resize += new EventHandler(_chart_Resize);
////清除Y轴系列
//lightningChart1.ViewXY.YAxes.Clear();
//Don't show legendbox
lightningChart1.ViewXY.LegendBoxes[0].Visible = true;
lightningChart1.ViewXY.LegendBoxes[0].Position = LegendBoxPositionXY.TopCenter;
//Allow chart rendering
lightningChart1.EndUpdate();
}
private void LightningChart1_SizeChanged(object sender, System.Windows.SizeChangedEventArgs e)
{
UpdateCursorResult();
}
private void LightningChart1_Loaded(object sender, System.Windows.RoutedEventArgs e)
{
UpdateCursorResult();
}
private void cursor_PositionChanged(object sender, PositionChangedEventArgs e)
{
e.CancelRendering = true;
UpdateCursorResult();
}
///
/// Update cursor texts.
///
private void UpdateCursorResult()
{
bool showNextToCursor = true;
//Disable rendering, strongly recommended before updating chart properties
lightningChart1.BeginUpdate();
//Get cursor
LineSeriesCursor cursor = lightningChart1.ViewXY.LineSeriesCursors[0];
//Get annotation
AnnotationXY cursorValueDisplay = lightningChart1.ViewXY.Annotations[0];
//Set annotation target. The location is relative to target.
//Use graph bottom as target Y value.
float targetYCoord = (float)lightningChart1.ViewXY.GetMarginsRect().Bottom;
double y;
lightningChart1.ViewXY.YAxes[0].CoordToValue(targetYCoord, out y);
cursorValueDisplay.TargetAxisValues.X = cursor.ValueAtXAxis;
cursorValueDisplay.TargetAxisValues.Y = y;
double seriesYValue = 0;
StringBuilder sb = new StringBuilder();
int seriesIndex = 1;
string channelStringFormat = "{0}: {1}";
bool labelVisible = false;
bool accurateValue = false;
string value = "";
foreach (PointLineSeries pointLineSeries in lightningChart1.ViewXY.PointLineSeries)
{
//show series titles and cursor values in them, on the right side of the chart,
//if cursor values are not shown next to the cursor in an annotation
pointLineSeries.Title.Visible = !showNextToCursor;
bool resolved = false;
value = "";
if (accurateValue)
resolved = SolveValueAccurate(pointLineSeries, cursor.ValueAtXAxis, out seriesYValue);
else
resolved = SolveValueCoarse(pointLineSeries, cursor.ValueAtXAxis, out seriesYValue);
AxisY axisY = lightningChart1.ViewXY.YAxes[pointLineSeries.AssignYAxisIndex];
if (resolved)
{
labelVisible = true;
//value = string.Format(channelStringFormat, seriesIndex, seriesYValue.ToString("0.000"), axisY.Units.Text);
value = string.Format(channelStringFormat, pointLineSeries.Title.Text, seriesYValue.ToString("0.000"));
}
else
{
//value = string.Format(channelStringFormat, seriesIndex, "---", axisY.Units.Text);
value = string.Format(channelStringFormat, pointLineSeries.Title.Text, "---");
}
sb.AppendLine(value);
//这个会改变曲线标题的名称,影响曲线的定位、删除等操作
//pointLineSeries.Title.Text = value;
seriesIndex++;
}
//sb.AppendLine("");
//sb.AppendLine("时间: " + lightningChart1.ViewXY.XAxes[0].TimeString(cursor.ValueAtXAxis, "HH:mm:ss.ffff"));
////Set text
cursorValueDisplay.Text = sb.ToString();
////Show the label only if it selected to be shown
cursorValueDisplay.Visible = labelVisible && showNextToCursor;
//Allow chart rendering
lightningChart1.EndUpdate();
}
///
/// Solve value from series data points array. Accurate method, but slower than SolveValueCoarse
///
/// Series
/// X value
/// Output Y value
/// Success status
private bool SolveValueAccurate(PointLineSeries series, double xValue, out double yValue)
{
AxisY axisY = lightningChart1.ViewXY.YAxes[series.AssignYAxisIndex];
yValue = 0;
LineSeriesValueSolveResult result = series.SolveYValueAtXValue(xValue);
if (result.SolveStatus == LineSeriesSolveStatus.OK)
{
//PointLineSeries may have two or more points at same X value. If so, center it between min and max
yValue = (result.YMax + result.YMin) / 2.0;
return true;
}
else
{
return false;
}
}
///
/// Solve value from screen coordinates. Faster method, but not less accurateValue than SolveValueAccurate
///
/// Series
/// X value
/// Output Y value
/// Success status
private bool SolveValueCoarse(PointLineSeries series, double xValue, out double yValue)
{
AxisY axisY = lightningChart1.ViewXY.YAxes[series.AssignYAxisIndex];
float coordX = lightningChart1.ViewXY.XAxes[0].ValueToCoord(xValue);
float coordY;
yValue = 0;
LineSeriesCoordinateSolveResult result = series.SolveYCoordAtXCoord(coordX);
if (result.SolveStatus == LineSeriesSolveStatus.OK)
{
coordY = (result.CoordBottom + result.CoordTop) / 2f;
if (axisY.CoordToValue((int)Math.Round(coordY), out yValue) == false)
{
return false;
}
}
else
{
return false;
}
return true;
}
///
/// 增加Y轴
///
///
///
///
private void AddYAxis(string Name, double MinVal, double MaxVal)
{
lightningChart1.BeginUpdate();
var CreateAxisY = new AxisY();
CreateAxisY.Title.Text = Name;
CreateAxisY.Title.AllowDragging = false;
CreateAxisY.SetRange(MinVal, MaxVal);
CreateAxisY.MajorGrid.Visible = false;
//取消滚轮缩放
CreateAxisY.ZoomingEnabled = false;
lightningChart1.ViewXY.YAxes.Add(CreateAxisY);
//Configure and add series to the chart
PointLineSeries CreateSeries = new PointLineSeries(lightningChart1.ViewXY, lightningChart1.ViewXY.XAxes[0], CreateAxisY);
CreateSeries.Title.Text = Name;
CreateSeries.LineStyle.Color = ChartConfigInfo.ListColor[lightningChart1.ViewXY.YAxes.Count + 1];
CreateSeries.AllowUserInteraction = false;
lightningChart1.ViewXY.PointLineSeries.Add(CreateSeries);
lightningChart1.EndUpdate();
MainRealTimeChartSeriesCount++;
}
private void DeleteYAxis(string Name)
{
lightningChart1.BeginUpdate();
for (int i = 0; i < lightningChart1.ViewXY.YAxes.Count; i++)
{
if (lightningChart1.ViewXY.YAxes[i].Title.Text == Name)
{
lightningChart1.ViewXY.YAxes.RemoveAt(i);
break;
}
}
for (int i = 0; i < lightningChart1.ViewXY.PointLineSeries.Count; i++)
{
if (lightningChart1.ViewXY.PointLineSeries[i].Title.Text == Name)
{
lightningChart1.ViewXY.PointLineSeries.RemoveAt(i);
break;
}
}
lightningChart1.EndUpdate();
if (MainRealTimeChartSeriesCount <= 0)
{
MainRealTimeChartSeriesCount = 0;
}
else
{
MainRealTimeChartSeriesCount--;
}
}
#endregion
}
}