using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Drawing;
using System.Linq;
using System.Windows;
using Aitex.Core.RT.Log;
using Aitex.Core.Util;
using Aitex.Core.Utilities;
using MECF.Framework.Common.ControlDataContext;
using MECF.Framework.Common.DataCenter;
using MECF.Framework.Common.Utilities;
using MECF.Framework.UI.Client.CenterViews.DataLogs.ProcessHistory;
using MECF.Framework.UI.Client.ClientBase;
using MECF.Framework.UI.Client.ClientBase.Charting;
using MECF.Framework.UI.Client.ClientBase.EventArgs;
using MECF.Framework.UI.Client.ClientBase.Tree;
using OpenSEMI.ClientBase;
using SciChart.Charting.Visuals.Axes;
using SciChart.Charting.Visuals.RenderableSeries;
using SciChart.Core.Extensions;
namespace MECF.Framework.UI.Client.CenterViews.Operations.RealTime
{
public class RealtimeViewModel : BusyIndicateableUiViewModelBase
{
#region Variables
///
/// 允许监控的最大曲线数量
///
private const int MAX_PARAMETERS_ALLOWED = 200;
private readonly RealtimeProvider _provider = new RealtimeProvider();
private bool _enableAutoZoom = true;
private int _pointCount;
private readonly PeriodicJob _thread;
///
/// Monitor中添加数据点时直接向DataSeries对象添加,已实现跨线程操作。
///
private readonly List _dataSeriesList;
///
/// 更新报告导出信息。
///
private readonly IProgress _exportingProgressReporter;
#endregion
#region Constructors
public RealtimeViewModel()
{
DisplayName = "Realtime";
SelectedData = new ChartingLineSeriesCollection(DisplayName);
ParameterNodes = new TreeNode(DisplayName)
{
MaxTerminalSelectionAllowed = MAX_PARAMETERS_ALLOWED
};
ParameterNodes.ChildNodes.AddRange(_provider.GetTreeNodeParameters());
ParameterNodes.TerminalNodeSelectionChanged += OnNodeSelectionChanged;
IntervalSaved = true;
TrendInterval = 500;
TimeSpanSaved = true;
TrendTimeSpan = 60 * 5;
_thread = new PeriodicJob(TrendInterval, MonitorData, "RealTime", true);
_pointCount = Math.Max(TrendTimeSpan * 1000 / TrendInterval, 10);
_dataSeriesList = new List();
_exportingProgressReporter = new Progress(e =>
{
BusyIndicatorContent = e.Message;
});
}
#endregion
#region Properties
public bool IsPermission => this.Permission == 3;
public TreeNode ParameterNodes { get; }
public ChartingLineSeriesCollection SelectedData { get; set; }
public AutoRange ChartAutoRange => EnableAutoZoom ? AutoRange.Always : AutoRange.Never;
public bool EnableAutoZoom
{
get => _enableAutoZoom;
set
{
_enableAutoZoom = value;
NotifyOfPropertyChange(nameof(EnableAutoZoom));
NotifyOfPropertyChange(nameof(ChartAutoRange));
}
}
[IgnorePropertyChange]
public int TrendInterval { get; set; }
public bool IntervalSaved { get; set; }
[IgnorePropertyChange]
public int TrendTimeSpan { get; set; }
public bool TimeSpanSaved { get; set; }
private bool _IsHold;
public bool IsHold
{
get { return _IsHold; }
set
{
if (_IsHold!=value)
{
_IsHold = value;
if (_IsHold) EnableAutoZoom = false;
else EnableAutoZoom = true;
NotifyOfPropertyChange(nameof(IsHold));
}
}
}
#endregion
#region Methods
protected bool MonitorData()
{
try
{
Dictionary data = null;
if (!IsHold&&SelectedData.Count > 0)
{
data = QueryDataClient.Instance.Service.PollData(SelectedData.Select(r => (r as FastLineSeries)?.DataName).ToList());
AppendData(data);
}
for (var j = 0; j < ParameterNodes.ChildNodes.Count; j++)
{
var par = ParameterNodes.ChildNodes[j];
par.IsVisibilityParentNode = Visibility.Hidden;
}
}
catch (Exception ex)
{
LOG.Error(ex.Message);
}
return true;
}
public void AppendData(Dictionary data)
{
if (data == null)
return;
var dt = DateTime.Now;
foreach (var ds in _dataSeriesList)
{
if (ds == null || !data.ContainsKey(ds.SeriesName))
continue;
if (data[ds.SeriesName] is bool)
{
ds.Append(dt, ((bool)data[ds.SeriesName] ? 1 : 0) * ds.Factor + ds.Offset, new ParameterNodePoint(dt, ((bool)data[ds.SeriesName] ? 1 : 0)));
continue;
}
if (!double.TryParse(data[ds.SeriesName].ToString(), out var value))
continue;
ds.Append(dt, value * ds.Factor + ds.Offset, new ParameterNodePoint(dt, value));
}
}
private void UpdateFifoCapacity(int capacity)
{
foreach (var renderableSeries in SelectedData)
{
if (renderableSeries is FastLineSeries line)
{
var ds = line.GetDataSeries();
var copyMetaData = ds.Metadata.Where(x => x != null && ((ParameterNodePoint)x).Time > DateTime.Now.AddSeconds(-TrendTimeSpan)).ToList();
ds.FifoCapacity = capacity;
foreach (ParameterNodePoint item in copyMetaData)
{
ds.Append(item.Time, item.Value);
}
}
}
}
public void SetInterval()
{
_thread.ChangeInterval(TrendInterval);
_pointCount = Math.Max(10, TrendTimeSpan * 1000 / TrendInterval);
IntervalSaved = true;
NotifyOfPropertyChange(nameof(IntervalSaved));
UpdateFifoCapacity(_pointCount);
}
public void SetTimeSpan()
{
_pointCount = Math.Max(10, TrendTimeSpan * 1000 / TrendInterval);
TimeSpanSaved = true;
NotifyOfPropertyChange(nameof(TimeSpanSaved));
UpdateFifoCapacity(_pointCount);
}
#endregion
#region Events
public void OnNodeSelectionChanged(object sender, TreeNodeSelectionChangedEventArgs e)
{
if (e.NewValue == true && SelectedData.Count >= MAX_PARAMETERS_ALLOWED)
{
e.Cancel = true;
return;
}
var line = SelectedData.Cast().FirstOrDefault(x => x.DataName == e.Source.FullName);
switch (e.NewValue)
{
case false:
{
if (line != null)
{
_dataSeriesList.Remove(line.GetDataSeries());
SelectedData.Remove(line);
}
break;
}
case true:
{
if (line == null)
{
var series = new FastLineSeries(e.Source.FullName)
{
BackendParameterNode = e.Source
};
series.GetDataSeries().FifoCapacity = _pointCount;
SelectedData.Add(series);
_dataSeriesList.Add(series.GetDataSeries());
SelectedData.ResetColors();
}
break;
}
default:
break;
}
}
public void Deleted(object sender, RenderableSeriesDeletingEventArgs e)
{
var list = e.Source.Cast().ToList();
var total = SelectedData.Count;
for (var i = total - 1; i >= 0; i--)
{
if (i < list.Count)
{
list[i].BackendParameterNode.IsSelected = false;
}
}
_dataSeriesList.Clear();
((RealtimeView)View).tvParameterNodes.ClearPresetGroupSelectionOnly();
}
public void Exporting(object sender, EventArgs e)
{
BusyIndicatorContent = "Exporting ...";
IsBusy = true;
}
public void Exported(object sender, EventArgs e)
{
IsBusy = false;
}
public void ProgressUpdating(object sender, ProgressUpdatingEventArgs e)
{
_exportingProgressReporter.Report(e);
}
public override void Cancel()
{
if (View is RealtimeView view)
{
view.dataGrid.CancelOperation();
}
}
public void ArrowClick(string direction)
{
var view = GetView() as RealtimeView;
switch (direction)
{
case "Up":
view.sciChart.ChartModifier.YAxis.Scroll(50, SciChart.Charting.ClipMode.ClipAtMax);
break;
case "Down":
view.sciChart.ChartModifier.YAxis.Scroll(-50, SciChart.Charting.ClipMode.ClipAtMin);
break;
case "Left":
view.sciChart.ChartModifier.XAxis.Scroll(-50, SciChart.Charting.ClipMode.ClipAtMin);
break;
case "Right":
view.sciChart.ChartModifier.XAxis.Scroll(50, SciChart.Charting.ClipMode.ClipAtMax);
break;
}
}
public void ZoomInClick(int index)
{
var view = GetView() as RealtimeView;
index += 1;
if ((index & 0x01) == 1)
view.sciChart.ChartModifier.XAxis.ZoomBy(-0.1, -0.1);
if ((index & 0x02) == 2)
view.sciChart.ChartModifier.YAxis.ZoomBy(-0.1, -0.1);
}
public void ZoomOutClick(int index)
{
var view = GetView() as RealtimeView;
index += 1;
if ((index & 0x01) == 1)
view.sciChart.ChartModifier.XAxis.ZoomBy(0.1, 0.1);
if ((index & 0x02) == 2)
view.sciChart.ChartModifier.YAxis.ZoomBy(0.1, 0.1);
}
#endregion
}
}