DataViewModel.cs 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895
  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 == 3;
  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 ToLeftClick(int index)
  271. {
  272. var view = GetView() as DataView;
  273. index += 1;//从0开始
  274. if ((index & 0x01) == 1)
  275. view.chart.sciChart.ChartModifier.XAxis.Scroll(50, SciChart.Charting.ClipMode.ClipAtMin);
  276. if ((index & 0x02) == 2)
  277. {
  278. view.chart.sciChart.ChartModifier.YAxis.Scroll(50, SciChart.Charting.ClipMode.ClipAtMin);
  279. }
  280. }
  281. public void ToRightClick(int index)
  282. {
  283. var view = GetView() as DataView;
  284. index += 1;
  285. if ((index & 0x01) == 1)
  286. view.chart.sciChart.ChartModifier.XAxis.Scroll(-50, SciChart.Charting.ClipMode.ClipAtMax);
  287. if ((index & 0x02) == 2)
  288. view.chart.sciChart.ChartModifier.YAxis.Scroll(-50, SciChart.Charting.ClipMode.ClipAtMax);
  289. }
  290. public void ZoomInClick(int index)
  291. {
  292. var view = GetView() as DataView;
  293. index += 1;
  294. if ((index & 0x01) == 1)
  295. view.chart.sciChart.ChartModifier.XAxis.ZoomBy(-0.1, -0.1);
  296. if ((index & 0x02) == 2)
  297. view.chart.sciChart.ChartModifier.YAxis.ZoomBy(-0.1, -0.1);
  298. }
  299. public void ZoomOutClick(int index)
  300. {
  301. var view = GetView() as DataView;
  302. index += 1;
  303. if ((index & 0x01) == 1)
  304. view.chart.sciChart.ChartModifier.XAxis.ZoomBy(0.1, 0.1);
  305. if ((index & 0x02) == 2)
  306. view.chart.sciChart.ChartModifier.YAxis.ZoomBy(0.1, 0.1);
  307. }
  308. public void Query(object parameter)
  309. {
  310. WaferHistoryRecipe waferHistoryRecipe = parameter as WaferHistoryRecipe;
  311. if (waferHistoryRecipe != null)
  312. {
  313. System.Windows.Application.Current.Dispatcher.BeginInvoke(new Action(() =>
  314. {
  315. if (waferHistoryRecipe.StartTime != DateTime.MinValue)
  316. SearchBeginTime = waferHistoryRecipe.StartTime;
  317. if (waferHistoryRecipe.EndTime != DateTime.MinValue)
  318. SearchEndTime = waferHistoryRecipe.EndTime;
  319. else
  320. SearchEndTime = waferHistoryRecipe.StartTime.AddHours(1);
  321. //ParameterNodes.ForEachDo((x) =>
  322. //{
  323. // x.Selected = true;
  324. // ParameterCheck(x);
  325. //});
  326. QueryData();
  327. }));
  328. }
  329. }
  330. /// <summary>
  331. /// 查询数据。
  332. /// </summary>
  333. /// <exception cref="Exception"></exception>
  334. public void QueryData(bool isAppend = false)
  335. {
  336. this.SearchBeginTime = Convert.ToDateTime(this.view.wfTimeFrom.Content);
  337. this.SearchEndTime = Convert.ToDateTime(this.view.wfTimeTo.Content);
  338. if (SearchBeginTime > SearchEndTime)
  339. {
  340. MessageBox.Show("time range invalid, start time should be early than end time.", "Error",
  341. MessageBoxButton.OK,
  342. MessageBoxImage.Error);
  343. return;
  344. }
  345. // 哪些模组(TreeView的顶层Nodes)有选中项。
  346. var selectedModules = ParameterNodes.ChildNodes.Where(x => x.HasTerminalSelected).ToList();
  347. if (!selectedModules.Any())
  348. {
  349. MessageBox.Show($"No item(s) are selected to query.", "Warning", MessageBoxButton.OK,
  350. MessageBoxImage.Warning);
  351. return;
  352. }
  353. VisibleRangeTime = new DateRange(SearchBeginTime.AddHours(-1), SearchEndTime.AddHours(1));
  354. ChartAutoRange = AutoRange.Always;
  355. BusyIndicatorContent = "Preparing list ...";
  356. IsBusy = true;
  357. _cancellationTokenSource = new CancellationTokenSource();
  358. #region 生成曲线列表
  359. var selectedTerminal =
  360. selectedModules.SelectMany(x => x.Flatten(true).Where(p => p.IsSelected == true)).ToList();
  361. List<IRenderableSeries> appendedSeries = null;
  362. if (isAppend)
  363. appendedSeries = SelectedData.Append(selectedTerminal);
  364. else
  365. {
  366. SelectedData.ReArrange(selectedTerminal);
  367. appendedSeries = SelectedData.ToList();
  368. }
  369. SelectedData.ResetColors();
  370. #endregion
  371. var dataSeriesList = appendedSeries.Select(x => x.DataSeries).ToList();
  372. Task.Run(async () =>
  373. {
  374. // 延时启动,等待UI准备好
  375. await Task.Delay(500);
  376. var pipeline = new TwoStagePipelineBasedTaskExecutor<DataSet, object>();
  377. pipeline.Stage2ActionStarted += (s, e) =>
  378. {
  379. // 挂起Chart
  380. _progChartSuspendUpdating.Report(true);
  381. };
  382. pipeline.Stage2ActionFinished += (s, e) =>
  383. {
  384. // 挂起刷新
  385. _progChartSuspendUpdating.Report(false);
  386. };
  387. pipeline.Stage1Finished += (s, e) =>
  388. {
  389. // 查询完毕,Chart可能仍在渲染。
  390. _progQueryUpdate?.Report(new ProgressUpdatingEventArgs(50, 100, "Still rendering chart ..."));
  391. };
  392. using (pipeline)
  393. {
  394. var plTasks = pipeline.Start(null);
  395. try
  396. {
  397. //! 按时间分段查询,解决查询速度慢导致卡后台业务的问题。
  398. // var ts = SearchEndTime - SearchBeginTime;
  399. //if (ts.Days <= 1)
  400. //{
  401. // Query(pipeline, selectedModules, dataSeriesList, view.dataGrid1, SearchBeginTime, SearchEndTime,
  402. // _cancellationTokenSource, _progQueryUpdate);
  403. // // 结束流水线
  404. // pipeline.AppendFunc1(null);
  405. // pipeline.AppendFunc2(null);
  406. //}
  407. //else
  408. {
  409. var daySlices =
  410. DateRangeHelper.SplitInToHours(new DateRangeHelper(SearchBeginTime, SearchEndTime), 12);
  411. foreach (var range in daySlices)
  412. {
  413. Query(pipeline, selectedModules, dataSeriesList, view.dataGrid1, range.Start, range.End,
  414. _cancellationTokenSource, _progQueryUpdate);
  415. if (_cancellationTokenSource.Token.IsCancellationRequested)
  416. break;
  417. await Task.Delay(1);
  418. }
  419. // 结束流水线
  420. pipeline.AppendFunc1(null);
  421. pipeline.AppendFunc2(null);
  422. }
  423. await Task.WhenAll(plTasks.ToArray());
  424. }
  425. catch (ThreadAbortException ex)
  426. {
  427. Thread.Sleep(500);
  428. // 查询操作被取消。
  429. Debug.WriteLine(ex);
  430. }
  431. catch (AggregateException ae)
  432. {
  433. var errs = new StringBuilder();
  434. foreach (var ex in ae.Flatten().InnerExceptions)
  435. {
  436. LOG.Error(ex.Message, ex);
  437. errs.AppendLine(ex.Message);
  438. }
  439. var errMsg = $"It's failed to query data, {errs}";
  440. _progShowErrorMessageBox.Report(errMsg);
  441. LOG.Error(errMsg, ae);
  442. }
  443. catch (Exception ex)
  444. {
  445. var errMsg = $"It's failed to query data, {ex.Message}";
  446. _progShowErrorMessageBox.Report(errMsg);
  447. LOG.Error(errMsg, ex);
  448. }
  449. finally
  450. {
  451. // 等待一下UI
  452. Thread.Sleep(100);
  453. _progQueryUpdate.Report(new ProgressUpdatingEventArgs(100, 100, ""));
  454. // 强制恢复Chart刷新,避免异常情况导致的Chart挂起。
  455. _progChartSuspendUpdating.Report(false);
  456. }
  457. }
  458. });
  459. }
  460. /// <summary>
  461. /// 查询数据
  462. /// </summary>
  463. private static void Query(TwoStagePipelineBasedTaskExecutor<DataSet, object> pipeline, IEnumerable<TreeNode> selectedModules, List<IDataSeries> dataSeriesList, System.Windows.Controls.DataGrid dataGrid, DateTime startTime, DateTime endTime,
  464. CancellationTokenSource cancellation, IProgress<ProgressUpdatingEventArgs> progressReporter = null)
  465. {
  466. pipeline.AppendFunc1(() =>
  467. SearchDataBaseAsync(
  468. selectedModules,
  469. new DateRangeHelper(startTime, endTime),
  470. cancellation,
  471. progressReporter));
  472. pipeline.AppendFunc2(ds =>
  473. {
  474. RenderChartAndTable(ds, dataSeriesList, dataGrid, cancellation, progressReporter);
  475. return null;
  476. });
  477. //pipeline.AppendFunc2(ds =>
  478. //{
  479. // RenderDataGridTable(ds, , cancellation, progressReporter);
  480. // return null;
  481. //});
  482. }
  483. /// <summary>
  484. /// 取消查询。
  485. /// </summary>
  486. public void CancelQuery()
  487. {
  488. Task.Run(() =>
  489. {
  490. if (_cancellationTokenSource?.Token.CanBeCanceled == true)
  491. {
  492. _cancellationTokenSource.Cancel();
  493. }
  494. Thread.Sleep(100);
  495. _progQueryUpdate.Report(new ProgressUpdatingEventArgs(100, 100, ""));
  496. _progChartSuspendUpdating.Report(false);
  497. });
  498. if (View is DataView view)
  499. view.dataGrid.CancelOperation();
  500. }
  501. /// <summary>
  502. /// 检查指定的表名是否存在。
  503. /// </summary>
  504. /// <param name="tableName"></param>
  505. /// <returns></returns>
  506. private static bool CheckTableExists(string tableName)
  507. {
  508. var sql =
  509. $"SELECT EXISTS ( SELECT FROM pg_tables WHERE schemaname = 'public' AND tablename = '{tableName}' );";
  510. var table = QueryDataClient.Instance.Service.QueryData(sql);
  511. if (table == null)
  512. return false;
  513. if (table.Rows.Count <= 0)
  514. return false;
  515. var value = table.Rows[0][0].ToString();
  516. if (value.ToLower() == "true")
  517. return true;
  518. return false;
  519. }
  520. private static string GetDBTableName(DateRangeHelper dateRange, TreeNode module, int day)
  521. {
  522. var queryColNode = module.Flatten(false).Where(a => a.IsSelected == true).FirstOrDefault();
  523. if (queryColNode == null)
  524. {
  525. return string.Empty;
  526. }
  527. var dbName = FindKeyByValue(_displayDic, queryColNode.FullName);
  528. var moudleName = dbName.Split('.')[0];
  529. var systemDeviceNameDict = _provider.GetSystemDeviceNameDict();
  530. if (moudleName != ModuleName.System.ToString() && moudleName != ModuleName.PM1.ToString())
  531. {
  532. moudleName = ModuleName.System.ToString();
  533. }
  534. return $"{dateRange.Start.AddDays(day):yyyyMMdd}.{moudleName}";
  535. }
  536. private static string GetQueryCol(TreeNode treeNode)
  537. {
  538. var fullName = treeNode.FullName;
  539. return FindKeyByValue(_displayDic, treeNode.FullName);
  540. }
  541. static string FindKeyByValue(Dictionary<string, string> dictionary, string value)
  542. {
  543. foreach (var pair in dictionary)
  544. {
  545. if (pair.Value == value)
  546. {
  547. return pair.Key;
  548. }
  549. }
  550. return null; // Return null if the value is not found
  551. }
  552. /// <summary>
  553. /// 根据左侧选项查询数据
  554. /// </summary>
  555. /// <returns></returns>
  556. private static DataSet SearchDataBaseAsync(
  557. IEnumerable<TreeNode> modules, DateRangeHelper dateRange,
  558. CancellationTokenSource cancellation = null,
  559. IProgress<ProgressUpdatingEventArgs> progressReporter = null)
  560. {
  561. var ds = new DataSet();
  562. using (cancellation?.Token.Register(Thread.CurrentThread.Abort, true))
  563. {
  564. // 遍历模组
  565. foreach (var module in modules)
  566. {
  567. var sql = new StringBuilder();
  568. //! 因为数据库中按天拆表,无法一次性查询数据,需使用UNION合并多表查询,因此此处按天拼接SQL表达式
  569. // 最终SQL表达式结构为:
  570. // (select xx from date1.xx) union (select xx from date2.xx) union (select xx from date3.xx)
  571. // where time between xxx and xxx
  572. // order by time asc
  573. var ts = dateRange.Diff;
  574. for (var day = 0; day <= ts.Days; day++)
  575. {
  576. // 检查表名是否存在,否则SQL执行出错。
  577. var tblName = GetDBTableName(dateRange, module, day);
  578. if (CheckTableExists(tblName))
  579. {
  580. sql.Append("select \"time\" AS InternalTimeStamp");
  581. var selectedParams = module.Flatten(true)
  582. .Where(x => x.IsSelected == true);
  583. // 添加待查询的列
  584. foreach (var item in selectedParams)
  585. {
  586. var colName = GetQueryCol(item);
  587. sql.Append("," + $"\"{colName}\"");
  588. }
  589. sql.Append($" from \"{tblName}\" ");
  590. if (day < ts.Days)
  591. sql.Append(" UNION ");
  592. }
  593. }
  594. // 所有表名不可用,可能是日期范围错误
  595. if (sql.Length <= 0)
  596. {
  597. continue;
  598. }
  599. sql.Append(
  600. $" where \"time\" between {dateRange.Start.Ticks} and {dateRange.End.Ticks} order by InternalTimeStamp asc");
  601. progressReporter?.Report(new ProgressUpdatingEventArgs(20, 100,
  602. $"Querying {dateRange}..."));
  603. if (cancellation?.Token.IsCancellationRequested == true)
  604. return null;
  605. // 查询数据并将返回的结果存储在DataSet中
  606. var dataTable = QueryDataClient.Instance.Service.QueryData(sql.ToString());
  607. if (cancellation?.Token.IsCancellationRequested == true)
  608. return null;
  609. //! 返回的 DataTable 可能不存在,原因是上述代码自动生成的表明可能不存在。
  610. if (dataTable == null)
  611. continue;
  612. dataTable.TableName = module.Name;
  613. ds.Tables.Add(dataTable);
  614. }
  615. }
  616. return ds;
  617. }
  618. /// <summary>
  619. /// 渲染图表。
  620. /// </summary>
  621. /// <param name="ds"></param>
  622. /// <param name="dataSeriesList"></param>
  623. /// <param name="cancellation"></param>
  624. /// <param name="progressReporter"></param>
  625. /// <exception cref="Exception"></exception>
  626. private static void RenderChartAndTable(
  627. DataSet ds, List<IDataSeries> dataSeriesList, System.Windows.Controls.DataGrid dataGrid,
  628. CancellationTokenSource cancellation = null,
  629. IProgress<ProgressUpdatingEventArgs> progressReporter = null)
  630. {
  631. if (ds == null || ds.Tables.Count <= 0)
  632. return;
  633. Dictionary<string, Dictionary<long, string>> bigTable = new Dictionary<string, Dictionary<long, string>>();
  634. System.Windows.Application.Current.Dispatcher.BeginInvoke(new Action(() =>
  635. {
  636. dataGrid.Columns.Clear();
  637. DataGridTextColumn dataGridTextColumn = new DataGridTextColumn();
  638. dataGridTextColumn.Header = "Time";
  639. dataGridTextColumn.Binding = new System.Windows.Data.Binding("internaltimestamp");
  640. ((System.Windows.Data.Binding)dataGridTextColumn.Binding).Converter = new StringToDateTimeStringConvert();
  641. dataGrid.Columns.Add(dataGridTextColumn);
  642. }));
  643. // 一个Table一个模组
  644. foreach (var table in ds.Tables.Cast<DataTable>())
  645. {
  646. if (table.Rows.Count <= 0)
  647. continue;
  648. // 一列对应模组中的某个数据记录点
  649. foreach (var col in table.Columns.Cast<DataColumn>())
  650. {
  651. // 忽略时间列
  652. if (col.Ordinal == 0)
  653. continue;
  654. Dictionary<long, string> dictColumn = new Dictionary<long, string>();
  655. var fullName = _displayDic[col.ColumnName];
  656. col.ColumnName = col.ColumnName.Replace(".", "");
  657. //fullName是数据库字段,SerierName是界面选择字段,需要做个转换
  658. var dataSeries =
  659. dataSeriesList.FirstOrDefault(x => x.SeriesName == fullName) as XyDataSeries<DateTime, double>;
  660. if (dataSeries == null)
  661. continue;
  662. var rows = table.Rows;
  663. var dateList = new List<DateTime>();
  664. var valueList = new List<double>();
  665. var metaList = new List<ParameterNodePoint>();
  666. for (var i = 0; i < rows.Count; i++)
  667. {
  668. var date = new DateTime(long.Parse(rows[i][0].ToString()));
  669. var cellValue = rows[i][col];
  670. var value = double.NaN;
  671. if (cellValue is bool b)
  672. value = b ? 1 : 0;
  673. else if (double.TryParse(cellValue.ToString(), out var num))
  674. value = num;
  675. else
  676. value = 0;
  677. dateList.Add(date);
  678. valueList.Add(value);
  679. metaList.Add(new ParameterNodePoint(date, value));
  680. dictColumn.Add(long.Parse(rows[i][0].ToString()), value.ToString());
  681. if (cancellation?.Token.IsCancellationRequested == true)
  682. return;
  683. }
  684. bigTable.Add(fullName.Replace(".", ""), dictColumn);
  685. System.Windows.Application.Current.Dispatcher.BeginInvoke(new Action(() =>
  686. {
  687. DataGridTextColumn dataGridTextColumn2 = new DataGridTextColumn();
  688. dataGridTextColumn2.Header = fullName;
  689. dataGridTextColumn2.Binding = new System.Windows.Data.Binding(fullName.Replace(".", ""));
  690. var MyStyle = new Style(typeof(DataGridCell))
  691. {
  692. Setters = {
  693. new Setter(TextBlock.TextAlignmentProperty, TextAlignment.Center)
  694. }
  695. };
  696. dataGridTextColumn2.CellStyle = MyStyle;
  697. dataGrid.Columns.Add(dataGridTextColumn2);
  698. }));
  699. if (cancellation?.Token.IsCancellationRequested == true)
  700. return;
  701. dataSeries.Append(dateList, valueList, metaList);
  702. }
  703. if (cancellation?.Token.IsCancellationRequested == true)
  704. return;
  705. // 每一轮更新完毕后会恢复Chart刷新,
  706. // 数据量太小时会频繁挂起和恢复Chart,导致Chart不刷新,需要稍等一下UI。
  707. Thread.Sleep(50);
  708. }
  709. System.Windows.Application.Current.Dispatcher.BeginInvoke(new Action(() =>
  710. {
  711. System.Data.DataTable dt = new System.Data.DataTable();
  712. dt = new System.Data.DataTable($"Temp");
  713. dt.Columns.Add("internaltimestamp", typeof(long));
  714. foreach (var item in bigTable.Keys)
  715. {
  716. dt.Columns.Add(item, typeof(string));
  717. }
  718. if (bigTable.Count > 0)
  719. {
  720. foreach (var subitem in bigTable[bigTable.FirstOrDefault().Key].Keys)
  721. {
  722. var row = dt.NewRow();
  723. row[0] = subitem;
  724. foreach (var col in bigTable.Keys)
  725. {
  726. row[col] = bigTable[col][subitem];
  727. }
  728. dt.Rows.Add(row);
  729. }
  730. }
  731. dataGrid.ItemsSource = dt.DefaultView;
  732. }));
  733. }
  734. public void Exporting(object sender, EventArgs e)
  735. {
  736. BusyIndicatorContent = "Exporting Start ...";
  737. IsBusy = true;
  738. }
  739. public void Exported(object sender, EventArgs e)
  740. {
  741. IsBusy = false;
  742. }
  743. public void Deleted(object sender, EventArgs e)
  744. {
  745. ((DataView)View).tvParameterNodes.ClearPresetGroupSelectionOnly();
  746. }
  747. public void ProgressUpdating(object sender, ProgressUpdatingEventArgs e)
  748. {
  749. _progCsvExport.Report(e);
  750. }
  751. #endregion
  752. }
  753. public class StringToDateTimeStringConvert : IValueConverter
  754. {
  755. public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
  756. {
  757. return new DateTime((Int64)value).ToString("yyyy/MM/dd HH:mm:ss");
  758. }
  759. public object ConvertBack(object value, Type targetTypes, object parameter, System.Globalization.CultureInfo culture)
  760. {
  761. return null;
  762. }
  763. }
  764. }