diff --git a/FrameworkLocal/UIClient/CenterViews/DataLogs/DataHistory/DataViewModel.cs b/FrameworkLocal/UIClient/CenterViews/DataLogs/DataHistory/DataViewModel.cs index 76288b8..5e7aab2 100644 --- a/FrameworkLocal/UIClient/CenterViews/DataLogs/DataHistory/DataViewModel.cs +++ b/FrameworkLocal/UIClient/CenterViews/DataLogs/DataHistory/DataViewModel.cs @@ -5,6 +5,7 @@ using System.Diagnostics; using System.Drawing; using System.IO; using System.Linq; +using System.Net.Configuration; using System.Text; using System.Threading; using System.Threading.Tasks; @@ -209,8 +210,46 @@ namespace MECF.Framework.UI.Client.CenterViews.DataLogs.DataHistory VisibleRangeTime = new DateRange(StartDateTime.AddMinutes(-5), EndDateTime.AddMinutes(5)); ChartAutoRange = AutoRange.Always; + var colorRandom = new Random(); + var colorPattern = GetNewColorPatternQueue(); + + // 生成待显示的数据列表 SelectedData.Clear(); + #region 生成曲线对象 + + foreach (var module in selectedModules) + { + var selectedTerminal = module.Flatten(true).Where(x => x.Selected == true); + + foreach (var terminal in selectedTerminal) + { + var line2D = new SicFastLineSeries(terminal.FullName) + { + AntiAliasing = true, + ResamplingMode = ResamplingMode.MinMax + }; + + // 确保当列队未空时不会出错,选择默认黑色。 + var color = colorPattern.Any() + ? colorPattern.Dequeue() + : Color.FromArgb(255, colorRandom.Next(0, 255), colorRandom.Next(0, 255), + colorRandom.Next(0, 255)); + + line2D.Stroke = System.Windows.Media.Color.FromRgb(color.R, color.G, color.B); + + var ds = line2D.GetDataSeries(); + ds.Tag = terminal; + + + SelectedData.Add(line2D); + } + } + + #endregion + + var dataSeriesList = SelectedData.Select(x => x.DataSeries).ToList(); + BusyIndicatorContent = "Querying Data ..."; IsBusy = true; _cancellationTokenSource = new CancellationTokenSource(); @@ -221,14 +260,14 @@ namespace MECF.Framework.UI.Client.CenterViews.DataLogs.DataHistory var ts = EndDateTime - StartDateTime; if (ts.Days <= 1) { - await Query(selectedModules, StartDateTime, EndDateTime, _cancellationTokenSource); + Query(selectedModules, dataSeriesList, StartDateTime, EndDateTime, _cancellationTokenSource); } else { var daySlices = Core.DateRange.SplitInToDays(new Core.DateRange(StartDateTime, EndDateTime)); foreach (var range in daySlices) { - await Query(selectedModules, range.Start, range.End, _cancellationTokenSource); + Query(selectedModules, dataSeriesList, range.Start, range.End, _cancellationTokenSource); if (_cancellationTokenSource.Token.IsCancellationRequested) break; @@ -257,7 +296,7 @@ namespace MECF.Framework.UI.Client.CenterViews.DataLogs.DataHistory /// /// 查询数据 /// - public async Task Query(IEnumerable selectedModules, DateTime startTime, DateTime endTime, + public void Query(IEnumerable selectedModules, List dataSeriesList, DateTime startTime, DateTime endTime, CancellationTokenSource cancellation) { var sw = new Stopwatch(); @@ -267,7 +306,7 @@ namespace MECF.Framework.UI.Client.CenterViews.DataLogs.DataHistory Debug.WriteLine("start to query data ...."); sw.Start(); #endif - var ds = await SearchDataBaseAsync(selectedModules, startTime, endTime, cancellation); + var ds = SearchDataBaseAsync(selectedModules, startTime, endTime, cancellation); #if DEBUG sw.Stop(); Debug.WriteLine($"Data returned, costs {sw.ElapsedMilliseconds}ms...."); @@ -288,7 +327,8 @@ namespace MECF.Framework.UI.Client.CenterViews.DataLogs.DataHistory Debug.WriteLine("start to render data ...."); sw.Restart(); #endif - await RenderChartAndTable(ds, cancellation); + var t = RenderChartAndTable(ds, cancellation, dataSeriesList); + Task.WaitAll(t.ToArray()); #if DEBUG sw.Stop(); Debug.WriteLine($"Data render done, costs {sw.ElapsedMilliseconds}ms...."); @@ -331,83 +371,80 @@ namespace MECF.Framework.UI.Client.CenterViews.DataLogs.DataHistory /// 根据左侧选项查询数据 /// /// - private async Task SearchDataBaseAsync(IEnumerable modules, DateTime startTime, DateTime endTime, CancellationTokenSource cancellation) + private DataSet SearchDataBaseAsync(IEnumerable modules, DateTime startTime, DateTime endTime, + CancellationTokenSource cancellation) { if (cancellation == null) throw new ArgumentNullException(nameof(cancellation), "cancellation object can not be null."); var ds = new DataSet(); - - return await Task.Run(() => + + try { - try + using (cancellation.Token.Register(Thread.CurrentThread.Abort)) { - using (cancellation.Token.Register(Thread.CurrentThread.Abort)) + // 遍历模组 + foreach (var module in modules) { - // 遍历模组 - foreach (var module in modules) + var sql = new StringBuilder(); + //! 因为数据库中按天拆表,无法一次性查询数据,需使用UNION合并多表查询,因此此处按天拼接SQL表达式 + // 最终SQL表达式结构为: + // (select xx from date1.xx) union (select xx from date2.xx) union (select xx from date3.xx) + // where time between xxx and xxx + // order by time asc + var ts = endTime - startTime; + for (var day = 0; day <= ts.Days; day++) { - var sql = new StringBuilder(); - //! 因为数据库中按天拆表,无法一次性查询数据,需使用UNION合并多表查询,因此此处按天拼接SQL表达式 - // 最终SQL表达式结构为: - // (select xx from date1.xx) union (select xx from date2.xx) union (select xx from date3.xx) - // where time between xxx and xxx - // order by time asc - var ts = endTime - startTime; - for (var day = 0; day <= ts.Days; day++) + // 检查表名是否存在,否则SQL执行出错。 + var tblName = $"{startTime.AddDays(day):yyyyMMdd}.{module}"; + if (CheckTableExists(tblName)) { - // 检查表名是否存在,否则SQL执行出错。 - var tblName = $"{startTime.AddDays(day):yyyyMMdd}.{module}"; - if (CheckTableExists(tblName)) + + sql.Append("select \"time\" AS InternalTimeStamp"); + var selectedParams = module.ChildNodes.FlattenNodes(true) + .Where(x => x.Selected == true); + + // 添加待查询的列 + foreach (var item in selectedParams) { - - sql.Append("select \"time\" AS InternalTimeStamp"); - var selectedParams = module.ChildNodes.FlattenNodes(true) - .Where(x => x.Selected == true); - - // 添加待查询的列 - foreach (var item in selectedParams) - { - sql.Append("," + $"\"{item}\""); - } - - sql.Append($" from \"{tblName}\" "); - - if (day < ts.Days) - sql.Append(" UNION "); + sql.Append("," + $"\"{item}\""); } + + sql.Append($" from \"{tblName}\" "); + + if (day < ts.Days) + sql.Append(" UNION "); } - - // 所有表名不可用,可能是日期范围错误 - if (sql.Length <= 0) - { - continue; - } - - sql.Append( - $" where \"time\" between {startTime.Ticks} and {endTime.Ticks} order by InternalTimeStamp asc"); - - // 查询数据并将返回的结果存储在DataSet中 - var dataTable = QueryDataClient.Instance.Service.QueryData(sql.ToString()); - - //! 返回的 DataTable 可能不存在,原因是上述代码自动生成的表明可能不存在。 - if (dataTable == null) - continue; - dataTable.TableName = module.Name; - ds.Tables.Add(dataTable); } + + // 所有表名不可用,可能是日期范围错误 + if (sql.Length <= 0) + { + continue; + } + + sql.Append( + $" where \"time\" between {startTime.Ticks} and {endTime.Ticks} order by InternalTimeStamp asc"); + + // 查询数据并将返回的结果存储在DataSet中 + var dataTable = QueryDataClient.Instance.Service.QueryData(sql.ToString()); + + //! 返回的 DataTable 可能不存在,原因是上述代码自动生成的表明可能不存在。 + if (dataTable == null) + continue; + dataTable.TableName = module.Name; + ds.Tables.Add(dataTable); } } - catch (ThreadAbortException) - { - // 操作被取消 - ds = null; - throw; - } + } + catch (ThreadAbortException) + { + // 操作被取消 + ds = null; + throw; + } - return ds; - - }, cancellation.Token); + return ds; } /// @@ -415,7 +452,7 @@ namespace MECF.Framework.UI.Client.CenterViews.DataLogs.DataHistory /// /// /// - private async Task RenderChartAndTable(DataSet ds, CancellationTokenSource cancellation) + private List RenderChartAndTable(DataSet ds, CancellationTokenSource cancellation, List dataSeriesList) { if (cancellation == null) throw new ArgumentNullException(nameof(cancellation), "cancellation object can not be null."); @@ -429,10 +466,10 @@ namespace MECF.Framework.UI.Client.CenterViews.DataLogs.DataHistory var appendTask = new List(); - var colorRandom = new Random(); - var colorPattern = GetNewColorPatternQueue(); + /*var colorRandom = new Random(); + var colorPattern = GetNewColorPatternQueue();*/ - // 当曲线的数据点添加完毕时属性统计值。 + /*// 当曲线的数据点添加完毕时属性统计值。 IProgress> seriesAppendDone = new Progress>(series => { if (series.Tag is ParameterNode node) @@ -441,7 +478,7 @@ namespace MECF.Framework.UI.Client.CenterViews.DataLogs.DataHistory node.MaxValue = node.RawData.Max(x => x.Value).ToString("F2"); node.AverageValue = node.RawData.Average(x => x.Value).ToString("F2"); } - }); + });*/ // 一个Table一个模组 foreach (var table in ds.Tables.Cast()) @@ -458,22 +495,24 @@ namespace MECF.Framework.UI.Client.CenterViews.DataLogs.DataHistory var fullName = col.ColumnName; - // 在SelectedData中检查是否曲线已经存在,若不存在,新建一个 + /*// 在SelectedData中检查是否曲线已经存在,若不存在,新建一个 var line2D = SelectedData.Cast().FirstOrDefault(x => x.DataName == fullName); if (line2D == null) { - line2D = new SicFastLineSeries(fullName) + line2D = new SicFastLineSeries(fullName) { AntiAliasing = true, ResamplingMode = ResamplingMode.MinMax }; - SelectedData.Add(line2D); + SelectedData.Add(line2D); } + + // 绑定LineChart对应的节点。 var node = ParameterNodes.FindTerminalByFullName(fullName); - + // 确保当列队未空时不会出错,选择默认黑色。 var color = colorPattern.Any() ? colorPattern.Dequeue() @@ -481,89 +520,110 @@ namespace MECF.Framework.UI.Client.CenterViews.DataLogs.DataHistory colorRandom.Next(0, 255)); line2D.Stroke = System.Windows.Media.Color.FromRgb(color.R, color.G, color.B); - - var dataSeries = line2D.GetDataSeries(); + + var dataSeries = line2D.GetDataSeries();*/ + + var dataSeries = + dataSeriesList.FirstOrDefault(x => (x.Tag is ParameterNode node) && node.FullName == fullName); + if (dataSeries == null) continue; - // ParameterNode放在line2D.DataSeries.Tag中,而不是line2D.Tag中,因为ParameterNode需要从Task中抛给seriesAppendDone + /*// ParameterNode放在line2D.DataSeries.Tag中,而不是line2D.Tag中,因为ParameterNode需要从Task中抛给seriesAppendDone // 进行数据统计,但Task中无法访问line2D对象,因为跨线程访问问题。 - dataSeries.Tag = node; - var rawData = node.RawData; + dataSeries.Tag = node;*/ + var rawData = (dataSeries.Tag as ParameterNode)?.RawData; + if(rawData == null) + continue; + #endregion #if DEBUG - Debug.WriteLine($"[{sw.ElapsedMilliseconds}ms] Appending point to series {line2D.DataName} ..."); + Debug.WriteLine($"[{sw.ElapsedMilliseconds}ms] Appending point to series {fullName} ..."); #endif //using (dataSeries.SuspendUpdates()) //{ - var t = Task.Run(() => + var t = Task.Run(() => + { +#if DEBUG + Debug.WriteLine( + $"\t[{sw.ElapsedMilliseconds}ms] Start to appending data to {fullName} ..."); +#endif + + // 限制LineChart.Points数量,提高显示速度。 + const int RESTRICT_POINTS_IN_SERIES = 1000; + + var rows = table.Rows; + var skippedRows = rows.Count / RESTRICT_POINTS_IN_SERIES; + if (skippedRows <= 0) + skippedRows = 1; + + // 遍历数据行,添加数据点到LineChart。 + var skipped = 0; + + var dateList = new List(); + var valueList = new List(); + var metaList = new List(); + + for (var i = 0; i < rows.Count; i++) { -#if DEBUG - Debug.WriteLine( - $"\t[{sw.ElapsedMilliseconds}ms] Start to appending data to {line2D.DataName} ..."); -#endif + var date = new DateTime(long.Parse(rows[i][0].ToString())); + var cellValue = rows[i][col]; + var value = double.NaN; - // 限制LineChart.Points数量,提高显示速度。 - const int RESTRICT_POINTS_IN_SERIES = 1000; + if (cellValue is bool b) + value = b ? 1 : 0; + else if (double.TryParse(cellValue.ToString(), out var num)) + value = num; + else + value = 0; - var rows = table.Rows; - var skippedRows = rows.Count / RESTRICT_POINTS_IN_SERIES; - if (skippedRows <= 0) - skippedRows = 1; + dateList.Add(date); + valueList.Add(value); + metaList.Add(new ParameterNodePoint(date, value)); - // 遍历数据行,添加数据点到LineChart。 - var skipped = 0; - for (var i = 0; i < rows.Count; i++) + /*((XyDataSeries)dataSeries).Append(date, value, new ParameterNodePoint(date, value)); + rawData.Add(new ParameterNodePoint(date, value));*/ + + + /*// Re-sampling to improve the rendering performance. + if (skipped >= skippedRows) { - var date = new DateTime(long.Parse(rows[i][0].ToString())); - var cellValue = rows[i][col]; - var value = double.NaN; - - if (cellValue is bool b) - value = b ? 1 : 0; - else if (double.TryParse(cellValue.ToString(), out var num)) - value = num; - else - value = 0; - dataSeries.Append(date, value, new ParameterNodePoint(date, value)); - rawData.Add(new ParameterNodePoint(date, value)); - - - /*// Re-sampling to improve the rendering performance. - if (skipped >= skippedRows) - { - dataSeries.Append(date, value, new ParameterNodePoint(date, value)); - skipped = 0; - } - else - { - skipped++; - }*/ - - - // 操作被取消 - if (cancellation.Token.IsCancellationRequested) - return; - - //Thread.Sleep(1); - - + skipped = 0; } + else + { + skipped++; + }*/ - // - seriesAppendDone.Report(dataSeries); + + // 操作被取消 + if (cancellation.Token.IsCancellationRequested) + return; + + //Thread.Sleep(1); + + + } + + // + //seriesAppendDone.Report(dataSeries); + + using (dataSeries.SuspendUpdates()) + { + ((XyDataSeries)dataSeries).Append(dateList, valueList, metaList); + } #if DEBUG Debug.WriteLine( - $"\t[{sw.ElapsedMilliseconds}ms] Finish appending data to {line2D.DataName}!"); + $"\t[{sw.ElapsedMilliseconds}ms] Finish appending data to {fullName}!"); #endif - }); + }); - appendTask.Add(t); + appendTask.Add(t); //} //await Task.Delay(100); @@ -575,7 +635,7 @@ namespace MECF.Framework.UI.Client.CenterViews.DataLogs.DataHistory #endif // 等待所有序列写入数据完成。 - await Task.WhenAll(appendTask); + //await Task.WhenAll(appendTask); //} @@ -585,6 +645,8 @@ namespace MECF.Framework.UI.Client.CenterViews.DataLogs.DataHistory Debug.WriteLine($"[{sw.ElapsedMilliseconds}ms] Method {nameof(RenderChartAndTable)} Done!"); sw.Stop(); #endif + + return appendTask; } #region Parameter Grid Control diff --git a/FrameworkLocal/output/MECF.Framework/MECF.Framework.RT.Core.dll b/FrameworkLocal/output/MECF.Framework/MECF.Framework.RT.Core.dll index e599302..a08883c 100644 Binary files a/FrameworkLocal/output/MECF.Framework/MECF.Framework.RT.Core.dll and b/FrameworkLocal/output/MECF.Framework/MECF.Framework.RT.Core.dll differ diff --git a/FrameworkLocal/output/MECF.Framework/MECF.Framework.Simulator.Core.dll b/FrameworkLocal/output/MECF.Framework/MECF.Framework.Simulator.Core.dll index 590b257..2d28f2f 100644 Binary files a/FrameworkLocal/output/MECF.Framework/MECF.Framework.Simulator.Core.dll and b/FrameworkLocal/output/MECF.Framework/MECF.Framework.Simulator.Core.dll differ diff --git a/FrameworkLocal/output/MECF.Framework/MECF.Framework.UI.Client.dll b/FrameworkLocal/output/MECF.Framework/MECF.Framework.UI.Client.dll index 5aa1b92..628a25c 100644 Binary files a/FrameworkLocal/output/MECF.Framework/MECF.Framework.UI.Client.dll and b/FrameworkLocal/output/MECF.Framework/MECF.Framework.UI.Client.dll differ diff --git a/FrameworkLocal/output/MECF.Framework/MECF.Framework.UI.Core.dll b/FrameworkLocal/output/MECF.Framework/MECF.Framework.UI.Core.dll index 4b768cf..5eb909c 100644 Binary files a/FrameworkLocal/output/MECF.Framework/MECF.Framework.UI.Core.dll and b/FrameworkLocal/output/MECF.Framework/MECF.Framework.UI.Core.dll differ