DBInfoTraceViewModel.cs 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678
  1. using Mapster;
  2. using ScottPlot;
  3. using ScottPlot.Plottables;
  4. using ScottPlot.WPF;
  5. using SqlSugar;
  6. using System.Windows;
  7. using System.Windows.Media;
  8. using Universal;
  9. namespace ProximaAnalizer.ViewModels;
  10. internal partial class DBInfoTraceViewModel : ObservableObject
  11. {
  12. public DBInfoTraceViewModel(TraceData traceData,
  13. SqlSugarCustom sqlSugarCustom,
  14. IEventAggregator eventAggregator,
  15. IDialogService dialogService)
  16. {
  17. this.traceData = traceData;
  18. this.sqlSugarCustom = sqlSugarCustom;
  19. this.dialogService = dialogService;
  20. this.PlotControl = new();
  21. this.InitPlot();
  22. eventAggregator.GetEvent<RefreshRecipeData>().Subscribe(Search);
  23. }
  24. private void InitPlot()
  25. {
  26. this.PlotControl.Plot.Grid.XAxisStyle.MajorLineStyle.Width = 1f;
  27. this.PlotControl.Plot.Grid.YAxisStyle.MajorLineStyle.Width = 1f;
  28. this.PlotControl.Plot.Axes.Bottom.TickLabelStyle.Alignment = Alignment.MiddleLeft;
  29. this.PlotControl.Plot.Axes.DateTimeTicksBottom();
  30. this.PlotControl.Background = (Brush)App.Current.Resources.FindName("BackgroundColor");
  31. this.PlotControl.Plot.RenderManager.RenderStarting += (s, e) =>
  32. {
  33. Tick[] ticks = this.PlotControl.Plot.Axes.Bottom.TickGenerator.Ticks;
  34. for (int i = 0; i < ticks.Length; i++)
  35. {
  36. DateTime dt = DateTime.FromOADate(ticks[i].Position);
  37. //string label = $"{dt:MM-dd HH:mm:ss}";
  38. string label = $"{dt:HH:mm:ss}";
  39. ticks[i] = new Tick(ticks[i].Position, label);
  40. }
  41. };
  42. this.PlotControl.Plot.FigureBackground.Color = ScottPlot.Colors.Transparent;
  43. this.PlotControl.Plot.Axes.Bottom.TickLabelStyle.Rotation = 90;
  44. this.PlotControl.Plot.Axes.Bottom.TickLabelStyle.Alignment = Alignment.MiddleLeft;
  45. PixelPadding padding = new(48, 48, 56, 40);
  46. this.PlotControl.Plot.Layout.Fixed(padding);
  47. this.PlotControl.Plot.FigureBackground.Color = ScottPlot.Colors.Transparent;
  48. //UpdateDetail();
  49. }
  50. [ObservableProperty]
  51. private WpfPlot _PlotControl;
  52. private readonly TraceData traceData;
  53. private readonly SqlSugarCustom sqlSugarCustom;
  54. private readonly IDialogService dialogService;
  55. [ObservableProperty]
  56. private ObservableDictionary<DateTime, SelectRecipeStep> _RecipeSteps = [];
  57. [ObservableProperty]
  58. private IDictionary<string, object>? _Hierachy;
  59. [ObservableProperty]
  60. private ObservableDictionary<string, object> _Left = [];
  61. [ObservableProperty]
  62. private ObservableDictionary<string, object> _Right = [];
  63. private readonly Stack<IDictionary<string, object>> Cache = new();
  64. private bool AutoGenerated = false;
  65. [RelayCommand]
  66. private void Selected(SelectRecipeStep step)
  67. {
  68. if (RecipeSteps is null)
  69. return;
  70. if (sqlSugarCustom.Client is null)
  71. return;
  72. if (AutoGenerated)
  73. {
  74. UnSelected(step);
  75. return;
  76. }
  77. if (RecipeSteps.Where(t => t.Value.IsSelected).Count() != 2)
  78. return;
  79. DateTime start = RecipeSteps.Where(t => t.Value.IsSelected).First().Key;
  80. DateTime end = RecipeSteps.Where(t => t.Value.IsSelected).Last().Key;
  81. RecipeSteps.Where(t => t.Key >= start && t.Key <= end).Foreach(t => t.Value.IsSelected = true);
  82. this.AutoGenerated = true;
  83. }
  84. [RelayCommand]
  85. private void UnSelected(SelectRecipeStep step)
  86. {
  87. RecipeSteps.Where(t => t.Value.IsSelected).Foreach(t => t.Value.IsSelected = false);
  88. this.AutoGenerated = false;
  89. }
  90. [RelayCommand]
  91. private void SelectHierachy(KeyValuePair<string, object> item)
  92. {
  93. if (this.Hierachy is null)
  94. return;
  95. if (item.Value is IDictionary<string, object> next)
  96. {
  97. this.Cache.Push(this.Hierachy);
  98. this.Hierachy = next;
  99. return;
  100. }
  101. //this.Left.TryAdd((string)item.Value, item.Value);
  102. //IDialogParameters parameters, Action< IDialogResult > callback
  103. IDialogParameters para = new DialogParameters() { { "Line", item.Value } };
  104. dialogService.Show("LinePicker", para, AddLine);
  105. }
  106. [RelayCommand]
  107. private void EditLine(KeyValuePair<string, object> item)
  108. {
  109. IDialogParameters para = new DialogParameters() { { "Line", item.Key } };
  110. dialogService.Show("LinePicker", para, AddLine);
  111. }
  112. private void AddLine(IDialogResult dialogResult)
  113. {
  114. dialogResult.Parameters.TryGetValue("Line", out LineType? lineType);
  115. dialogResult.Parameters.TryGetValue("Name", out string? line);
  116. dialogResult.Parameters.TryGetValue("Axis", out string? Axis);
  117. if (lineType is null || string.IsNullOrEmpty(line) || string.IsNullOrEmpty(Axis))
  118. return;
  119. ObservableDictionary<string, object>? lineCollection = Axis switch
  120. {
  121. "L" => Left,
  122. "R" => Right,
  123. _ => default
  124. };
  125. if (lineCollection is null)
  126. return;
  127. if (lineCollection.TryGetValue(line, out object? oldType) && oldType is LineType oldLine)
  128. oldLine.IsEnable = true;
  129. lineCollection[line] = lineType;
  130. }
  131. [RelayCommand]
  132. private void Return()
  133. {
  134. if (this.Cache.TryPop(out var output) && output is not null)
  135. this.Hierachy = output;
  136. }
  137. [RelayCommand]
  138. private void Search()
  139. {
  140. if (traceData.ProcessData is null)
  141. return;
  142. if (sqlSugarCustom.Client is null)
  143. return;
  144. this.Cache.Clear();
  145. this.RecipeSteps ??= [];
  146. this.RecipeSteps.Clear();
  147. this.Clear("");
  148. DateTime beginTime = traceData.ProcessData.Process_Begin_Time;
  149. DateTime endTime = traceData.ProcessData.Process_End_Time;
  150. long beginTicks = beginTime.Ticks;
  151. long endTicks = endTime.Ticks;
  152. List<RecipeStepData> recipeSteps;
  153. try
  154. {
  155. recipeSteps = sqlSugarCustom.Client.Queryable<RecipeStepData>().AS("recipe_step_data")
  156. .Where(t => t.Process_Data_Guid == traceData.ProcessData.Guid)
  157. .OrderBy(t => t.Step_Begin_Time).ToList();
  158. }
  159. catch
  160. {
  161. MessageBox.Show($"recipe_step_data 记录不存在", "Warning", MessageBoxButton.OK, MessageBoxImage.Error);
  162. return;
  163. }
  164. if (recipeSteps.Count == 0)
  165. {
  166. MessageBox.Show($"recipe_step 步骤数量为0", "Warning", MessageBoxButton.OK, MessageBoxImage.Error);
  167. return;
  168. }
  169. foreach (var item in recipeSteps)
  170. {
  171. SelectRecipeStep step = new();
  172. item.Adapt(step);
  173. this.RecipeSteps.TryAdd(item.Step_Begin_Time, step);
  174. }
  175. dynamic f;
  176. dynamic system;
  177. try
  178. {
  179. f = sqlSugarCustom.Client.Queryable<dynamic>().AS($"\"{beginTime:yyyyMMdd}.PM1\"").First();
  180. system = sqlSugarCustom.Client.Queryable<dynamic>().AS($"\"{beginTime:yyyyMMdd}.System\"").First();
  181. }
  182. catch
  183. {
  184. MessageBox.Show($"{beginTime:yyyyMMdd} 记录不存在", "Warning", MessageBoxButton.OK, MessageBoxImage.Error);
  185. return;
  186. }
  187. GeneralProcessData processData = new();
  188. if (f is not IDictionary<string, object> input)
  189. return;
  190. if (!processData.ToDictionary(input, out Dictionary<string, object>? output) || output is null)
  191. return;
  192. if (output["PM1"] is not IDictionary<string, object> pmData)
  193. return;
  194. if (!processData.ToDictionary(system, out Dictionary<string, object>? systemDic) || systemDic is null)
  195. return;
  196. Dictionary<string, object> temp = [];
  197. Dictionary<string, object> PM1 = [];
  198. Dictionary<string, object> System = [];
  199. temp.Add("PM1", PM1);
  200. temp.Add("System", System);
  201. this.CreateTablePM(pmData, PM1);
  202. this.CreateTableSystem(systemDic!, System);
  203. this.Hierachy = temp;
  204. this.Cache.Push(temp);
  205. }
  206. private bool CreateTablePM(IDictionary<string, object> data, IDictionary<string, object> cache)
  207. {
  208. Dictionary<string, object> ValueSensor = [];
  209. Dictionary<string, object> StatusSensor = [];
  210. Dictionary<string, object> leakCheck = [];
  211. Dictionary<string, object> recipe = [];
  212. Dictionary<string, object> aoValue = [];
  213. Dictionary<string, object> ffu = [];
  214. Dictionary<string, object> mfc = [];
  215. Dictionary<string, object> gaslineHeater = [];
  216. Dictionary<string, object> bufferFoup = [];
  217. Dictionary<string, object> avValue = [];
  218. foreach (var item in data)
  219. {
  220. if (item.Value is not IDictionary<string, object> values)
  221. {
  222. switch (item.Key)
  223. {
  224. case string s when s.EndsWith("Enable"):
  225. continue;
  226. case string s when s.StartsWith("LeakCheck"):
  227. leakCheck.Add(item.Key, item.Value);
  228. continue;
  229. default:
  230. recipe.Add(item.Key, item.Value);
  231. continue;
  232. }
  233. }
  234. switch (item.Key)
  235. {
  236. case "APC":
  237. case "APCVATGV":
  238. case "BoatElevatorServo":
  239. case "BoatRotationServo":
  240. case "BufferServo":
  241. case "Shutter":
  242. CreateTable(cache, item.Key, values);
  243. continue;
  244. case string s when s.StartsWith("Trig"):
  245. if (item.Value is IDictionary<string, object> value)
  246. aoValue.Add(item.Key, value["AOValue"]);
  247. continue;
  248. case string s when s.StartsWith("FS"):
  249. case string s1 when s1.StartsWith("PG"):
  250. case string s2 when s2.StartsWith("PS"):
  251. case string s3 when s3.StartsWith("VG"):
  252. if (item.Value is IDictionary<string, object> vss)
  253. ValueSensor.Add(item.Key, vss["Value"]);
  254. continue;
  255. case string s when s.StartsWith("Sensor"):
  256. if (item.Value is IDictionary<string, object> status)
  257. StatusSensor.Add(item.Key, status["Value"]);
  258. continue;
  259. case string s when s.StartsWith("MFC"):
  260. if (item.Value is IDictionary<string, object> mfcs)
  261. mfcs.Foreach(t => mfc.Add($"{item.Key}_{t.Key}", t.Value));
  262. continue;
  263. case string s when s.StartsWith("FFU"):
  264. if (item.Value is IDictionary<string, object> ffus)
  265. ffus.Foreach(t => ffu.Add($"{item.Key}_{t.Key}", t.Value));
  266. continue;
  267. case string s when s.StartsWith("GaselineHeater"):
  268. if (item.Value is IDictionary<string, object> gaslines)
  269. gaslines.Foreach(t => gaslineHeater.Add($"{item.Key}_{t.Key}", t.Value));
  270. continue;
  271. case string s when s.StartsWith("Valve"):
  272. if (item.Value is IDictionary<string, object> valves)
  273. valves.Foreach(t => avValue.Add($"{item.Key}_{t.Key}", t.Value));
  274. continue;
  275. default:
  276. continue;
  277. }
  278. }
  279. CreateTable(cache, "MFC", mfc);
  280. CreateTable(cache, "FFU", ffu);
  281. CreateTable(cache, "Valve", avValue);
  282. CreateTable(cache, "GaselineHeater", gaslineHeater);
  283. CreateTable(cache, "ValueSensor", ValueSensor);
  284. CreateTable(cache, "StatusSensor", StatusSensor);
  285. CreateTable(cache, "LeakCheck", leakCheck);
  286. CreateTable(cache, "AoValue", aoValue);
  287. CreateTable(cache, "Recipe", recipe);
  288. return true;
  289. }
  290. private void CreateTableSystem(IDictionary<string, object> data, IDictionary<string, object> cache)
  291. {
  292. Dictionary<string, object> systemCollection = [];
  293. Dictionary<string, object> alarmCollection = [];
  294. Dictionary<string, object> Heater = [];
  295. Dictionary<string, object> Stocker = [];
  296. Dictionary<string, object> LoadPort = [];
  297. Dictionary<string, object> FIMS = [];
  298. foreach (var item in data)
  299. {
  300. if (item.Value is not IDictionary<string, object> values)
  301. continue;
  302. switch (item.Key)
  303. {
  304. case "Boat":
  305. case "CarrierRobot":
  306. case "Scheduler":
  307. case "WaferRobot":
  308. CreateTable(cache, item.Key, values);
  309. continue;
  310. case string s when s.StartsWith("Stocker"):
  311. Stocker.Add(item.Key, values);
  312. continue;
  313. case string s when s.StartsWith("LP"):
  314. LoadPort.Add(item.Key, values);
  315. continue;
  316. //case string s when s.StartsWith("FIMS"):
  317. // FIMS.Add(item.Key, values);
  318. // continue;
  319. case "System":
  320. if (values is not IDictionary<string, object> systems)
  321. continue;
  322. foreach (var system in systems)
  323. {
  324. switch (system.Key)
  325. {
  326. case string s when s.StartsWith("Heater"):
  327. Heater.Add(system.Key, system.Value);
  328. continue;
  329. case string s when s.StartsWith("AlarmSignalHeater"):
  330. alarmCollection.Add(system.Key, ((IDictionary<string, object>)system.Value)["Value"] ??= false);
  331. continue;
  332. default:
  333. systemCollection.Add(system.Key, system.Value);
  334. break;
  335. }
  336. }
  337. continue;
  338. default:
  339. break;
  340. }
  341. //CreateTable(cache, "FIMS", FIMS);
  342. CreateTable(cache, "Heater", Heater);
  343. CreateTable(cache, "LoadPort", LoadPort);
  344. CreateTable(cache, "Stocker", Stocker);
  345. CreateTable(cache, "System", systemCollection);
  346. CreateTable(cache, "AlarmSignalHeater", alarmCollection);
  347. }
  348. }
  349. private bool CreateTable(IDictionary<string, object> cache, string key, object value)
  350. {
  351. return cache.TryAdd(key, value);
  352. }
  353. [RelayCommand]
  354. private void RemoveLeft(string key)
  355. {
  356. this.Left.TryRemove(key, out object? line);
  357. if (line is LineType lineType)
  358. lineType.IsEnable = true;
  359. }
  360. [RelayCommand]
  361. private void RemoveRight(string key)
  362. {
  363. this.Right.TryRemove(key, out object? line);
  364. if (line is LineType lineType)
  365. lineType.IsEnable = true;
  366. }
  367. [RelayCommand]
  368. private void Clear(string para)
  369. {
  370. switch (para)
  371. {
  372. case "L":
  373. this.Left.Foreach(t => ((LineType)t.Value).IsEnable = true);
  374. this.Left.Clear();
  375. break;
  376. case "R":
  377. this.Right.Foreach(t => ((LineType)t.Value).IsEnable = true);
  378. this.Right.Clear();
  379. break;
  380. default:
  381. this.Right.Foreach(t => ((LineType)t.Value).IsEnable = true);
  382. this.Right.Clear();
  383. this.Left.Foreach(t => ((LineType)t.Value).IsEnable = true);
  384. this.Left.Clear();
  385. break;
  386. }
  387. }
  388. [RelayCommand]
  389. private void GeneratePlot()
  390. {
  391. if (this.sqlSugarCustom.Client is null)
  392. return;
  393. if (!this.RecipeSteps.Any(t => t.Value.IsSelected))
  394. return;
  395. DateTime start = this.RecipeSteps.Where(t => t.Value.IsSelected).First().Key;
  396. DateTime end = this.RecipeSteps.Where(t => t.Value.IsSelected).Last().Key;
  397. var whereFunc = ObjectFuncModel.Create("Format", "time", ">", "{long}:" + $"{start.Ticks}", "&&", "time", "<", "{long}:" + $"{end.Ticks}");
  398. var f = sqlSugarCustom.Client.Queryable<dynamic>().AS($"\"{start:yyyyMMdd}.PM1\"").Where(whereFunc).ToList();
  399. var system = sqlSugarCustom.Client.Queryable<dynamic>().AS($"\"{start:yyyyMMdd}.System\"").Where(whereFunc).ToList();
  400. var alarmwhereFunc = ObjectFuncModel.Create("Format", "time", ">", "{long}:" + $"{start.Ticks}", "&&", "time", "<", "{long}:" + $"{end.Ticks}");
  401. var alarm = sqlSugarCustom.Client.Queryable<EventData>().AS($"event_data").Where(t => t.Occur_Time >= start && t.Occur_Time <= end && (t.Level == "Alarm" || t.Level == "Warning")).ToList();
  402. f.AddRange(system);
  403. Dictionary<string, List<float>> cacheLeft = [];
  404. Dictionary<string, List<float>> cacheRight = [];
  405. List<DateTime> time = [];
  406. foreach (var item in f)
  407. {
  408. if (item is not IDictionary<string, object> data)
  409. continue;
  410. time.Add(new DateTime((long)data["time"]));
  411. foreach (string key in Left.Keys)
  412. {
  413. if (!data.TryGetValue(key, out object? value) || value is null)
  414. continue;
  415. if (!cacheLeft.TryGetValue(key, out List<float>? collections) || collections is null)
  416. {
  417. collections = [];
  418. cacheLeft[key] = collections;
  419. }
  420. collections.Add((float)value);
  421. }
  422. foreach (string key in Right.Keys)
  423. {
  424. if (!data.TryGetValue(key, out object? value) || value is null)
  425. continue;
  426. if (!cacheRight.TryGetValue(key, out List<float>? collections) || collections is null)
  427. {
  428. collections = [];
  429. cacheRight[key] = collections;
  430. }
  431. collections.Add((float)value);
  432. }
  433. }
  434. this.PlotControl.Plot.Clear();
  435. foreach (var item in cacheLeft)
  436. this.SetLeftLine(time, item.Value, ((LineType)Left[item.Key])!.LinePattern, MarkerStyle.None, 2f, ((LineType)Left[item.Key])!.HexRGB);
  437. foreach (var item in cacheRight)
  438. this.SetRightLine(time, item.Value, ((LineType)Right[item.Key])!.LinePattern, MarkerStyle.None, 2f, ((LineType)Right[item.Key])!.HexRGB);
  439. foreach (var item in alarm)
  440. {
  441. switch (item.Level)
  442. {
  443. case "Alarm":
  444. this.SetAlarmLine(item.Occur_Time, $"{item.Source}");
  445. break;
  446. case "Warning":
  447. this.SetWarningLine(item.Occur_Time, $"{item.Source}");
  448. break;
  449. default:
  450. break;
  451. }
  452. }
  453. this.PlotControl.Plot.Axes.Bottom.TickLabelStyle.OffsetY = 0;
  454. this.PlotControl.Plot.Axes.AutoScale();
  455. this.PlotControl.Plot.Axes.Zoom(1.085, 1);
  456. this.PlotControl.Background = (Brush)App.Current.Resources.FindName("BackgroundColor");
  457. PlotControl.Refresh();
  458. }
  459. private void SetLeftLine(List<DateTime> time, List<float> value, LinePattern linePattern, MarkerStyle markerStyle, float lineWidth, string color)
  460. {
  461. Scatter mainScatter = this.PlotControl.Plot.Add.Scatter(time, value);
  462. mainScatter.MarkerStyle = markerStyle;
  463. mainScatter.LineWidth = lineWidth;
  464. mainScatter.LinePattern = linePattern;
  465. mainScatter.Color = new ScottPlot.Color(color);
  466. mainScatter.Axes.YAxis = this.PlotControl.Plot.Axes.Left;
  467. }
  468. private void SetRightLine(List<DateTime> time, List<float> value, LinePattern linePattern, MarkerStyle markerStyle, float lineWidth, string color)
  469. {
  470. Scatter mainScatter = this.PlotControl.Plot.Add.Scatter(time, value);
  471. mainScatter.MarkerStyle = markerStyle;
  472. mainScatter.LineWidth = lineWidth;
  473. mainScatter.LinePattern = linePattern;
  474. mainScatter.Color = new ScottPlot.Color(color);
  475. mainScatter.Axes.YAxis = this.PlotControl.Plot.Axes.Right;
  476. }
  477. private void SetAlarmLine(DateTime dateTime, string text)
  478. {
  479. var line = this.PlotControl.Plot.Add.VerticalLine(dateTime.ToOADate());
  480. line.Color = new ScottPlot.Color("DC143C");
  481. line.Text = text;
  482. line.LineWidth = 0.4f;
  483. line.LabelOppositeAxis = true;
  484. }
  485. private void SetWarningLine(DateTime dateTime, string text)
  486. {
  487. var line = this.PlotControl.Plot.Add.VerticalLine(dateTime.ToOADate());
  488. line.Color = new ScottPlot.Color("FFA500");
  489. line.LineWidth = 0.4f;
  490. line.Text = text;
  491. line.LabelOppositeAxis = true;
  492. }
  493. [RelayCommand]
  494. private void ReScale(string para)
  495. {
  496. switch (para)
  497. {
  498. case "+":
  499. this.PlotControl.Plot.Axes.Zoom(1.25, 1);
  500. break;
  501. case "-":
  502. this.PlotControl.Plot.Axes.Zoom(0.8, 1);
  503. break;
  504. case "add":
  505. this.PlotControl.Plot.Axes.Zoom(1, 1.25);
  506. break;
  507. case "minus":
  508. this.PlotControl.Plot.Axes.Zoom(1, 0.8);
  509. break;
  510. default:
  511. this.PlotControl.Plot.Axes.AutoScale();
  512. this.PlotControl.Plot.Axes.Zoom(1.085, 1);
  513. break;
  514. }
  515. this.PlotControl.Refresh();
  516. }
  517. }
  518. public partial class SelectRecipeStep : ObservableObject
  519. {
  520. [ObservableProperty]
  521. private bool _IsSelected;
  522. public string? Guid { get; set; }
  523. public DateTime Step_Begin_Time { get; set; }
  524. public DateTime Step_End_Time { get; set; }
  525. public string? Step_Name { get; set; }
  526. public float Step_Time { get; set; }
  527. public int Step_Number { get; set; }
  528. public string? Sub_Recipe_Step_Time { get; set; }
  529. public string? Sub_Recipe_Step_Number { get; set; }
  530. public string? Sub_Recipe_Step_Name { get; set; }
  531. public string? Sub_Recipe_Loop_Info { get; set; }
  532. public string? Temp_correction { get; set; }
  533. public string? Temp_pid { get; set; }
  534. }
  535. public class DBProcessData<T_Hierarchy>(char spilter, int skip)
  536. where T_Hierarchy : IDictionary<string, object>, new()
  537. {
  538. public bool ToDictionary(IDictionary<string, object> input, out T_Hierarchy? output)
  539. {
  540. output = default;
  541. if (input is null)
  542. return false;
  543. T_Hierarchy cache = [];
  544. foreach (KeyValuePair<string, object> rawData in input)
  545. {
  546. Span<string> source = rawData.Key.Split(spilter).AsSpan()[skip..];
  547. ColumAnalizer(cache, source, rawData.Key);
  548. }
  549. output = cache;
  550. return true;
  551. }
  552. private static void ColumAnalizer(/*ref*/ T_Hierarchy cache, Span<string> seprated, object value)
  553. {
  554. if (seprated.Length <= 1)
  555. {
  556. cache[seprated[0]] = value;
  557. return;
  558. }
  559. if (!cache.TryGetValue(seprated[0], out object? output) || output is not T_Hierarchy hierarchy)
  560. {
  561. hierarchy = [];
  562. cache[seprated[0]] = hierarchy;
  563. }
  564. cache = hierarchy;
  565. ColumAnalizer(cache, seprated[1..], value);
  566. }
  567. }
  568. public class GeneralProcessData(int skip = 0) : DBProcessData<Dictionary<string, object>>('.', skip)
  569. {
  570. }