using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Data; using System.Linq; using System.Windows; using Aitex.Core.RT.Log; using Aitex.Core.UI.ControlDataContext; using Aitex.Core.Util; using MECF.Framework.Common.CommonData; using MECF.Framework.Common.ControlDataContext; using MECF.Framework.Common.DataCenter; using MECF.Framework.Common.Utilities; using OpenSEMI.ClientBase; using SciChart.Charting.Visuals.Axes; using SciChart.Charting.Visuals.RenderableSeries; using SciChart.Data.Model; using VirgoUI.Client.Models.History.ProcessHistory; using VirgoUI.Client.Models.Operate.RealTime; using VirgoUI.Client.Models.Sys; namespace VirgoUI.Client.Models.History.DataHistory { public class TimeChartDataLine : ChartDataLine { public string Module { get; set; } public DateTime StartTime { get; set; } public DateTime EndTime { get; set; } public DateTime TokenTime { get; set; } private string[] _dbModules = {"PM2", "PM4", "PM6"}; public TimeChartDataLine(string dataName, DateTime startTime, DateTime endTime) : base(dataName) { StartTime = startTime; EndTime = endTime; TokenTime = startTime; Module = "System"; foreach (var dbModule in _dbModules) { if (dataName.StartsWith(dbModule)) { Module = dbModule; break; } } } } public class DataViewModel : UiViewModelBase { private int MenuPermission; private class QueryIndexer { public DateTime TimeToken { get; set; } public List DataList { get; set; } public string Module { get; set; } } #region Property private const int MAX_PARAMETERS = 20; private ObservableCollection _ParameterNodes; public ObservableCollection ParameterNodes { get { return _ParameterNodes; } set { _ParameterNodes = value; NotifyOfPropertyChange("ParameterNodes"); } } public ObservableCollection SelectedData { get; set; } private object _lockSelection = new object(); private object _lockQueryCondition = new object(); private AutoRange _autoRange; public AutoRange ChartAutoRange { get { return _autoRange; } set { _autoRange = value; NotifyOfPropertyChange(nameof(ChartAutoRange)); } } private IRange _timeRange; public IRange VisibleRangeTime { get { return _timeRange; } set { _timeRange = value; NotifyOfPropertyChange(nameof(VisibleRangeTime)); } } private IRange _VisibleRangeValue; public IRange VisibleRangeValue { get { return _VisibleRangeValue; } set { _VisibleRangeValue = value; NotifyOfPropertyChange(nameof(VisibleRangeValue)); } } public DateTime StartDateTime { get; set; } public DateTime EndDateTime { get; set; } public DateTime _queryDateTimeToken; //private bool _restart; private PeriodicJob _thread; ConcurrentBag _lstTokenTimeData = new ConcurrentBag(); RealtimeProvider _provider = new RealtimeProvider(); #endregion #region Function public DataViewModel() { MenuPermission = ClientApp.Instance.GetPermission("DataHistory"); DisplayName = "Data History"; SelectedData = new ObservableCollection(); ParameterNodes = _provider.GetParameters(); var now = DateTime.Now; StartDateTime = now.AddHours(-2); EndDateTime = now; _queryDateTimeToken = StartDateTime; VisibleRangeTime = new DateRange(DateTime.Now.AddMinutes(60), DateTime.Now.AddMinutes(-60)); VisibleRangeValue = new DoubleRange(0, 10); _thread = new PeriodicJob(500, MonitorData, "RealTime", true); } protected override void OnActivate() { base.OnActivate(); } protected bool MonitorData() { try { bool allUpdated = true; lock (_lockSelection) { foreach (var item in _lstTokenTimeData) { DateTime timeFrom = item.TimeToken; if (timeFrom >= EndDateTime) continue; allUpdated = false; Application.Current.Dispatcher.BeginInvoke(new Action(() => { ChartAutoRange = AutoRange.Always; })); DateTime timeTo = timeFrom.AddMinutes(60); if (timeTo > EndDateTime) timeTo = EndDateTime; item.TimeToken = timeTo; GetData(item.DataList, timeFrom, timeTo, item.Module); } } if (allUpdated) { lock (_lockSelection) { while (_lstTokenTimeData.Count > 0) { _lstTokenTimeData.TryTake(out _); } } Application.Current.Dispatcher.BeginInvoke(new Action(() => { ChartAutoRange = AutoRange.Never; })); } } catch (Exception ex) { LOG.Error(ex.Message); } return true; } public void Preset() { } public void Clear() { } private void GetData(List keys, DateTime from, DateTime to, string module) { string sql = "select time AS InternalTimeStamp"; foreach (var dataId in keys) { sql += "," + string.Format("\"{0}\"", dataId); } sql += string.Format(" from \"{0}\" where time > {1} and time <= {2} order by time asc", from.ToString("yyyyMMdd") + "." + "Data", from.Ticks, to.Ticks); DataTable dataTable = QueryDataClient.Instance.Service.QueryData(sql); Dictionary> historyData = new Dictionary>(); if (dataTable == null || dataTable.Rows.Count == 0) return; DateTime dt = new DateTime(); Dictionary colName = new Dictionary(); for (int colNo = 0; colNo < dataTable.Columns.Count; colNo++) { colName.Add(colNo, dataTable.Columns[colNo].ColumnName); historyData[dataTable.Columns[colNo].ColumnName] = new List(); } for (int rowNo = 0; rowNo < dataTable.Rows.Count; rowNo++) { var row = dataTable.Rows[rowNo]; for (int i = 0; i < dataTable.Columns.Count; i++) { HistoryDataItem data = new HistoryDataItem(); if (i == 0) { long ticks = (long)row[i]; dt = new DateTime(ticks); continue; } else { string dataId = colName[i]; if (row[i] is DBNull || row[i] == null) { data.dateTime = dt; data.dbName = colName[i]; data.value = 0; } else if (row[i] is bool) { data.dateTime = dt; data.dbName = colName[i]; data.value = (bool)row[i] ? 1 : 0; } else { data.dateTime = dt; data.dbName = colName[i]; data.value = float.Parse(row[i].ToString()); } } historyData[data.dbName].Add(data); } } Application.Current.Dispatcher.BeginInvoke(new Action(() => { try { double min = ((DoubleRange)VisibleRangeValue).Min ; double max = ((DoubleRange)VisibleRangeValue).Max; foreach (var data in historyData) { foreach (var item in SelectedData) { var seriesItem = item as ChartDataLine; if (data.Key != seriesItem.DataName) continue; foreach (var historyDataItem in data.Value) { if (historyDataItem.value < min) min = historyDataItem.value; if (historyDataItem.value > max) max = historyDataItem.value; seriesItem.Append(historyDataItem.dateTime, historyDataItem.value); } } } VisibleRangeTime = new DateRange(StartDateTime.AddMinutes(-5), EndDateTime.AddMinutes(5)); VisibleRangeValue = new DoubleRange(min,max); } catch (Exception ex) { LOG.Write(ex); } })); } private DataView view; public void Query() { if (MenuPermission != 3) return; this.StartDateTime = this.view.wfTimeFrom.Value; this.EndDateTime = this.view.wfTimeTo.Value; if (StartDateTime > EndDateTime) { MessageBox.Show("Time range invalid, start time should be early than end time"); return; } ParameterNodes = _provider.GetParameters(); VisibleRangeTime = new DateRange(StartDateTime.AddMinutes(-5), EndDateTime.AddMinutes(5)); ChartAutoRange = AutoRange.Always; lock (_lockQueryCondition) { _queryDateTimeToken = StartDateTime; //_restart = true; while (!_lstTokenTimeData.IsEmpty) { _lstTokenTimeData.TryTake(out _); } foreach (var dataLine in SelectedData) { TimeChartDataLine line = dataLine as TimeChartDataLine; line.ClearData(); QueryIndexer indexer = _lstTokenTimeData.FirstOrDefault(x => x.Module == line.Module); if (indexer == null) { indexer = new QueryIndexer() { DataList = new List(), Module = line.Module, TimeToken = StartDateTime, }; _lstTokenTimeData.Add(indexer); } indexer.DataList.Add(line.DataName); } } } public void Query(object parameter) { WaferHistoryRecipe waferHistoryRecipe = parameter as WaferHistoryRecipe; if (waferHistoryRecipe != null) { Application.Current.Dispatcher.BeginInvoke(new Action(() => { if (waferHistoryRecipe.StartTime != DateTime.MinValue) this.view.wfTimeFrom.Value = waferHistoryRecipe.StartTime; if (waferHistoryRecipe.EndTime != DateTime.MinValue) this.view.wfTimeTo.Value = waferHistoryRecipe.EndTime; else this.view.wfTimeTo.Value = waferHistoryRecipe.StartTime.AddHours(1); //ParameterNodes.ForEachDo((x) => //{ // x.Selected = true; // ParameterCheck(x); //}); Query(); })); } } public void ParameterCheck(ParameterNode node) { bool result = RefreshTreeStatusToChild(node); if (!result) node.Selected = !node.Selected; else RefreshTreeStatusToParent(node); } /// /// Refresh tree node status from current to children, and add data to SelectedData /// private bool RefreshTreeStatusToChild(ParameterNode node) { if (node.ChildNodes.Count > 0) { for (int i = 0; i < node.ChildNodes.Count; i++) { ParameterNode n = node.ChildNodes[i]; n.Selected = node.Selected; if (!RefreshTreeStatusToChild(n)) { //uncheck left node for (int j = i; j < node.ChildNodes.Count; j++) { node.ChildNodes[j].Selected = !node.Selected; } node.Selected = !node.Selected; return false; } } } else //leaf node { lock (_lockSelection) { bool isExist = SelectedData.FirstOrDefault(x => (x as ChartDataLine).DataName == node.Name) != null; if (node.Selected && !isExist) { if (SelectedData.Count < MAX_PARAMETERS) { var line = new TimeChartDataLine(node.Name, StartDateTime, EndDateTime); line.Tag = node; SelectedData.Add(line); QueryIndexer indexer = _lstTokenTimeData.FirstOrDefault(x => x.Module == line.Module); if (indexer == null) { indexer = new QueryIndexer() { DataList = new List(), Module = line.Module, TimeToken = StartDateTime, }; _lstTokenTimeData.Add(indexer); } indexer.DataList.Add(line.DataName); } else { DialogBox.ShowWarning($"The max number of parameters is {MAX_PARAMETERS}."); return false; } } else if (!node.Selected && isExist) { //SelectedParameters.Remove(node.Name); var data = SelectedData.FirstOrDefault(d => (d as ChartDataLine).DataName == node.Name); SelectedData.Remove(data); } } } return true; } /// /// Refresh tree node status from current to parent /// /// /// private void RefreshTreeStatusToParent(ParameterNode node) { if (node.ParentNode != null) { if (node.Selected) { bool flag = true; for (int i = 0; i < node.ParentNode.ChildNodes.Count; i++) { if (!node.ParentNode.ChildNodes[i].Selected) { flag = false; //as least one child is unselected break; } } if (flag) node.ParentNode.Selected = true; } else { node.ParentNode.Selected = false; } RefreshTreeStatusToParent(node.ParentNode); } } #region Parameter Grid Control public void DeleteAll() { if (MenuPermission != 3) return; //uncheck all tree nodes foreach (ChartDataLine cp in SelectedData) { (cp.Tag as ParameterNode).Selected = false; RefreshTreeStatusToParent(cp.Tag as ParameterNode); } SelectedData.Clear(); } private void SetParameterNode(ObservableCollection nodes, bool isChecked) { foreach (ParameterNode n in nodes) { n.Selected = isChecked; SetParameterNode(n.ChildNodes, isChecked); } } public void Delete(ChartDataLine cp) { if (MenuPermission != 3) return; if (cp != null && SelectedData.Contains(cp)) { //uncheck tree node (cp.Tag as ParameterNode).Selected = false; RefreshTreeStatusToParent(cp.Tag as ParameterNode); SelectedData.Remove(cp); } } public void ExportAll() { if (MenuPermission != 3) return; try { Microsoft.Win32.SaveFileDialog dlg = new Microsoft.Win32.SaveFileDialog(); dlg.DefaultExt = ".xlsx"; // Default file extension dlg.Filter = "Excel数据表格文件(*.xlsx)|*.xlsx"; // Filter files by extension dlg.FileName = $"Export_{DateTime.Now:yyyyMMdd_HHmmss}"; Nullable result = dlg.ShowDialog();// Show open file dialog box if (result == true) // Process open file dialog box results { System.Data.DataSet ds = new System.Data.DataSet(); ds.Tables.Add(new System.Data.DataTable($"Export_{DateTime.Now:yyyyMMdd_HHmmss}")); ds.Tables[0].Columns.Add("Time"); ds.Tables[0].Columns[0].DataType = typeof(DateTime); lock (_lockSelection) { Dictionary timeValue = new Dictionary(); for (int i = 0; i < SelectedData.Count; i++) { List> data = (SelectedData[i] as ChartDataLine).Points; foreach (var tuple in data) { if (!timeValue.ContainsKey(tuple.Item1)) timeValue[tuple.Item1] = new double[SelectedData.Count]; timeValue[tuple.Item1][i] = tuple.Item2; } ds.Tables[0].Columns.Add((SelectedData[i] as ChartDataLine).DataName); ds.Tables[0].Columns[i + 1].DataType = typeof(double); } foreach (var item in timeValue) { var row = ds.Tables[0].NewRow(); row[0] = item.Key; for (int j = 0; j < item.Value.Length; j++) { row[j + 1] = item.Value[j]; } ds.Tables[0].Rows.Add(row); } } if (!ExcelHelper.ExportToExcel(dlg.FileName, ds, out string reason)) { MessageBox.Show($"Export failed, {reason}", "Export", MessageBoxButton.OK, MessageBoxImage.Warning); return; } MessageBox.Show($"Export succeed, file save as {dlg.FileName}", "Export", MessageBoxButton.OK, MessageBoxImage.Information); } } catch (Exception ex) { LOG.Write(ex); MessageBox.Show("Write failed," + ex.Message, "export failed", MessageBoxButton.OK, MessageBoxImage.Warning); } } public void Export(ChartDataLine cp) { if (MenuPermission != 3) return; try { Microsoft.Win32.SaveFileDialog dlg = new Microsoft.Win32.SaveFileDialog(); dlg.DefaultExt = ".xlsx"; // Default file extension dlg.Filter = "Excel数据表格文件(*.xlsx)|*.xlsx"; // Filter files by extension dlg.FileName = $"{cp.DataName}_{DateTime.Now:yyyyMMdd_HHmmss}"; Nullable result = dlg.ShowDialog();// Show open file dialog box if (result == true) // Process open file dialog box results { System.Data.DataSet ds = new System.Data.DataSet(); ds.Tables.Add(new System.Data.DataTable(cp.DataName)); ds.Tables[0].Columns.Add("Time"); ds.Tables[0].Columns[0].DataType = typeof(DateTime); ds.Tables[0].Columns.Add(cp.DataName); ds.Tables[0].Columns[1].DataType = typeof(double); foreach (var item in cp.Points) { var row = ds.Tables[0].NewRow(); row[0] = item.Item1; row[1] = item.Item2; ds.Tables[0].Rows.Add(row); } if (!ExcelHelper.ExportToExcel(dlg.FileName, ds, out string reason)) { MessageBox.Show($"Export failed, {reason}", "Export", MessageBoxButton.OK, MessageBoxImage.Warning); return; } MessageBox.Show($"Export succeed, file save as {dlg.FileName}", "Export", MessageBoxButton.OK, MessageBoxImage.Information); } } catch (Exception ex) { LOG.Write(ex); MessageBox.Show("Write failed," + ex.Message, "export failed", MessageBoxButton.OK, MessageBoxImage.Warning); } } public void SelectColor(ChartDataLine cp) { if (cp == null) return; var dlg = new System.Windows.Forms.ColorDialog(); if (dlg.ShowDialog() == System.Windows.Forms.DialogResult.OK) { cp.Stroke = new System.Windows.Media.Color() { A = dlg.Color.A, B = dlg.Color.B, G = dlg.Color.G, R = dlg.Color.R }; } } #endregion #endregion protected override void OnViewLoaded(object _view) { base.OnViewLoaded(_view); this.view = (DataView)_view; this.view.wfTimeFrom.Value = this.StartDateTime; this.view.wfTimeTo.Value = this.EndDateTime; //this.AppendData(); } } }