using FLY.Thick.Base.IService;
using FObjBase;
using LiveCharts;
using Misc;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using FLY.Thick.Base.UI;
namespace FLY.Thick.Base.UI.UiModule
{
public class FixGraphViewModel : ISaveManager, INotifyPropertyChanged
{
#region 图表控制
///
/// 图表Y轴最大值
///
public double YMax { get; set; } = double.NaN;
///
/// 图表Y轴最小值
///
public double YMin { get; set; } = double.NaN;
///
/// X轴坐标文字显示格式
///
public Func XFormatter { get; set; }
///
/// Y轴坐标文字显示格式
///
public Func YFormatter { get; set; }
///
/// 图表X轴最大值
///
public double XMax { get; set; } = double.NaN;
///
/// 图表X轴最小值
///
public double XMin { get; set; } = double.NaN;
///
/// 显示在chart上的数据
///
public ChartValues Values { get; } = new ChartValues();
#endregion
#region 界面统计值
///
/// 平均值
///
public double Average { get; set; }
///
/// 最大值
///
public double Max { get; set; }
///
/// 最小值
///
public double Min { get; set; }
///
/// 3sigma
///
public double Sigma3 { get; set; }
#endregion
#region 状态
///
/// 测试时间
///
public DateTime Time { get; set; }
///
/// 间隔
///
public TimeSpan Interval { get; set; }
///
/// 间隔,单位ms
///
public double Intervalms { get; set; }
///
/// 混合数
///
public int Mix { get; set; }
///
/// 暂停
///
public bool IsPaused { get; set; }
///
/// AD值模式
///
public bool IsADMode { get; private set; }
#endregion
///
/// 真实接收的数据,与 Values 不一样, Values只是mDatas的一部分
///
List mDatas = new List();
///
/// 数据保存路径
///
public string SavePath { get; set; }
///
/// 自动Y轴中值时,Y轴中值的Value
///
public double AutoTarget { get; set; }
///
/// 多少个 mDatas 显示为一个 Values, 限定屏幕最大显示2000个数据
///
int values_scale = 1;
///
/// 限定屏幕最大显示2000个数据
///
const int values_cap = 1000;
IFixService mFixService;
FixGraphItemParam graphparam;
public FixGraphViewModel()
{
#region 与数据无关界面参数
XFormatter = (x) =>
{
int index = (int)x;
double s = Interval.TotalSeconds * index * values_scale;
return $"{s:F1}s";
};
YFormatter = (y) => y.ToString("F1");
#endregion
}
public void Init(IFixService fixService, FixGraphItemParam bulkGraphItemFix)
{
mFixService = fixService;
graphparam = bulkGraphItemFix;
Update4Graphparam();
graphparam.PropertyChanged += Graphparam_PropertyChanged;
mFixService.RegistTimeGridEvent(new FixEventHandler(mFixService_TimeGridEvent));
this.PropertyChanged += FixGraphViewModel_PropertyChanged;
}
private void FixGraphViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "AutoTarget")
{
updateAutoTarget();
}
}
void updateAutoTarget()
{
if (!graphparam.IsAutoY && graphparam.IsAutoTarget)
{
if ((!double.IsNaN(YMax)) && (!double.IsNaN(YMin)))
{
double mid = (YMax + YMin) / 2;
double range = (YMax - YMin) / 2;
double ymax2 = mid + range / 2;
double ymin2 = mid - range / 2;
if ((AutoTarget < ymax2) && (AutoTarget > ymin2))
{
if (Math.Abs(range - graphparam.YRange) < 0.05)
{
//还在范围内,不需要改变 Y轴范围
return;
}
}
}
YMax = AutoTarget + graphparam.YRange;
YMin = AutoTarget - graphparam.YRange;
}
}
private void Graphparam_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "Interval")
{
UpdateInterval();
}
else if (e.PropertyName == "Mix")
{
UpdateMix();
}
else if (e.PropertyName == "Len")
{
UpdateX();
}
else if ((e.PropertyName == "IsAutoY") ||
(e.PropertyName == "IsAutoTarget") ||
(e.PropertyName == "YRange") ||
(e.PropertyName == "YTarget"))
{
UpdateY();
}
else if (e.PropertyName == "IsADMode")
{
UpdateIsADMode();
}
}
void Update4Graphparam()
{
UpdateInterval();
UpdateMix();
UpdateX();
UpdateY();
UpdateIsADMode();
}
void UpdateX()
{
if (graphparam.Len > values_cap)
values_scale = graphparam.Len / values_cap;
else
values_scale = 1;
XMax = (int)(graphparam.Len / values_scale);//最后算出来 的数量肯定大于 values_cap,没关系,反正数据量少了
XMin = 0;
}
void UpdateY()
{
if (graphparam.IsAutoY)
{
YMax = double.NaN;
YMin = double.NaN;
}
else if (graphparam.IsAutoTarget)
{
updateAutoTarget();
//YMax = AutoTarget + graphparam.YRange;
//YMin = AutoTarget - graphparam.YRange;
}
else
{
YMax = graphparam.YTarget + graphparam.YRange;
YMin = graphparam.YTarget - graphparam.YRange;
}
}
void UpdateInterval()
{
TimeSpan interval;
if (graphparam.Interval < 10)
interval = TimeSpan.FromTicks((long)(graphparam.Interval * TimeSpan.TicksPerMillisecond * 1.28));
else
interval = TimeSpan.FromTicks((long)(graphparam.Interval * TimeSpan.TicksPerMillisecond));
Interval = interval;
Intervalms = ((double)(Interval.Ticks)) / TimeSpan.TicksPerMillisecond;
}
void UpdateMix()
{
Mix = graphparam.Mix;
}
void UpdateIsADMode()
{
IsADMode = graphparam.IsADMode;
//清空全部数据
Clear();
UpdateStat();
}
///
/// 全部 mDatas 重新放入Values
///
///
void RefreshValues(List datas)
{
Values.Clear();
//显示在屏幕的数据,只能有2000个
if (values_scale <= 1)
{
foreach (var d in datas)
Values.Add(d);
}
else
{
data_show.Clear();
foreach (var d in datas)
{
data_show.Add(d);
if (data_show.Count >= values_scale)
{
Values.Add(data_show.Average());
data_show.Clear();
}
}
}
}
///
/// 增量数据,添加到 Values
///
///
void RefreshValuesMore(List datas)
{
//显示在屏幕的数据,只能有2000个
if (values_scale <= 1)
{
foreach (var d in datas)
Values.Add(d);
while (Values.Count > graphparam.Len)
Values.RemoveAt(0);
}
else
{
foreach (var d in datas)
{
data_show.Add(d);
if (data_show.Count >= values_scale)
{
Values.Add(data_show.Average());
data_show.Clear();
}
}
//删除数据
while (Values.Count > graphparam.Len / values_scale)
Values.RemoveAt(0);
}
}
public void Clear()
{
mDatas.Clear();
data_interval.Clear();
data_mix.Clear();
data_show.Clear();
data_interval_totaltime = TimeSpan.Zero;
}
#region 用于间隔
///
/// 显示数据用,mDatas->mPoints 的转换
///
List data_show = new List();
///
/// interval 时间内数据整合
///
List data_interval = new List();
///
/// data_interval 的总时间
///
TimeSpan data_interval_totaltime = TimeSpan.Zero;
///
/// 数据混合
///
List data_mix = new List();
#endregion
void mFixService_TimeGridEvent(object sender, FixEventArgs e)
{
if (IsPaused)
return;
//输入1.28ms 的数据, 输出 混合为mix,间隔为 interval 的数据
List data_out = new List();
for (int i = 0; i < e.thicks.Length; i++)
{
double v = (IsADMode) ? e.ADs[i] : e.thicks[i];
data_interval.Add(v);
data_interval_totaltime += e.ts;
if (data_interval_totaltime >= Interval)
{
double avg = data_interval.Average();
data_interval.Clear();
data_interval_totaltime -= Interval;
data_mix.Add(avg);
int len = data_mix.Count - graphparam.Mix;
if (len > 0)
data_mix.RemoveRange(0, len);
data_out.Add(data_mix.Average());
}
}
if (data_out.Count() > 0)
{
Time = e.time;
AddPoint(data_out);
}
}
void UpdateStat()
{
if (mDatas.Count() == 0)
{
Max = double.NaN;
Min = double.NaN;
Average = double.NaN;
Sigma3 = double.NaN;
AutoTarget = double.NaN;
return;
}
Max = mDatas.Max();
Min = mDatas.Min();
Average = mDatas.Average();
Sigma3 = mDatas.Sigma() * 3;
if (mDatas.Count > 100)
{
//最后100个数据的平均值
AutoTarget = mDatas.Skip(mDatas.Count - 100).Average();
}
else
{
AutoTarget = Average;
}
}
///
/// 1.28ms 经过重新 划分间隔, 混合后的数据,添加到 mDatas
///
///
void AddPoint(List datas)
{
mDatas.AddRange(datas);
//删除数据
if (mDatas.Count > graphparam.Len)
mDatas.RemoveRange(0, mDatas.Count - graphparam.Len);
//更新状态
UpdateStat();
//增量数据,添加到 Values
RefreshValuesMore(datas);
}
public void PreSave()
{
string directory = System.IO.Path.GetDirectoryName(SavePath);
if (string.IsNullOrEmpty(directory))
{
directory = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory);
}
string filename = $"{DateTime.Now:yyyyMMdd_HHmmss}.csv";
SavePath = System.IO.Path.Combine(directory, filename);
}
AsyncCBHandler mAsyncDelegate;
object mAsyncState;
///
/// 成功或失败,会调用 AsyncDelegate
///
///
///
public void Save(AsyncCBHandler AsyncDelegate, object AsyncState)
{
//需要异步!!!!
if (!IsPaused)
{
mAsyncDelegate(mAsyncState, false);
return;
}
mAsyncDelegate = AsyncDelegate;
mAsyncState = AsyncState;
Task.Factory.StartNew(Output);
}
void Output()
{
string path = SavePath;
try
{
using (StreamWriter sw = new StreamWriter(path, false, Encoding.GetEncoding("GB2312")))
{
double ms = graphparam.Interval;
if (ms < 10)
ms *= 1.28;
if (IsADMode)
{
sw.WriteLine("时间(ms),AD");
for (int i = 0; i < mDatas.Count(); i++)
{
double d = mDatas[i];
sw.WriteLine($"{(i * ms):F2},{d:F0}");
}
}
else
{
sw.WriteLine("时间(ms),数据");
for (int i = 0; i < mDatas.Count(); i++)
{
double d = mDatas[i];
sw.WriteLine($"{(i * ms):F2},{d:F2}");
}
}
sw.Flush();
sw.Close();
}
mAsyncDelegate(mAsyncState, true);
}
catch (Exception e)
{
mAsyncDelegate(mAsyncState, false);
}
}
public void GetDatas(out double intervalms, out List datas)
{
intervalms = Intervalms;
datas = mDatas;
}
#region INotifyPropertyChanged 成员
public event PropertyChangedEventHandler PropertyChanged;
#endregion
}
}