using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Aitex.UI.Charting.Model;
using System.Threading;
using Abt.Controls.SciChart;
using System.Windows.Media;
using Aitex.UI.Charting.Command;
using System.Windows.Input;
using System.Collections.ObjectModel;
using System.Windows.Threading;
using System.Windows;
using System.Windows.Controls;
using Abt.Controls.SciChart.Model.DataSeries;
using Abt.Controls.SciChart.Visuals.RenderableSeries;
using System.ComponentModel;
using Aitex.UI.Charting.View;
using DataAnalysisControl.Core;
namespace Aitex.UI.Charting.ViewModel
{
public sealed class CommonViewModel : ChartingBaseViewModel
{
///
/// 获取所有数据曲线对象
///
public List GetAllDataLineSeries()
{
List lines = new List();
for (int i = 0; i < RenderableSeries.Count; i++)
{
var line = RenderableSeries[i] as MyLineSeries;
if (line != null)
{
lines.Add(line);
}
}
return lines;
}
///
/// new object is not allowed
///
private CommonViewModel()
{
//主窗口的显示
DataConfigViewVisibility = Visibility.Collapsed;
DataDisplayViewVisibility = Visibility.Visible;
Switch2DataCfgViewCommand = new ChartingCommand((o) => true, (o) =>
{
DataDisplayViewVisibility = Visibility.Collapsed;
DataConfigViewVisibility = Visibility.Visible;
InvokePropertyChanged("DataDisplayViewVisibility");
InvokePropertyChanged("DataConfigViewVisibility");
});
Switch2DataDisplayViewCommand = new ChartingCommand((o) => true, (o) =>
{
DataConfigViewVisibility = Visibility.Collapsed;
DataDisplayViewVisibility = Visibility.Visible;
InvokePropertyChanged("DataDisplayViewVisibility");
InvokePropertyChanged("DataConfigViewVisibility");
});
RenderableSeries = new ObservableCollection();
DataSources = new Dictionary();
//显示或隐藏数据配置窗口
ToggleDataDisplayPanelCommand = new ChartingCommand((o) => true, (o) =>
{
if (DataConfigViewRow != null)
{
DataConfigViewRow.Height = new GridLength(DataConfigViewRow.Height.Value > 0 ? 0 : 150);
InvokePropertyChanged("IsDataConfigVisiable");
}
});
//Charting控件的某些元素必须STA,UI主线程完成,因此此处需定义个DispatcherTimer来实现某些GUI相关的操作
_dispatcherTimer = new DispatcherTimer();
_dispatcherTimer.Interval = new TimeSpan(0, 0, 0, 0, 100);
_dispatcherTimer.Tick += new EventHandler(_dispatcherTimer_Tick);
}
public void Start()
{
if (_isAlive) return;
_isAlive = true;
System.Threading.Tasks.Task.Factory.StartNew(TaskRun);
_dispatcherTimer.Start();
}
public void Stop()
{
_dispatcherTimer.Stop();
_isAlive = false;
}
bool _isAlive = false;
///
/// DispatcherTimer中定期处理一些和GUI相关的处理,例如新建、删除LineSeries对象等
///
///
///
void _dispatcherTimer_Tick(object sender, EventArgs e)
{
//执行命令队列中的指令
if (_commandQueue.Count > 0)
{
if (!Monitor.TryEnter(_dataLocker))
return;
try
{
IEnumerable commandList = null;
lock (_cmdQLocker)
{
commandList = _commandQueue.ToList();
_commandQueue.Clear();
}
foreach (var command in commandList)
{
command.Execute(this);
}
}
catch (Exception ex)
{
CONTEXT.WriteLog(ex);
}
finally
{
Monitor.Exit(_dataLocker);
}
}
}
DispatcherTimer _dispatcherTimer;
//单实例定义
private readonly static Lazy _instance = new Lazy(() => new CommonViewModel(), true);
///
/// 获取当前对象的单实例
///
public static CommonViewModel Instance
{
get
{
return _instance.Value;
}
}
#region view switch
public ICommand ToggleDataDisplayPanelCommand { get; set; }
public Visibility DataConfigViewVisibility { get; private set; }
public Visibility DataDisplayViewVisibility { get; private set; }
public ICommand Switch2DataCfgViewCommand { get; set; }
public ICommand Switch2DataDisplayViewCommand { get; set; }
#endregion
#region data source operations
///
/// 生长率曲线相关的定义
///
public MyLineSeries GrowthRateRenderableSeries { get; set; }
public RowDefinition DataConfigViewRow { get; set; }
public bool IsDataConfigVisiable { get { return DataConfigViewRow != null && DataConfigViewRow.Height.Value > 0; } }
Queue _commandQueue = new Queue();
object _cmdQLocker = new object();
object _dataLocker = new object();
public Dictionary DataSources { get; set; }
public ObservableCollection RenderableSeries { get; set; }
public DateTime Time1 { get; set; } //时间标尺1
public DateTime Time2 { get; set; } //时间标尺2
IDataSource _currentSelectedDataSource;
public IDataSource CurrentSelectedDataSource
{
get
{
return _currentSelectedDataSource;
}
set
{
if ((value != null) &&(_currentSelectedDataSource == null || _currentSelectedDataSource != value))
{
if (!string.IsNullOrEmpty(value.WaferDisplayIndex) &&
(_currentSelectedDataSource == null ||
!value.WaferDisplayIndex.Equals(_currentSelectedDataSource.WaferDisplayIndex)))
{
WaferDisplayIndex = value.WaferDisplayIndex;
}
else
{
WaferDisplayIndex = GetWaferDisplayIndex(value.BeginTime, value.ChamberName);
}
value.WaferDisplayIndex = WaferDisplayIndex;
}
_currentSelectedDataSource = value;
if (_currentSelectedDataSource != null)
CurrentSelectedDataSourceName = _currentSelectedDataSource.Title;
InvokePropertyChanged("CurrentSelectedDataSource");
InvokePropertyChanged("CurrentSelectedDataSourceName");
}
}
///
/// 获取数据曲线对象
///
///
///
public MyLineSeries GetDataLineSeries(string uniqueDataId)
{
for (int i = 0; i < RenderableSeries.Count; i++)
{
var line = RenderableSeries[i] as MyLineSeries;
if (line != null && line.UniqueId == uniqueDataId)
{
return RenderableSeries[i] as MyLineSeries;
}
}
return null;
}
public string CurrentSelectedDataSourceName
{
get;
private set;
}
public ObservableCollection DataSourceList
{
get
{
var ret = new ObservableCollection();
foreach (var key in DataSources.Keys)
ret.Add(DataSources[key]);
return ret;
}
}
public List DataSourceNameList
{
get
{
return DataSources.Keys.ToList();
}
}
///
/// 移除先前绘制的生长率计算曲线
///
public void ClearGrowthRateCurve()
{
lock (_cmdQLocker)
{
_commandQueue.Enqueue(new RemoveGrowthCurveCommand());
}
}
///
/// 新增DataSource对象
///
///
///
public void AddDataSource(IDataSource dataSource)
{
lock (_cmdQLocker)
{
CONTEXT.WriteLog("添加数据源");
_commandQueue.Enqueue(new AddDataSourceCommand(dataSource));
}
}
///
/// 移除DataSource对象
///
/// 移除指定的数据源,如果sourceName为null,则移除所有的数据源
///
public void RemoveDataSource(string sourceName)
{
lock (_cmdQLocker)
{
_commandQueue.Enqueue(new RemoveDataSourceCommand(sourceName));
}
}
#endregion
#region data item operations
///
/// 添加Data Item对象
///
///
///
///
///
///
///
///
public void AddDataSeries(string dataSourceName, string seriesName, string displayName, Color lineColor, float factor, float offset, int lineWidth)
{
lock (_cmdQLocker)
{
_commandQueue.Enqueue(new AddDataSeriesCommand(dataSourceName, seriesName, displayName, lineColor, factor, offset, lineWidth));
}
}
///
/// 显示所有当前数据
///
public void ShowAllSeries()
{
lock (_cmdQLocker)
{
_commandQueue.Enqueue(new ShowAllSeriesCommand());
}
}
///
/// 隐藏当前所有数据
///
public void HidAllSeries()
{
lock (_cmdQLocker)
{
_commandQueue.Enqueue(new HidAllSeriesCommand());
}
}
///
/// 显示预设的数据管理对话框
///
public void LoadPresetSetting()
{
lock (_cmdQLocker)
{
_commandQueue.Enqueue(new LoadPresetSettingCommand());
}
}
///
/// 数据源时间更新
///
///
///
///
public void SyncSourceTime(string dataSourceName, DateTime syncTimePoint, string syncStepName)
{
lock (_cmdQLocker)
{
_commandQueue.Enqueue(new SourceTimeSyncCommand(dataSourceName, syncTimePoint, syncStepName));
}
}
///
/// 数据缩放因子+Offset改变命令
///
///
///
public void ChangeDataDisplayFactor(string uniqueDataName, double newFactor, double newOffset)
{
lock (_cmdQLocker)
{
_commandQueue.Enqueue(new ChangeFactorOffsetCommand(uniqueDataName, newFactor, newOffset));
}
}
///
/// 去除DataItem对象
///
///
public void RemoveDataSeries(string uniqueDataId)
{
lock (_cmdQLocker)
{
_commandQueue.Enqueue(new RemoveDataSeriesCommand(uniqueDataId));
}
}
///
/// 保存当前所有数据
///
public void ExportAllSeries()
{
lock (_cmdQLocker)
{
_commandQueue.Enqueue(new ExportAllSeriesCommand());
}
}
#endregion
private int GetIndex(List Total, int index)
{
int _result = -1;
//string[] _temp = Total.Split(',');
//for (int i = 0; i < _temp.Length; i++)
//{
// if (_temp[i].PadLeft(2,'0').Equals(index.ToString().Trim().PadLeft(2,'0')))
// {
// _result = i+1;
// break;
// }
//}
_result = Total.FindIndex((x) => x == (index.ToString().Trim().PadLeft(2, '0')));
if (_result >= 0) { _result++; }
return _result;
}
#region thread - timely retrieve data
///
/// data getting task
///
void TaskRun()
{
while (_isAlive)
{
Thread.Sleep(100);
try
{
//为提升效率,将线程从IDataSource对象获取数据的缓慢过程尽量和Chart对象通过UI线程的更新分开进行
//通过Dictionary将数据进行当前线程内的暂时缓存
List> newDataDic =
new List>();
Dictionary>> rqDataDic =
new Dictionary>>();
lock (_dataLocker)
{
foreach(MyLineSeries line in RenderableSeries)
{
if (DateTime.Now < line.NextQueryTime || line == null || line.DataSource == null)
continue;
IDataSource dataSource = line.DataSource; //数据源对象
string chamName = dataSource.ChamberName; //Chamber名称
DateTime lastUpdateTime = line.LastUpdateTime; //最后一次获取数据的时间
DateTime dataEndTime = dataSource.EndTime; //数据源的结束时间
if (lastUpdateTime >= dataEndTime)
continue;
DateTime nextDataTime = lastUpdateTime.Date + new TimeSpan(0, 23, 59, 59, 999); //ChartDataDic[dataId].Item4.LastUpdateTime + new TimeSpan(2, 0, 0);
if (nextDataTime > dataEndTime) nextDataTime = dataEndTime;
string dataName = line.SeriesName;
//if(int.TryParse(WaferDisplayIndex.Split(',')[index-1],out index))
if (!rqDataDic.ContainsKey(dataSource))
rqDataDic.Add(dataSource, new List>());
rqDataDic[dataSource].Add(new Tuple(dataSource, dataName, line.UniqueId, lastUpdateTime, nextDataTime));
}
}
//优化1:在此作判断,如果多个DataItem的如果在同一个DataSource中而且beginTime,EndTime完全一致,那么合并后进行Select Query可以进行加速
foreach (var ds in rqDataDic.Keys)
{
var tempDic = new Dictionary>>();
foreach (var daq in rqDataDic[ds])
{
string id = string.Format("{0}-{1}", daq.Item4.Ticks, daq.Item5.Ticks);
if (!tempDic.ContainsKey(id))
tempDic.Add(id, new List>());
tempDic[id].Add(daq);
}
//优化2:如果存在2组不同时间区间的数据请求,那么优化执行时间较早的数据请求,目的为了将多组不同时间段的请求合并为一个SQL语句,提升SQL查询效率
DateTime earliestEndT = DateTime.MaxValue;
foreach (var tickKey in tempDic.Keys)
{
var endT = tempDic[tickKey][0].Item5;
if (endT < earliestEndT)
earliestEndT = endT;
}
ListremoveQueryList = new List();
foreach (var tickKey in tempDic.Keys)
{
var beginT = tempDic[tickKey][0].Item4;
if (beginT > earliestEndT)
removeQueryList.Add(tickKey);
}
foreach (var tickKey in removeQueryList)
tempDic.Remove(tickKey);
//database query
foreach (var tickKey in tempDic.Keys)
{
List dataNameList = new List();
List dataIdList = new List();
foreach (var da in tempDic[tickKey])
{
dataNameList.Add(da.Item2);
dataIdList.Add(da.Item3);
}
if (tempDic[tickKey].Count > 0)
{
List rtdata;
var singleData = tempDic[tickKey][0];
var dataSource = singleData.Item1;
var beginT = singleData.Item4;
var endT = singleData.Item5;
var isSucc = dataSource.GetData(dataNameList, beginT, endT, out rtdata);
if (isSucc)
{
for (int ss = 0; ss < dataIdList.Count; ss++)
{
newDataDic.Add(new Tuple(dataIdList[ss], rtdata[ss], endT));
}
}
}
}
}
//对Chart对象的数据集进行集中更新
Application.Current.Dispatcher.Invoke(new Action
>>((o) =>
{
if (o.Count > 0)
{
lock (_dataLocker)
{
var nextRetryQueryTime = DateTime.Now + new TimeSpan(0, 0, 0, 2);
foreach (var item in o)
{
var dataId = item.Item1;
var dataName = item.Item2.DataName;
var line = GetDataLineSeries(dataId);
if (line == null)
continue;
if (item.Item2.TimeStamp.Count == 0) //get nothing for this querying
{
if (item.Item3.Date >= DateTime.Now.Date)//possible data is still in generating today, try again 2 seconds later
{
line.NextQueryTime = nextRetryQueryTime;
}
else //data not available for this day, jump to query next day
{
line.NextQueryTime = DateTime.Now;
line.LastUpdateTime = item.Item3.Date + new TimeSpan(1, 0, 0, 0);
}
}
else //get something for this querying
{
//update next query time point
line.NextQueryTime = DateTime.Now;
if (item.Item3.Date >= DateTime.Now.Date) //update the last update time by exactly last data timestamp, for some more data is in generating state
line.LastUpdateTime = item.Item2.TimeStamp[item.Item2.TimeStamp.Count - 1] + new TimeSpan(0, 0, 0, 0, 500);
else
line.LastUpdateTime = item.Item3.Date + new TimeSpan(1, 0, 0, 0);
//write to data buf
line.DataSource.Datas[dataName].RawData.AddRange(item.Item2.RawData);
line.DataSource.Datas[dataName].TimeStamp.AddRange(item.Item2.TimeStamp);
var timeMove = line.DataSource.TimeMove;
var factorScale = (float)line.Factor;
var offset = (float)line.Offset;
var dataSet = line.DataSeries as DataSeries;
if (timeMove.Ticks != 0 && (factorScale == 1 && offset == 0))
{
List movedTimeList = new List();
foreach (var time1 in item.Item2.TimeStamp)
{
movedTimeList.Add(time1 + timeMove);
}
dataSet.Append(movedTimeList, item.Item2.RawData);
}
else if (timeMove.Ticks == 0 && (factorScale != 1 || offset != 0))
{
List scaledValue = new List();
foreach (var value1 in item.Item2.RawData)
{
scaledValue.Add(value1 * factorScale + offset);
}
dataSet.Append(item.Item2.TimeStamp, scaledValue);
}
else if (timeMove.Ticks != 0 && (factorScale != 1|| offset != 0))
{
List movedTimeList = new List();
List scaledValue = new List();
foreach (var time1 in item.Item2.TimeStamp)
{
movedTimeList.Add(time1 + timeMove);
}
foreach (var value1 in item.Item2.RawData)
{
scaledValue.Add(value1 * factorScale + offset);
}
dataSet.Append(movedTimeList, scaledValue);
}
else
{
dataSet.Append(item.Item2.TimeStamp, item.Item2.RawData);
}
}
}
}
InvokePropertyChanged("RenderableSeries");
}
}), newDataDic);
}
catch (Exception ex)
{
CONTEXT.WriteLog(ex, "Charting绘图Task运行发生异常");
}
}
}
#endregion
}
}