DataViewModel.cs 34 KB

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