| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560 | using ScottPlot;using ScottPlot.WPF;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._eventAggregator = eventAggregator;        this.Left = [];        this.Right = [];        this.PlotControl = new();        this._DisplayDataHelper = new(Left, Right);        this._PlotHelper = new(this.PlotControl);        this._PlotHelper.InitPlot();        eventAggregator.GetEvent<RefreshRecipeData>().Subscribe(InitDisplayData);    }    private readonly TraceData _traceData;    private readonly IDialogService _dialogService;    private readonly IEventAggregator _eventAggregator;    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<RecipeStepData>? recipeSteps) || recipeSteps is null)        {            MessageBox.Show(reason, "Warning", MessageBoxButton.OK, MessageBoxImage.Warning);            return;        }        recipeSteps.ForEach(t => this.RecipeSteps.TryAdd(t.Step_Begin_Time, t.Adapt<SelectRecipeStep>()));        if (!this._DBDataHelper.GetFirstData(_traceData.ProcessData.Process_Begin_Time, out string error, out Dictionary<string, object>? firstData) || firstData is null)        {            MessageBox.Show(error, "Warning", MessageBoxButton.OK, MessageBoxImage.Warning);            return;        }        this.Hierachy = firstData;        this._HierachyCache.Push(firstData);        this.Alarms.Clear();    }    #region Step Operations    [ObservableProperty]    private ObservableDictionary<DateTime, SelectRecipeStep> _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<string, object>? _Hierachy;    private readonly Stack<IDictionary<string, object>> _HierachyCache = new();    [RelayCommand]    private void SelectHierachy(KeyValuePair<string, object> item)    {        if (this.Hierachy is null)            return;        if (item.Value is IDictionary<string, object> 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<string, LineType> _Left = [];    [ObservableProperty]    private ObservableDictionary<string, LineType> _Right = [];    [RelayCommand]    private void EditLine(KeyValuePair<string, LineType> 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;        lineType.IsEnable = false;        ObservableDictionary<string, LineType>? lineCollection = Axis switch        {            "L" => Left,            "R" => Right,            _ => default        };        if (lineCollection is null)            return;        if (lineCollection.TryGetValue(line, out LineType? oldType) && oldType is LineType oldLine)            oldLine.IsEnable = true;        lineCollection[line] = lineType;        switch (Axis)        {            case "L":                this.RemoveRight(line);                break;            case "R":                this.RemoveLeft(line);                break;            default:                break;        }    }    [RelayCommand]    private void RemoveLeft(string key)    {        this.Left.TryRemove(key, out LineType? line);        if (line is LineType lineType)            lineType.IsEnable = true;    }    [RelayCommand]    private void RemoveRight(string key)    {        this.Right.TryRemove(key, out LineType? line);        if (line is LineType lineType)            lineType.IsEnable = true;    }    [RelayCommand]    private void SaveLoad(string para)    {        switch (para)        {            case "Save":                {                    SaveFileDialog saveFileDialog = new()                    {                        Filter = "Line Picker |*.lpr;",                        FileName = "Default.lpr",                        InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.Desktop)                    };                    if (saveFileDialog.ShowDialog() != true)                        return;                    List<LineSave> left = [];                    foreach (var item in this.Left)                    {                        LineSave save = new()                        {                            Direction = "L",                            Name = item.Key,                            Dash = item.Value.DashArray,                            Color = item.Value.HexRGB                        };                        left.Add(save);                    }                    foreach (var item in this.Right)                    {                        LineSave save = new()                        {                            Direction = "R",                            Name = item.Key,                            Dash = item.Value.DashArray,                            Color = item.Value.HexRGB                        };                        left.Add(save);                    }                    try                    {                        using StreamWriter sw = new(saveFileDialog.FileName);                        sw.WriteLine(JsonSerializer.Serialize(left));                        MessageBox.Show("文件保存成功", "Save Line Picker File", MessageBoxButton.OK, MessageBoxImage.Information);                    }                    catch                    {                        MessageBox.Show("文件保存失败", "Save Line Picker File", MessageBoxButton.OK, MessageBoxImage.Error);                    }                    return;                }            case "Load":                {                    OpenFileDialog open = new()                    {                        Filter = "Line Picker |*.lpr;",                        FileName = "Default.lpr",                        InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.Desktop)                    };                    if (open.ShowDialog() != true)                        return;                    try                    {                        using StreamReader sr = new(open.FileName);                        string? left = sr.ReadToEnd();                        if (string.IsNullOrEmpty(left))                        {                            MessageBox.Show("文件读取失败", "Save Line Picker File", MessageBoxButton.OK, MessageBoxImage.Error);                            return;                        }                        if (JsonSerializer.Deserialize<List<LineSave>>(left) is not List<LineSave> lines)                        {                            MessageBox.Show("文件读取失败", "Save Line Picker File", MessageBoxButton.OK, MessageBoxImage.Error);                            return;                        }                        this.Left.Clear();                        this.Right.Clear();                        foreach (var line in lines)                        {                            if (string.IsNullOrEmpty(line.Name) || string.IsNullOrEmpty(line.Color) || line.Dash is null)                                continue;                            DialogResult result = new();                            result.Parameters.Add("Line", new LineType(line.Color, line.Dash));                            result.Parameters.Add("Name", line.Name);                            result.Parameters.Add("Axis", line.Direction!);                            this.AddLineCallback(result);                        }                    }                    catch                    {                        MessageBox.Show("文件读取失败", "Save Line Picker File", MessageBoxButton.OK, MessageBoxImage.Error);                    }                    return;                }            default:                break;        }    }    [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<EventData> _Alarms = [];    [RelayCommand]    private void GeneratePlot()    {        if (!this.RecipeSteps.Any(t => t.Value.IsSelected))        {            MessageBox.Show("Recipe Step 未选择", "Warning", MessageBoxButton.OK, MessageBoxImage.Warning);            return;        }        if (this.Left.Count == 0 && Right.Count == 0)        {            MessageBox.Show("分析数据 未选择", "Warning", MessageBoxButton.OK, MessageBoxImage.Warning);            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<string> pmFields, out HashSet<string> systemFields);        if (!this._DBDataHelper.GetPMData(pmFields, out List<dynamic>? mainData) || mainData is null)            return;        this._DBDataHelper.GetSystemData(systemFields, out List<dynamic>? 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<EventData>? alarm) && alarm is not null)            this.GenerateAlarm(alarm);        this.PlotControl.Plot.Axes.AutoScale();        this.PlotControl.Refresh();    }    private void GenerateAlarm(List<EventData> alarm)    {        this.Alarms.Clear();        this.Alarms.AddRange(alarm);        Dictionary<string, DateTime> intervalFilter = [];        HashSet<DateTime> dup = [];        foreach (EventData item in alarm)        {            if (string.IsNullOrEmpty(item.Event_Enum))                continue;            if (intervalFilter.TryGetValue(item.Event_Enum, out DateTime time) &&                (item.Occur_Time - time).TotalSeconds < AlarmInverval)                continue;            intervalFilter[item.Event_Enum] = 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<string> pmFields, out HashSet<string> 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},            {"Alarm", this.Alarms }        };        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    [RelayCommand]    private void AlarmDetail(object para)    {        if (para is not EventData eventData)            return;        _eventAggregator.GetEvent<WindowSwitch>().Page = Pages.HistoryData;        _eventAggregator.GetEvent<WindowSwitch>().Publish();        RefreshAlarmData alarmData = _eventAggregator.GetEvent<RefreshAlarmData>();        alarmData.Selected = eventData;        alarmData.FromPage = Pages.MainWindow;        alarmData.Publish();    }    [RelayCommand]    private void ReturnPage()    {        _eventAggregator.GetEvent<WindowSwitch>().Page = Pages.RecipeStepNavi;        _eventAggregator.GetEvent<WindowSwitch>().Publish();    }}public class LineSave{    public string? Direction { get; set; }    public string? Name { get; set; }    public string? Color { get; set; }    public DoubleCollection? Dash { get; set; }}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; }}
 |