using Mapster; using ProximaAnalizer.Helpers; using ScottPlot; using ScottPlot.WPF; using SqlSugar; using System.Collections.ObjectModel; using System.Windows; using Universal; namespace ProximaAnalizer.ViewModels; internal partial class DBInfoTraceViewModel : ObservableObject { public DBInfoTraceViewModel( TraceData traceData, DBDataHelper dBDataHelper, IEventAggregator eventAggregator, IDialogService dialogService) { this._DBDataHelper = dBDataHelper; this._traceData = traceData; this._dialogService = dialogService; this.Left = []; this.Right = []; this.PlotControl = new(); this._DisplayDataHelper = new(Left, Right); this._PlotHelper = new(this.PlotControl); this._PlotHelper.InitPlot(); eventAggregator.GetEvent().Subscribe(InitDisplayData); } private readonly TraceData _traceData; private readonly IDialogService _dialogService; private readonly PlotHepler _PlotHelper; private readonly DBDataHelper _DBDataHelper; private readonly DisplayDataHelper _DisplayDataHelper; [ObservableProperty] private WpfPlot _PlotControl; [ObservableProperty] private bool _DisplayAlarm = false; [ObservableProperty] private bool _DisplayWarning = false; [ObservableProperty] private double _AlarmInverval = 60d; private void InitDisplayData() { if (_traceData.ProcessData is null) return; this._HierachyCache.Clear(); this.RecipeSteps.Clear(); this.Clear("Both"); if (!this._DBDataHelper.GetRecipeSteps(_traceData.ProcessData.Guid, out string reason, out List? recipeSteps) || recipeSteps is null) { MessageBox.Show(reason, "Warning", MessageBoxButton.OK, MessageBoxImage.Error); return; } recipeSteps.ForEach(t => this.RecipeSteps.TryAdd(t.Step_Begin_Time, t.Adapt())); if (!this._DBDataHelper.GetFirstData(_traceData.ProcessData.Process_Begin_Time, out string error, out Dictionary? firstData) || firstData is null) { MessageBox.Show(error, "Warning", MessageBoxButton.OK, MessageBoxImage.Error); return; } this.Hierachy = firstData; this._HierachyCache.Push(firstData); this.Alarms.Clear(); } #region Step Operations [ObservableProperty] private ObservableDictionary _RecipeSteps = []; private bool AutoGenerated = false; [RelayCommand] private void Selected(SelectRecipeStep step) { if (RecipeSteps is null) return; if (AutoGenerated) { UnSelected(step); return; } if (RecipeSteps.Where(t => t.Value.IsSelected).Count() != 2) return; DateTime start = RecipeSteps.Where(t => t.Value.IsSelected).First().Key; DateTime end = RecipeSteps.Where(t => t.Value.IsSelected).Last().Key; RecipeSteps.Where(t => t.Key >= start && t.Key <= end).Foreach(t => t.Value.IsSelected = true); this.AutoGenerated = true; } [RelayCommand] private void UnSelected(SelectRecipeStep step) { RecipeSteps.Where(t => t.Value.IsSelected).Foreach(t => t.Value.IsSelected = false); this.AutoGenerated = false; } #endregion #region Data Hierachy Operations [ObservableProperty] private IDictionary? _Hierachy; private readonly Stack> _HierachyCache = new(); [RelayCommand] private void SelectHierachy(KeyValuePair item) { if (this.Hierachy is null) return; if (item.Value is IDictionary next) { this._HierachyCache.Push(this.Hierachy); this.Hierachy = next; return; } IDialogParameters para = new DialogParameters() { { "Line", item.Value } }; _dialogService.Show("LinePicker", para, AddLineCallback); } [RelayCommand] private void Return() { if (this._HierachyCache.TryPop(out var output) && output is not null) this.Hierachy = output; } #endregion #region Line Operations [ObservableProperty] private ObservableDictionary _Left = []; [ObservableProperty] private ObservableDictionary _Right = []; [RelayCommand] private void EditLine(KeyValuePair item) { IDialogParameters para = new DialogParameters() { { "Line", item.Key } }; _dialogService.Show("LinePicker", para, AddLineCallback); } private void AddLineCallback(IDialogResult dialogResult) { dialogResult.Parameters.TryGetValue("Line", out LineType? lineType); dialogResult.Parameters.TryGetValue("Name", out string? line); dialogResult.Parameters.TryGetValue("Axis", out string? Axis); if (lineType is null || string.IsNullOrEmpty(line) || string.IsNullOrEmpty(Axis)) return; ObservableDictionary? lineCollection = Axis switch { "L" => Left, "R" => Right, _ => default }; if (lineCollection is null) return; if (lineCollection.TryGetValue(line, out object? oldType) && oldType is LineType oldLine) oldLine.IsEnable = true; lineCollection[line] = lineType; } [RelayCommand] private void RemoveLeft(string key) { this.Left.TryRemove(key, out object? line); if (line is LineType lineType) lineType.IsEnable = true; } [RelayCommand] private void RemoveRight(string key) { this.Right.TryRemove(key, out object? line); if (line is LineType lineType) lineType.IsEnable = true; } [RelayCommand] private void Clear(string para) { switch (para) { case "L": this.Left.Foreach(t => ((LineType)t.Value).IsEnable = true); this.Left.Clear(); break; case "R": this.Right.Foreach(t => ((LineType)t.Value).IsEnable = true); this.Right.Clear(); break; default: this.Right.Foreach(t => ((LineType)t.Value).IsEnable = true); this.Right.Clear(); this.Left.Foreach(t => ((LineType)t.Value).IsEnable = true); this.Left.Clear(); break; } } #endregion #region Plot Operations [ObservableProperty] private ObservableCollection _Alarms = []; [RelayCommand] private void GeneratePlot() { if (!this.RecipeSteps.Any(t => t.Value.IsSelected)) { MessageBox.Show("Recipe Step 未选择", "Warning", MessageBoxButton.OK, MessageBoxImage.Error); return; } if (this.Left.Count == 0 && Right.Count == 0) { MessageBox.Show("分析数据 未选择", "Warning", MessageBoxButton.OK, MessageBoxImage.Error); return; } DateTime startTime = this.RecipeSteps.Where(t => t.Value.IsSelected).First().Key; DateTime endTime = this.RecipeSteps.Where(t => t.Value.IsSelected).Last().Key; this._DBDataHelper.SetTimeRange(startTime, endTime); this.GetDBFields(out HashSet pmFields, out HashSet systemFields); if (!this._DBDataHelper.GetPMData(pmFields, out List? mainData) || mainData is null) return; this._DBDataHelper.GetSystemData(systemFields, out List? subData); this._DisplayDataHelper.ClearData(); this._DisplayDataHelper.CreateData(mainData, subData); this.PlotControl.Plot.Clear(); foreach (var item in this._DisplayDataHelper.DataLeft) this._PlotHelper.AddLeftLine(this._DisplayDataHelper.Time, item.Value, ((LineType)Left[item.Key])!.LinePattern, MarkerStyle.None, 1.5f, ((LineType)Left[item.Key])!.HexRGB); foreach (var item in this._DisplayDataHelper.DataRight) this._PlotHelper.AddRightLine(this._DisplayDataHelper.Time, item.Value, ((LineType)Right[item.Key])!.LinePattern, MarkerStyle.None, 1.5f, ((LineType)Right[item.Key])!.HexRGB); if (!this._DBDataHelper.GetAlarmData(out List? alarm) || alarm is null) return; this.GenerateAlarm(alarm); this.PlotControl.Plot.Axes.AutoScale(); this.PlotControl.Refresh(); } public void GenerateAlarm(List alarm) { this.Alarms.Clear(); this.Alarms.AddRange(alarm); Dictionary intervalFilter = []; HashSet dup = []; foreach (EventData item in alarm) { if (string.IsNullOrEmpty(item.Description)) continue; if (intervalFilter.TryGetValue(item.Description, out DateTime time) && (item.Occur_Time - time).TotalSeconds < AlarmInverval) continue; intervalFilter[item.Description] = item.Occur_Time; switch (item.Level) { case "Alarm": if (!this.DisplayAlarm) continue; if (!dup.Add(item.Occur_Time)) continue; this._PlotHelper.AddAlarmLine(item.Occur_Time, $"{item.Source}"); break; case "Warning": if (!this.DisplayWarning) continue; if (!dup.Add(item.Occur_Time)) continue; this._PlotHelper.AddWarningLine(item.Occur_Time, $"{item.Source}"); break; default: break; } } } private void GetDBFields(out HashSet pmFields, out HashSet systemFields) { pmFields = ["time"]; systemFields = []; foreach (string field in this.Left.Keys) { _ = field switch { string s when s.Contains("PM1") => pmFields.Add(field), string s when s.Contains("System") => systemFields.Add(field), _ => systemFields.Add(field) }; } foreach (string field in this.Right.Keys) { _ = field switch { string s when s.Contains("PM1") => pmFields.Add(field), string s when s.Contains("System") => systemFields.Add(field), _ => systemFields.Add(field) }; } } [RelayCommand] private void ViewTraceData() { IDialogParameters para = new DialogParameters { { "Data", this._DisplayDataHelper}, }; this._dialogService.Show("TraceData", para, null); } [RelayCommand] private void ReScale(string para) { switch (para) { case "+": this.PlotControl.Plot.Axes.Zoom(1.25, 1); break; case "-": this.PlotControl.Plot.Axes.Zoom(0.8, 1); break; case "add": this.PlotControl.Plot.Axes.Zoom(1, 1.25); break; case "minus": this.PlotControl.Plot.Axes.Zoom(1, 0.8); break; default: this.PlotControl.Plot.Axes.AutoScale(); this.PlotControl.Plot.Axes.Zoom(1.085, 1); break; } this.PlotControl.Refresh(); } #endregion } public partial class SelectRecipeStep : ObservableObject { [ObservableProperty] private bool _IsSelected; public string? Guid { get; set; } public DateTime Step_Begin_Time { get; set; } public DateTime Step_End_Time { get; set; } public string? Step_Name { get; set; } public float Step_Time { get; set; } public int Step_Number { get; set; } public string? Sub_Recipe_Step_Time { get; set; } public string? Sub_Recipe_Step_Number { get; set; } public string? Sub_Recipe_Step_Name { get; set; } public string? Sub_Recipe_Loop_Info { get; set; } public string? Temp_correction { get; set; } public string? Temp_pid { get; set; } }