DataViewModel.cs 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Collections.ObjectModel;
  4. using System.Data;
  5. using System.Diagnostics;
  6. using System.Globalization;
  7. using System.Linq;
  8. using System.Text;
  9. using System.Threading;
  10. using System.Threading.Tasks;
  11. using System.Windows;
  12. using System.Windows.Controls;
  13. using System.Windows.Data;
  14. using System.Windows.Forms;
  15. using Aitex.Core.RT.Log;
  16. using Aitex.Sorter.Common;
  17. using MECF.Framework.Common.DataCenter;
  18. using MECF.Framework.Common.Equipment;
  19. using MECF.Framework.UI.Client.CenterViews.DataLogs.Event;
  20. using MECF.Framework.UI.Client.CenterViews.DataLogs.ProcessHistory;
  21. #if !EXPORT_TO_CSV
  22. using MECF.Framework.Common.Utilities;
  23. #endif
  24. using MECF.Framework.UI.Client.CenterViews.Operations.RealTime;
  25. using MECF.Framework.UI.Client.ClientBase;
  26. using MECF.Framework.UI.Client.ClientBase.Charting;
  27. using MECF.Framework.UI.Client.ClientBase.EventArgs;
  28. using MECF.Framework.UI.Client.ClientBase.Pipelines;
  29. using MECF.Framework.UI.Client.ClientBase.Tree;
  30. using SciChart.Charting.Model.DataSeries;
  31. using SciChart.Charting.Visuals.Axes;
  32. using SciChart.Charting.Visuals.RenderableSeries;
  33. using SciChart.Core.Extensions;
  34. using SciChart.Core.Framework;
  35. using SciChart.Data.Model;
  36. using Cali = Caliburn.Micro;
  37. using DataGridCell = System.Windows.Controls.DataGridCell;
  38. using DateRange = SciChart.Data.Model.DateRange;
  39. using MessageBox = System.Windows.MessageBox;
  40. using TreeNode = MECF.Framework.UI.Client.ClientBase.Tree.TreeNode;
  41. namespace MECF.Framework.UI.Client.CenterViews.DataLogs.DataHistory
  42. {
  43. public class DataViewModel : BusyIndicateableUiViewModelBase
  44. {
  45. #region Variables
  46. /// <summary>
  47. /// 一次最多查询的项目数。
  48. /// </summary>
  49. private const int MAX_ITEMS_PER_QUERY = 50;
  50. private IRange _timeRange;
  51. private IRange _visibleRangeValue;
  52. private AutoRange _autoRange;
  53. private CancellationTokenSource _cancellationTokenSource;
  54. /// <summary>
  55. /// 更新报告导出信息。
  56. /// </summary>
  57. private readonly IProgress<ProgressUpdatingEventArgs> _progCsvExport;
  58. /// <summary>
  59. /// 查询进度信息更新。
  60. /// </summary>
  61. private IProgress<ProgressUpdatingEventArgs> _progQueryUpdate;
  62. /// <summary>
  63. /// 挂起或恢复Chart更新
  64. /// </summary>
  65. private readonly IProgress<bool> _progChartSuspendUpdating;
  66. private IUpdateSuspender _suspender;
  67. /// <summary>
  68. /// 打开错误消息对话框。
  69. /// </summary>
  70. private readonly IProgress<string> _progShowErrorMessageBox;
  71. #endregion
  72. #region Constructors
  73. public DataViewModel()
  74. {
  75. DisplayName = "Data History";
  76. SelectedData = new ChartingLineSeriesCollection(DisplayName);
  77. var now = DateTime.Today;
  78. SearchBeginTime = DateTime.Now.AddHours(-1); // -new TimeSpan(1, 0, 0, 0);
  79. SearchEndTime = DateTime.Now;
  80. //SearchBeginTime = new DateTime(now.Year, now.Month, now.Day, 00, 00, 00); ;// -new TimeSpan(1, 0, 0, 0);
  81. //SearchEndTime = new DateTime(now.Year, now.Month, now.Day, 23, 59, 59);
  82. var provider = new RealtimeProvider();
  83. ParameterNodes = new TreeNode(DisplayName)
  84. {
  85. MaxTerminalSelectionAllowed = MAX_ITEMS_PER_QUERY
  86. };
  87. ParameterNodes.ChildNodes.AddRange(provider.GetTreeNodeParameters());
  88. VisibleRangeTime = new DateRange(DateTime.Now.AddMinutes(-60), DateTime.Now.AddMinutes(60));
  89. VisibleRangeValue = new DoubleRange(0, 10);
  90. _progQueryUpdate = new Progress<ProgressUpdatingEventArgs>(e =>
  91. {
  92. if (e.CurrentProgress == e.TotalProgress)
  93. {
  94. IsBusy = false;
  95. if (_cancellationTokenSource?.Token.IsCancellationRequested == false)
  96. {
  97. foreach (var renderableSeries in SelectedData)
  98. {
  99. var series = renderableSeries as FastLineSeries;
  100. var dataSeries = series?.GetDataSeries();
  101. try
  102. {
  103. if (series != null && dataSeries != null)
  104. {
  105. var node = series.BackendParameterNode;
  106. if (double.IsInfinity((double)dataSeries.YRange.Diff))
  107. {
  108. node.ClearStatistic();
  109. }
  110. else
  111. {
  112. var min = ((double)dataSeries.YMin);
  113. var max = ((double)dataSeries.YMax);
  114. var average =
  115. dataSeries.Metadata.Cast<ParameterNodePoint>().Average(x => x.Value);
  116. node.SetStatistic(min, max, average);
  117. }
  118. }
  119. }
  120. catch (Exception ex)
  121. {
  122. var err = $"It's failed to load data of {series?.DataName ?? "Unknown"}, {ex.Message}";
  123. LOG.Error(err, ex);
  124. }
  125. }
  126. }
  127. ChartAutoRange = AutoRange.Never;
  128. ((DataView)View).chart.ZoomExtents();
  129. }
  130. BusyIndicatorContent = e.Message;
  131. });
  132. _progCsvExport = new Progress<ProgressUpdatingEventArgs>(e =>
  133. {
  134. BusyIndicatorContent = e.Message;
  135. });
  136. _progChartSuspendUpdating = new Progress<bool>(isSuspend =>
  137. {
  138. if (isSuspend)
  139. _suspender = ((DataView)View).chart.SuspendSuspendUpdates();
  140. else
  141. {
  142. try
  143. {
  144. if (_suspender?.IsSuspended == true)
  145. _suspender?.SafeDispose();
  146. }
  147. catch (Exception)
  148. {
  149. // 查询过程最后会强制恢复挂起一次,可能引发异常,忽略此异常。
  150. }
  151. }
  152. });
  153. _progShowErrorMessageBox = new Progress<string>((error =>
  154. {
  155. System.Windows.Forms.MessageBox.Show(error, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
  156. }));
  157. }
  158. #endregion
  159. #region Property
  160. public bool IsPermission => Permission == 3;
  161. private DataView view;
  162. public TreeNode ParameterNodes { get; }
  163. public ChartingLineSeriesCollection SelectedData { get; set; }
  164. public AutoRange ChartAutoRange
  165. {
  166. get => _autoRange;
  167. set
  168. {
  169. _autoRange = value;
  170. NotifyOfPropertyChange(nameof(ChartAutoRange));
  171. }
  172. }
  173. public IRange VisibleRangeTime
  174. {
  175. get => _timeRange;
  176. set
  177. {
  178. _timeRange = value;
  179. NotifyOfPropertyChange(nameof(VisibleRangeTime));
  180. }
  181. }
  182. public IRange VisibleRangeValue
  183. {
  184. get => _visibleRangeValue;
  185. set
  186. {
  187. _visibleRangeValue = value;
  188. NotifyOfPropertyChange(nameof(VisibleRangeValue));
  189. }
  190. }
  191. //public DateTime StartDateTime
  192. //{
  193. // get => ((DataView)View).wfTimeFrom.Value;
  194. // set
  195. // {
  196. // ((DataView)View).wfTimeFrom.Value = value;
  197. // NotifyOfPropertyChange(nameof(StartDateTime));
  198. // }
  199. //}
  200. //public DateTime EndDateTime
  201. //{
  202. // get => ((DataView)View).wfTimeTo.Value;
  203. // set
  204. // {
  205. // ((DataView)View).wfTimeTo.Value = value;
  206. // NotifyOfPropertyChange(nameof(EndDateTime));
  207. // }
  208. //}
  209. public DateTime SearchBeginTime { get; set; }
  210. public DateTime SearchEndTime { get; set; }
  211. #endregion
  212. #region Methods
  213. protected override void OnViewLoaded(object view)
  214. {
  215. base.OnViewLoaded(view);
  216. this.view = (DataView)view;
  217. //StartDateTime = DateTime.Now.Date;
  218. //EndDateTime = DateTime.Now.Date.AddDays(1).AddTicks(-1);
  219. this.view.wfTimeFrom.Content = SearchBeginTime.ToString("yyyy-MM-dd HH:mm:ss");
  220. this.view.wfTimeTo.Content = SearchEndTime.ToString("yyyy-MM-dd HH:mm:ss");
  221. }
  222. public void SelectDate(string SelectType)
  223. {
  224. var windowManager = Caliburn.Micro.Core.IoC.Get<Caliburn.Micro.IWindowManager>();
  225. if (SelectType == "Start")
  226. {
  227. this.SearchBeginTime = Convert.ToDateTime(this.view.wfTimeFrom.Content);
  228. SelectDateViewModel selectdateViewModel = new SelectDateViewModel(SearchBeginTime);
  229. var result = (windowManager as Caliburn.Micro.WindowManager)?.ShowDialogWithTitle(selectdateViewModel, null, "Start Time");
  230. if (result == true)
  231. {
  232. SearchBeginTime = selectdateViewModel.SearchDate;
  233. this.view.wfTimeFrom.Content = selectdateViewModel.SearchDate;
  234. }
  235. }
  236. else
  237. {
  238. this.SearchEndTime = Convert.ToDateTime(this.view.wfTimeTo.Content);
  239. SelectDateViewModel selectdateViewModel = new SelectDateViewModel(SearchEndTime);
  240. var result = (windowManager as Caliburn.Micro.WindowManager)?.ShowDialogWithTitle(selectdateViewModel, null, "End Time");
  241. if (result == true)
  242. {
  243. this.view.wfTimeTo.Content = selectdateViewModel.SearchDate;
  244. }
  245. }
  246. }
  247. public void ZoomInClick(int index)
  248. {
  249. var view = GetView() as DataView;
  250. index += 1;
  251. if ((index & 0x01) == 1)
  252. view.chart.sciChart.ChartModifier.XAxis.ZoomBy(-0.1, -0.1);
  253. if ((index & 0x02) == 2)
  254. view.chart.sciChart.ChartModifier.YAxis.ZoomBy(-0.1, -0.1);
  255. }
  256. public void ZoomOutClick(int index)
  257. {
  258. var view = GetView() as DataView;
  259. index += 1;
  260. if ((index & 0x01) == 1)
  261. view.chart.sciChart.ChartModifier.XAxis.ZoomBy(0.1, 0.1);
  262. if ((index & 0x02) == 2)
  263. view.chart.sciChart.ChartModifier.YAxis.ZoomBy(0.1, 0.1);
  264. }
  265. public void ArrowClick(string direction)
  266. {
  267. var view = GetView() as DataView;
  268. switch (direction)
  269. {
  270. case "Up":
  271. view.chart.sciChart.ChartModifier.YAxis.Scroll(50, SciChart.Charting.ClipMode.ClipAtMax);
  272. break;
  273. case "Down":
  274. view.chart.sciChart.ChartModifier.YAxis.Scroll(-50, SciChart.Charting.ClipMode.ClipAtMin);
  275. break;
  276. case "Left":
  277. view.chart.sciChart.ChartModifier.XAxis.Scroll(-50, SciChart.Charting.ClipMode.ClipAtMin);
  278. break;
  279. case "Right":
  280. view.chart.sciChart.ChartModifier.XAxis.Scroll(50, SciChart.Charting.ClipMode.ClipAtMax);
  281. break;
  282. }
  283. }
  284. public void Query(object parameter)
  285. {
  286. WaferHistoryRecipe waferHistoryRecipe = parameter as WaferHistoryRecipe;
  287. if (waferHistoryRecipe != null)
  288. {
  289. System.Windows.Application.Current.Dispatcher.BeginInvoke(new Action(() =>
  290. {
  291. if (waferHistoryRecipe.StartTime != DateTime.MinValue)
  292. SearchBeginTime = waferHistoryRecipe.StartTime;
  293. if (waferHistoryRecipe.EndTime != DateTime.MinValue)
  294. SearchEndTime = waferHistoryRecipe.EndTime;
  295. else
  296. SearchEndTime = waferHistoryRecipe.StartTime.AddHours(1);
  297. //ParameterNodes.ForEachDo((x) =>
  298. //{
  299. // x.Selected = true;
  300. // ParameterCheck(x);
  301. //});
  302. QueryData();
  303. }));
  304. }
  305. }
  306. /// <summary>
  307. /// 查询数据。
  308. /// </summary>
  309. /// <exception cref="Exception"></exception>
  310. public void QueryData(bool isAppend = false)
  311. {
  312. this.SearchBeginTime = Convert.ToDateTime(this.view.wfTimeFrom.Content);
  313. this.SearchEndTime = Convert.ToDateTime(this.view.wfTimeTo.Content);
  314. if (SearchBeginTime > SearchEndTime)
  315. {
  316. MessageBox.Show("time range invalid, start time should be early than end time.", "Error",
  317. MessageBoxButton.OK,
  318. MessageBoxImage.Error);
  319. return;
  320. }
  321. // 哪些模组(TreeView的顶层Nodes)有选中项。
  322. var selectedModules = ParameterNodes.ChildNodes.Where(x => x.HasTerminalSelected).ToList();
  323. if (!selectedModules.Any())
  324. {
  325. MessageBox.Show($"No item(s) are selected to query.", "Warning", MessageBoxButton.OK,
  326. MessageBoxImage.Warning);
  327. return;
  328. }
  329. VisibleRangeTime = new DateRange(SearchBeginTime.AddHours(-1), SearchEndTime.AddHours(1));
  330. ChartAutoRange = AutoRange.Always;
  331. BusyIndicatorContent = "Preparing list ...";
  332. IsBusy = true;
  333. _cancellationTokenSource = new CancellationTokenSource();
  334. #region 生成曲线列表
  335. var selectedTerminal =
  336. selectedModules.SelectMany(x => x.Flatten(true).Where(p => p.IsSelected == true)).ToList();
  337. List<IRenderableSeries> appendedSeries = null;
  338. if (isAppend)
  339. appendedSeries = SelectedData.Append(selectedTerminal);
  340. else
  341. {
  342. SelectedData.ReArrange(selectedTerminal);
  343. appendedSeries = SelectedData.ToList();
  344. }
  345. SelectedData.ResetColors();
  346. #endregion
  347. var dataSeriesList = appendedSeries.Select(x => x.DataSeries).ToList();
  348. Task.Run(async () =>
  349. {
  350. // 延时启动,等待UI准备好
  351. await Task.Delay(500);
  352. var pipeline = new TwoStagePipelineBasedTaskExecutor<DataSet, object>();
  353. pipeline.Stage2ActionStarted += (s, e) =>
  354. {
  355. // 挂起Chart
  356. _progChartSuspendUpdating.Report(true);
  357. };
  358. pipeline.Stage2ActionFinished += (s, e) =>
  359. {
  360. // 挂起刷新
  361. _progChartSuspendUpdating.Report(false);
  362. };
  363. pipeline.Stage1Finished += (s, e) =>
  364. {
  365. // 查询完毕,Chart可能仍在渲染。
  366. _progQueryUpdate?.Report(new ProgressUpdatingEventArgs(50, 100, "Still rendering chart ..."));
  367. };
  368. using (pipeline)
  369. {
  370. var plTasks = pipeline.Start(null);
  371. try
  372. {
  373. //! 按时间分段查询,解决查询速度慢导致卡后台业务的问题。
  374. //var ts = SearchEndTime - SearchBeginTime;
  375. //if (ts.Days <= 1)
  376. //{
  377. // Query(pipeline, selectedModules, dataSeriesList, view.dataGrid1, SearchBeginTime, SearchEndTime,
  378. // _cancellationTokenSource, _progQueryUpdate);
  379. // // 结束流水线
  380. // pipeline.AppendFunc1(null);
  381. // pipeline.AppendFunc2(null);
  382. //}
  383. //else
  384. {
  385. var daySlices =
  386. DateRangeHelper.SplitInToHours(new DateRangeHelper(SearchBeginTime, SearchEndTime), 12);
  387. foreach (var range in daySlices)
  388. {
  389. Query(pipeline, selectedModules, dataSeriesList, view.dataGrid1, range.Start, range.End,
  390. _cancellationTokenSource, _progQueryUpdate);
  391. if (_cancellationTokenSource.Token.IsCancellationRequested)
  392. break;
  393. await Task.Delay(1);
  394. }
  395. // 结束流水线
  396. pipeline.AppendFunc1(null);
  397. pipeline.AppendFunc2(null);
  398. }
  399. await Task.WhenAll(plTasks.ToArray());
  400. }
  401. catch (ThreadAbortException ex)
  402. {
  403. Thread.Sleep(500);
  404. // 查询操作被取消。
  405. Debug.WriteLine(ex);
  406. }
  407. catch (AggregateException ae)
  408. {
  409. var errs = new StringBuilder();
  410. foreach (var ex in ae.Flatten().InnerExceptions)
  411. {
  412. LOG.Error(ex.Message, ex);
  413. errs.AppendLine(ex.Message);
  414. }
  415. var errMsg = $"It's failed to query data, {errs}";
  416. _progShowErrorMessageBox.Report(errMsg);
  417. LOG.Error(errMsg, ae);
  418. }
  419. catch (Exception ex)
  420. {
  421. var errMsg = $"It's failed to query data, {ex.Message}";
  422. _progShowErrorMessageBox.Report(errMsg);
  423. LOG.Error(errMsg, ex);
  424. }
  425. finally
  426. {
  427. // 等待一下UI
  428. Thread.Sleep(100);
  429. _progQueryUpdate.Report(new ProgressUpdatingEventArgs(100, 100, ""));
  430. // 强制恢复Chart刷新,避免异常情况导致的Chart挂起。
  431. _progChartSuspendUpdating.Report(false);
  432. }
  433. }
  434. });
  435. }
  436. /// <summary>
  437. /// 查询数据
  438. /// </summary>
  439. private static void Query(TwoStagePipelineBasedTaskExecutor<DataSet, object> pipeline, IEnumerable<TreeNode> selectedModules, List<IDataSeries> dataSeriesList, System.Windows.Controls.DataGrid dataGrid, DateTime startTime, DateTime endTime,
  440. CancellationTokenSource cancellation, IProgress<ProgressUpdatingEventArgs> progressReporter = null)
  441. {
  442. pipeline.AppendFunc1(() =>
  443. SearchDataBaseAsync(
  444. selectedModules,
  445. new DateRangeHelper(startTime, endTime),
  446. cancellation,
  447. progressReporter));
  448. pipeline.AppendFunc2(ds =>
  449. {
  450. RenderChartAndTable(ds, dataSeriesList, dataGrid, cancellation, progressReporter);
  451. return null;
  452. });
  453. //pipeline.AppendFunc2(ds =>
  454. //{
  455. // RenderDataGridTable(ds, , cancellation, progressReporter);
  456. // return null;
  457. //});
  458. }
  459. /// <summary>
  460. /// 取消查询。
  461. /// </summary>
  462. public void CancelQuery()
  463. {
  464. Task.Run(() =>
  465. {
  466. if (_cancellationTokenSource?.Token.CanBeCanceled == true)
  467. {
  468. _cancellationTokenSource.Cancel();
  469. }
  470. Thread.Sleep(100);
  471. _progQueryUpdate.Report(new ProgressUpdatingEventArgs(100, 100, ""));
  472. _progChartSuspendUpdating.Report(false);
  473. });
  474. if (View is DataView view)
  475. view.dataGrid.CancelOperation();
  476. }
  477. /// <summary>
  478. /// 检查指定的表名是否存在。
  479. /// </summary>
  480. /// <param name="tableName"></param>
  481. /// <returns></returns>
  482. private static bool CheckTableExists(string tableName)
  483. {
  484. var sql =
  485. $"SELECT EXISTS ( SELECT FROM pg_tables WHERE schemaname = 'public' AND tablename = '{tableName}' );";
  486. var table = QueryDataClient.Instance.Service.QueryData(sql);
  487. if (table == null)
  488. return false;
  489. if (table.Rows.Count <= 0)
  490. return false;
  491. var value = table.Rows[0][0].ToString();
  492. if (value.ToLower() == "true")
  493. return true;
  494. return false;
  495. }
  496. /// <summary>
  497. /// 根据左侧选项查询数据
  498. /// </summary>
  499. /// <returns></returns>
  500. private static DataSet SearchDataBaseAsync(
  501. IEnumerable<TreeNode> modules, DateRangeHelper dateRange,
  502. CancellationTokenSource cancellation = null,
  503. IProgress<ProgressUpdatingEventArgs> progressReporter = null)
  504. {
  505. var ds = new DataSet();
  506. using (cancellation?.Token.Register(Thread.CurrentThread.Abort, true))
  507. {
  508. // 遍历模组
  509. foreach (var module in modules)
  510. {
  511. var sql = new StringBuilder();
  512. //! 因为数据库中按天拆表,无法一次性查询数据,需使用UNION合并多表查询,因此此处按天拼接SQL表达式
  513. // 最终SQL表达式结构为:
  514. // (select xx from date1.xx) union (select xx from date2.xx) union (select xx from date3.xx)
  515. // where time between xxx and xxx
  516. // order by time asc
  517. var ts = dateRange.Diff;
  518. for (var day = 0; day <= ts.Days; day++)
  519. {
  520. // 检查表名是否存在,否则SQL执行出错。
  521. var tblName = $"{dateRange.Start.AddDays(day):yyyyMMdd}.{module}";
  522. if (module.ToString() == "IO")
  523. {
  524. //var node = module.ChildNodes.FirstOrDefault(x => (bool)x.HasTerminalSelected);
  525. tblName = $"{dateRange.Start.AddDays(day):yyyyMMdd}.System";
  526. }
  527. else
  528. if (!ModuleHelper.IsPm(module.ToString()))
  529. {
  530. tblName = $"{dateRange.Start.AddDays(day):yyyyMMdd}.System";
  531. }
  532. if (CheckTableExists(tblName))
  533. {
  534. sql.Append("select \"time\" AS InternalTimeStamp");
  535. var selectedParams = module.Flatten(true)
  536. .Where(x => x.IsSelected == true);
  537. // 添加待查询的列
  538. foreach (var item in selectedParams)
  539. {
  540. sql.Append("," + $"\"{item}\"");
  541. }
  542. sql.Append($" from \"{tblName}\" ");
  543. if (day < ts.Days)
  544. sql.Append(" UNION ");
  545. }
  546. }
  547. // 所有表名不可用,可能是日期范围错误
  548. if (sql.Length <= 0)
  549. {
  550. continue;
  551. }
  552. sql.Append(
  553. $" where \"time\" between {dateRange.Start.Ticks} and {dateRange.End.Ticks} order by InternalTimeStamp asc");
  554. progressReporter?.Report(new ProgressUpdatingEventArgs(20, 100,
  555. $"Querying {dateRange}..."));
  556. if (cancellation?.Token.IsCancellationRequested == true)
  557. return null;
  558. // 查询数据并将返回的结果存储在DataSet中
  559. var dataTable = QueryDataClient.Instance.Service.QueryData(sql.ToString());
  560. if (cancellation?.Token.IsCancellationRequested == true)
  561. return null;
  562. //! 返回的 DataTable 可能不存在,原因是上述代码自动生成的表明可能不存在。
  563. if (dataTable == null)
  564. continue;
  565. dataTable.TableName = module.Name;
  566. ds.Tables.Add(dataTable);
  567. }
  568. }
  569. return ds;
  570. }
  571. /// <summary>
  572. /// 渲染图表。
  573. /// </summary>
  574. /// <param name="ds"></param>
  575. /// <param name="dataSeriesList"></param>
  576. /// <param name="cancellation"></param>
  577. /// <param name="progressReporter"></param>
  578. /// <exception cref="Exception"></exception>
  579. private static void RenderChartAndTable(
  580. DataSet ds, List<IDataSeries> dataSeriesList, System.Windows.Controls.DataGrid dataGrid,
  581. CancellationTokenSource cancellation = null,
  582. IProgress<ProgressUpdatingEventArgs> progressReporter = null)
  583. {
  584. if (ds == null || ds.Tables.Count <= 0)
  585. return;
  586. Dictionary<string, Dictionary<long, string>> bigTable = new Dictionary<string, Dictionary<long, string>>();
  587. System.Windows.Application.Current.Dispatcher.BeginInvoke(new Action(() =>
  588. {
  589. dataGrid.Columns.Clear();
  590. DataGridTextColumn dataGridTextColumn = new DataGridTextColumn();
  591. dataGridTextColumn.Header = "Time";
  592. dataGridTextColumn.Binding = new System.Windows.Data.Binding("internaltimestamp");
  593. ((System.Windows.Data.Binding)dataGridTextColumn.Binding).Converter = new StringToDateTimeStringConvert();
  594. dataGrid.Columns.Add(dataGridTextColumn);
  595. }));
  596. // 一个Table一个模组
  597. foreach (var table in ds.Tables.Cast<DataTable>())
  598. {
  599. if (table.Rows.Count <= 0)
  600. continue;
  601. // 一列对应模组中的某个数据记录点
  602. foreach (var col in table.Columns.Cast<DataColumn>())
  603. {
  604. // 忽略时间列
  605. if (col.Ordinal == 0)
  606. continue;
  607. Dictionary<long, string> dictColumn = new Dictionary<long, string>();
  608. var fullName = col.ColumnName;
  609. col.ColumnName = col.ColumnName.Replace(".", "");
  610. var dataSeries =
  611. dataSeriesList.FirstOrDefault(x => x.SeriesName == fullName) as XyDataSeries<DateTime, double>;
  612. if (dataSeries == null)
  613. continue;
  614. var rows = table.Rows;
  615. var dateList = new List<DateTime>();
  616. var valueList = new List<double>();
  617. var metaList = new List<ParameterNodePoint>();
  618. for (var i = 0; i < rows.Count; i++)
  619. {
  620. var date = new DateTime(long.Parse(rows[i][0].ToString()));
  621. var cellValue = rows[i][col];
  622. var value = double.NaN;
  623. if (cellValue is bool b)
  624. value = b ? 1 : 0;
  625. else if (double.TryParse(cellValue.ToString(), out var num))
  626. value = num;
  627. else
  628. value = 0;
  629. dateList.Add(date);
  630. valueList.Add(value);
  631. metaList.Add(new ParameterNodePoint(date, value));
  632. dictColumn.Add(long.Parse(rows[i][0].ToString()), value.ToString());
  633. if (cancellation?.Token.IsCancellationRequested == true)
  634. return;
  635. }
  636. bigTable.Add(fullName.Replace(".", ""), dictColumn);
  637. System.Windows.Application.Current.Dispatcher.BeginInvoke(new Action(() =>
  638. {
  639. DataGridTextColumn dataGridTextColumn2 = new DataGridTextColumn();
  640. dataGridTextColumn2.Header = fullName;
  641. dataGridTextColumn2.Binding = new System.Windows.Data.Binding(fullName.Replace(".", ""));
  642. var MyStyle = new Style(typeof(DataGridCell))
  643. {
  644. Setters = {
  645. new Setter(TextBlock.TextAlignmentProperty, TextAlignment.Center)
  646. }
  647. };
  648. dataGridTextColumn2.CellStyle = MyStyle;
  649. dataGrid.Columns.Add(dataGridTextColumn2);
  650. }));
  651. if (cancellation?.Token.IsCancellationRequested == true)
  652. return;
  653. dataSeries.Append(dateList, valueList, metaList);
  654. }
  655. if (cancellation?.Token.IsCancellationRequested == true)
  656. return;
  657. // 每一轮更新完毕后会恢复Chart刷新,
  658. // 数据量太小时会频繁挂起和恢复Chart,导致Chart不刷新,需要稍等一下UI。
  659. Thread.Sleep(50);
  660. }
  661. System.Windows.Application.Current.Dispatcher.BeginInvoke(new Action(() =>
  662. {
  663. System.Data.DataTable dt = new System.Data.DataTable();
  664. dt = new System.Data.DataTable($"Temp");
  665. dt.Columns.Add("internaltimestamp", typeof(long));
  666. foreach (var item in bigTable.Keys)
  667. {
  668. dt.Columns.Add(item, typeof(string));
  669. }
  670. if (bigTable.Count > 0)
  671. {
  672. foreach (var subitem in bigTable[bigTable.FirstOrDefault().Key].Keys)
  673. {
  674. var row = dt.NewRow();
  675. row[0] = subitem;
  676. foreach (var col in bigTable.Keys)
  677. {
  678. row[col] = bigTable[col][subitem];
  679. }
  680. dt.Rows.Add(row);
  681. }
  682. }
  683. dataGrid.ItemsSource = dt.DefaultView;
  684. }));
  685. }
  686. public void Exporting(object sender, EventArgs e)
  687. {
  688. BusyIndicatorContent = "Exporting Start ...";
  689. IsBusy = true;
  690. }
  691. public void Exported(object sender, EventArgs e)
  692. {
  693. IsBusy = false;
  694. }
  695. public void Deleted(object sender, EventArgs e)
  696. {
  697. ((DataView)View).tvParameterNodes.ClearPresetGroupSelectionOnly();
  698. }
  699. public void ProgressUpdating(object sender, ProgressUpdatingEventArgs e)
  700. {
  701. _progCsvExport.Report(e);
  702. }
  703. #endregion
  704. }
  705. public class StringToDateTimeStringConvert : IValueConverter
  706. {
  707. public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
  708. {
  709. return new DateTime((Int64)value).ToString("yyyy/MM/dd HH:mm:ss");
  710. }
  711. public object ConvertBack(object value, Type targetTypes, object parameter, System.Globalization.CultureInfo culture)
  712. {
  713. return null;
  714. }
  715. }
  716. }