using SciChart.Charting.Model.ChartSeries; using SciChart.Charting.Visuals.Annotations; using SciChart.Charting.Visuals.RenderableSeries; using SciChart.Data.Model; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Media; using Tango.BL.Entities; using Tango.BL.Enumerations; using Tango.Core.Commands; using Tango.CSV; using Tango.FSE.Common; using Tango.FSE.Common.Dialogs; using Tango.FSE.Common.Insights; using Tango.FSE.Insights.Config; using Tango.FSE.Insights.Contracts; using Tango.FSE.Insights.Dialogs; using Tango.FSE.Insights.ML; using Tango.FSE.Insights.Navigation; using Tango.FSE.Insights.SciChart; using Tango.Logging; using Tango.PMR.Insights; using Tango.SharedUI.Components; namespace Tango.FSE.Insights.ViewModels { public class InsightsViewVM : FSEViewModelWithModuleSettings { private const int MAX_VISIBLE_CHARTS = 8; private const int MAX_DAYS = 30; private bool _chartsLoaded; private ICollectionView _monitorsView; private List _lastSelectedForDetection; private List _defaultChartForAnomalies = new List() { //TechMonitors.IncomingVoltage, //TechMonitors.ChillerTemperature, //TechMonitors.OverallTemperature, //TechMonitors.GasSensor, //TechMonitors.BlowerVoltage, //TechMonitors.HeadZone1Temperature }; #region Properties public RelayCommand GetInsightsCommand { get; set; } private ObservableCollection _charts; public ObservableCollection Charts { get { return _charts; } set { _charts = value; RaisePropertyChangedAuto(); } } public ObservableCollection VisibleCharts { get; set; } private List _anomaliesDetectionProgresses; public List AnomaliesDetectionProgresses { get { return _anomaliesDetectionProgresses; } set { _anomaliesDetectionProgresses = value; RaisePropertyChangedAuto(); } } private DateTime _minDate; public DateTime MinDate { get { return _minDate; } set { _minDate = value; RaisePropertyChangedAuto(); } } private DateTime _maxDate; public DateTime MaxDate { get { return _maxDate; } set { _maxDate = value; RaisePropertyChangedAuto(); } } private DateTime? _startDate; public DateTime? StartDate { get { return _startDate; } set { _startDate = value; RaisePropertyChangedAuto(); } } private DateTime? _startTime; public DateTime? StartTime { get { return _startTime; } set { _startTime = value; RaisePropertyChangedAuto(); } } private DateTime? _endDate; public DateTime? EndDate { get { return _endDate; } set { _endDate = value; RaisePropertyChangedAuto(); } } private DateTime? _endTime; public DateTime? EndTime { get { return _endTime; } set { _endTime = value; RaisePropertyChangedAuto(); } } private InsightsHandler _handler; public InsightsHandler Handler { get { return _handler; } set { _handler = value; RaisePropertyChangedAuto(); } } private IRange _visibleChartsRange; public IRange VisibleChartsRange { get { return _visibleChartsRange; } set { _visibleChartsRange = value; RaisePropertyChangedAuto(); } } private String _monitorsFilter; public String MonitorsFilter { get { return _monitorsFilter; } set { _monitorsFilter = value; RaisePropertyChangedAuto(); OnMonitorsFilterChanged(); } } private InsightsPackage _insightsPackage; public InsightsPackage InsightsPackage { get { return _insightsPackage; } set { _insightsPackage = value; RaisePropertyChangedAuto(); RaisePropertyChanged(nameof(InsightsPackageAvailable)); InvalidateRelayCommands(); } } public bool InsightsPackageAvailable { get { return InsightsPackage != null; } } private bool _displayAnnotations; public bool DisplayAnnotations { get { return _displayAnnotations; } set { _displayAnnotations = value; RaisePropertyChangedAuto(); } } private bool _displayAnomalies; public bool DisplayAnomalies { get { return _displayAnomalies; } set { _displayAnomalies = value; RaisePropertyChangedAuto(); } } private bool _isSearchBarOpened; public bool IsSearchBarOpened { get { return _isSearchBarOpened; } set { _isSearchBarOpened = value; RaisePropertyChangedAuto(); } } private bool _isSettingsBarOpened; public bool IsSettingsBarOpened { get { return _isSettingsBarOpened; } set { _isSettingsBarOpened = value; RaisePropertyChangedAuto(); OnIsSettingsBarOpenedChanged(); } } private bool _isDetecting; public bool IsDetecting { get { return _isDetecting; } set { _isDetecting = value; RaisePropertyChangedAuto(); } } public AnnotationCollection Annotations { get; set; } private AnnotationsConfiguration _annotationsConfiguration; public AnnotationsConfiguration AnnotationsConfiguration { get { return _annotationsConfiguration; } set { _annotationsConfiguration = value; RaisePropertyChangedAuto(); OnAnnotationsConfigurationChanged(); } } private SelectedObjectCollection _selectedEventsCategories; public SelectedObjectCollection SelectedEventsCategories { get { return _selectedEventsCategories; } set { _selectedEventsCategories = value; RaisePropertyChangedAuto(); } } private SelectedObjectCollection _selectedEventsGroups; public SelectedObjectCollection SelectedEventsGroups { get { return _selectedEventsGroups; } set { _selectedEventsGroups = value; RaisePropertyChangedAuto(); } } #endregion #region Commands public RelayCommand PeekApplicationLogsCommand { get; set; } public RelayCommand PeekFirmwareLogsCommand { get; set; } public RelayCommand OpenBugReportCommand { get; set; } public RelayCommand ViewApplicationExceptionCommand { get; set; } public RelayCommand AnalyzeCommand { get; set; } public RelayCommand ExportToCsvCommand { get; set; } #endregion #region Constructors public InsightsViewVM() { MaxDate = DateTime.Now; IsSearchBarOpened = true; DisplayAnnotations = false; DisplayAnomalies = false; Charts = new ObservableCollection(); VisibleCharts = new ObservableCollection(); Annotations = new AnnotationCollection(); AnnotationsConfiguration = AnnotationsConfiguration.CreateDefault(); GetInsightsCommand = new RelayCommand(GetInsights, () => IsFree); PeekApplicationLogsCommand = new RelayCommand(PeekAnnotationApplicationLogs); PeekFirmwareLogsCommand = new RelayCommand(PeekAnnotationFirmwareLogs); OpenBugReportCommand = new RelayCommand(OpenAnnotationBugReport); ViewApplicationExceptionCommand = new RelayCommand(ViewAnnotationApplicationException); AnalyzeCommand = new RelayCommand(Analyze, () => InsightsPackage != null); ExportToCsvCommand = new RelayCommand(ExportToCsv, () => InsightsPackage != null); } #endregion #region Application Started & Ready public override void OnApplicationStarted() { base.OnApplicationStarted(); if (!ModuleSettings.EnableAnyStartDate) { MinDate = DateTime.Now.AddDays(-MAX_DAYS); } StartDate = DateTime.Now.AddDays(-ModuleSettings.DefaultDiffDays); StartTime = new DateTime(1, 1, 1, 0, 0, 0); EndDate = null; EndTime = null; DisplayAnnotations = ModuleSettings.DisplayAnnotations; AnnotationsConfiguration = ModuleSettings.AnnotationsConfiguration; InitEventsSelection(); MachineProvider.MachineConnected += MachineProvider_MachineConnected; } public async override void OnApplicationReady() { base.OnApplicationReady(); if (!_chartsLoaded) { try { var monitors = (await Services.TechComponentsService.Monitors.FindAll()).OrderByAlphaNumeric(x => x.Description).ToList(); foreach (var monitor in monitors.Where(x => !x.MultiChannel).ToList()) { InsightsChart chart = new InsightsChart(VisibleCharts); chart.Name = monitor.Name; chart.Description = monitor.Description; chart.TechMonitor = (TechMonitors)monitor.Code; chart.InsightsMonitorsProperty = typeof(InsightsMonitors).GetProperty(((TechMonitors)monitor.Code).ToString(), BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly); chart.IsVisibleChanged += Chart_IsVisibleChanged; chart.DataSeries.Append(GetDemoDates(), GetDemoValues()); Charts.Add(chart); } foreach (var chart in Charts.ToList().Where(x => x.InsightsMonitorsProperty == null)) { Charts.Remove(chart); LogManager.Log($"Error loading insights chart '{chart.Name}'. Could not find property on InsightsMonitors.", LogCategory.Error); } Charts.Where(x => ModuleSettings.VisibleMonitors.Contains(x.TechMonitor)).ToList().ForEach(x => x.IsVisible = true); InitMonitorsView(); _chartsLoaded = true; } catch (Exception ex) { LogManager.Log(ex, "Error initializing insights module."); NotificationProvider.PushErrorReportingSnackbar(ex, "Insights Module Error", "Error initializing insights module."); } } } public override void OnApplicationShuttingDown() { base.OnApplicationShuttingDown(); SaveSettings(); } #endregion #region Navigation public override void OnNavigatedTo() { base.OnNavigatedTo(); MaxDate = DateTime.Now; } #endregion #region Insights private async void GetInsights() { if (StartDate == null) { await NotificationProvider.ShowError("Please specify start date before trying to get insights."); return; } var range = GetInsightsRange(); if (range.Start >= range.End) { await NotificationProvider.ShowError("Selected end date must be greater than the start date."); return; } LogManager.Log($"Getting insights from '{range.Start}' to '{range.End}'"); try { IsSearchBarOpened = false; using (var task = NotificationProvider.PushTaskItem("Getting insights from the remote machine...", false, () => { try { Handler?.Abort(); } catch (Exception ex) { LogManager.Log(ex, "Error aborting insights request."); } })) { IsFree = false; Handler = await InsightsProvider.GetInsights(range.Start.ToUniversalTime(), range.End.ToUniversalTime()); Handler.ProgressChanged += (x, e) => { task.UpdateProgress(e.Progress.Message, e.Progress.Value, e.Progress.Maximum, e.Progress.IsIndeterminate); }; task.CanCancel = true; var package = await Handler.WaitForCompletion(); task.CanCancel = false; await LoadInsightsPackage(package); } } catch (ThreadAbortException) { IsSearchBarOpened = true; //Operation canceled by cancel button.. } catch (OperationCanceledException) { IsSearchBarOpened = true; //Aborted by user... } catch (Exception ex) { IsSearchBarOpened = true; LogManager.Log(ex, "Error getting insights."); await NotificationProvider.ShowError($"Error occurred while trying to get insights.\n{ex.FlattenMessage()}"); NotificationProvider.PushErrorReportingSnackbar(ex, "Insights Module Error", "Error occurred while trying to retrieve the remote machine insights."); } finally { IsFree = true; } } private async Task LoadInsightsPackage(InsightsPackage package) { LogManager.Log($"Insights received with {package.Frames.Count} frames."); await Task.Factory.StartNew(() => { foreach (var chart in Charts) { chart.DataSeries.Clear(); List dates = new List(); List values = new List(); foreach (var frame in package.Frames) { var value = chart.InsightsMonitorsProperty.GetValue(frame.Monitors); dates.Add(frame.Frame.Time.ToLocalTime()); if (value != null) { double v = (double)value; values.Add(v); if (!double.IsNaN(v)) { chart.MonitorValues.Add(new MonitorValue() { Time = frame.Frame.Time, Value = (float)v }); } } else { values.Add(double.NaN); } } chart.DataSeries.Append(dates, values); } InvokeUI(() => { Annotations.Clear(); Charts.ToList().ForEach(x => x.Anomalies.Clear()); Charts.ToList().ForEach(x => x.Annotations.Clear()); //Load annotations PlotAnnotations(package); InsightsPackage = package; View.ZoomExtents(); SaveSettings(); }); }); } public void AddMachineConnectedDisconnectedAnnotation(DateTime date, bool connected) { VerticalLineAnnotation line = new VerticalLineAnnotation(); line.StrokeThickness = 2; line.Stroke = connected ? Brushes.Green : Brushes.DarkGray; line.X1 = date; line.X2 = date; line.IsHidden = !AnnotationsConfiguration.DisplayConnectionMarkers; //line.ShowLabel = false; //line.AnnotationLabels.Add(new AnnotationLabel() //{ // LabelPlacement = connected ? LabelPlacement.TopRight : LabelPlacement.BottomLeft, // Text = connected ? "machine connected" : "machine disconnected" //}); Annotations.Add(line); } private void PlotAnnotations(InsightsPackage package) { Annotations.Clear(); bool disconnected = false; int positiveFrameCounter = 0; foreach (var frame in package.Frames) { if (frame.Frame.IsEmpty) { if (!disconnected && positiveFrameCounter > 1) { disconnected = true; AddMachineConnectedDisconnectedAnnotation(frame.Frame.Time.ToLocalTime(), false); } positiveFrameCounter = 0; } else { positiveFrameCounter++; if (disconnected) { disconnected = false; AddMachineConnectedDisconnectedAnnotation(frame.Frame.Time.ToLocalTime(), true); } } } var y = 0.05; int jobCounter = 0; foreach (var insight in package.Events.Cast() .Concat(package.Statuses) .Concat(package.Bugs) .Concat(package.ApplicationExceptions) .Concat(package.Updates) .OrderBy(x => x.Time)) { CustomAnnotation annotation = new CustomAnnotation(); annotation.X1 = insight.Time.ToLocalTime(); annotation.Y1 = y; annotation.CoordinateMode = AnnotationCoordinateMode.RelativeY; if (insight is InsightsReadyEvent ev) { annotation.Content = ev; if (ev.EventType.Group == EventTypeGroups.Jobs) { Panel.SetZIndex(annotation, 5); switch (ev.EventType.Type) { case EventTypes.JOB_STARTED: ev.JobDescription = $"Job #{++jobCounter} Started"; break; case EventTypes.JOB_COMPLETED: ev.JobDescription = $"Job #{jobCounter} Completed"; break; case EventTypes.JOB_FAILED: ev.JobDescription = $"Job #{jobCounter} Failed"; break; case EventTypes.JOB_ABORTED: ev.JobDescription = $"Job #{jobCounter} Aborted"; break; } annotation.ContentTemplate = Application.Current.Resources["InsightEventJobsTemplate"] as DataTemplate; } else { annotation.ContentTemplate = Application.Current.Resources["InsightEventTemplate"] as DataTemplate; } } else if (insight is InsightsReadyStatus status) { Panel.SetZIndex(annotation, 1); annotation.Content = status; annotation.ContentTemplate = Application.Current.Resources["InsightStatusTemplate"] as DataTemplate; } else if (insight is InsightsReadyUpdate update) { Panel.SetZIndex(annotation, 2); annotation.Content = update; annotation.ContentTemplate = Application.Current.Resources["InsightUpdateTemplate"] as DataTemplate; } else if (insight is InsightsReadyBug bug) { Panel.SetZIndex(annotation, 3); annotation.Content = bug; annotation.ContentTemplate = Application.Current.Resources["InsightBugTemplate"] as DataTemplate; } else if (insight is InsightsReadyApplicationException appException) { annotation.Content = appException; Panel.SetZIndex(annotation, 4); if (appException.IsApplicationCrash) { annotation.ContentTemplate = Application.Current.Resources["InsightApplicationCrashTemplate"] as DataTemplate; } else { annotation.ContentTemplate = Application.Current.Resources["InsightApplicationExceptionTemplate"] as DataTemplate; } } Annotations.Add(annotation); y += 0.05; if (y > 0.95) { y = 0.05; } } } #endregion #region Menu Items Handling private async void PeekAnnotationFirmwareLogs(InsightReadyBase ev) { using (NotificationProvider.PushTaskItem("Peeking firmware logs...")) { await Task.Delay(1000); await NavigationManager.NavigateWithObject("FirmwareModule.MainView", new PeekLogsNavigationObject() { Time = ev.Time }); } } private async void PeekAnnotationApplicationLogs(InsightReadyBase ev) { using (NotificationProvider.PushTaskItem("Peeking application logs...")) { await Task.Delay(1000); await NavigationManager.NavigateWithObject("PPCConsoleModule.MainView", new PeekLogsNavigationObject() { Time = ev.Time }); } } private void OpenAnnotationBugReport(InsightsReadyBug bug) { Process.Start(bug.Bug.URL); } private async void ViewAnnotationApplicationException(InsightsReadyApplicationException appException) { MessageLogItem logItem = new MessageLogItem(); logItem.Category = LogCategory.Critical; logItem.TimeStamp = appException.Time; logItem.Message = appException.Exception; await NotificationProvider.ShowDialog(new ApplicationLogItemViewVM() { LogItem = logItem }); } #endregion #region Event Handlers private async void MachineProvider_MachineConnected(object sender, Common.Connection.MachineConnectedEventArgs e) { try { var date = await InsightsProvider.GetInsightsMinDate(); if (date != null) { var remoteMinDate = date.Value.ToLocalTime(); if (remoteMinDate > StartDate) { StartDate = remoteMinDate; } if (!ModuleSettings.EnableAnyStartDate) { MinDate = remoteMinDate; } } } catch (Exception ex) { LogManager.Log(ex, "Error retrieving minimum insights date from the remote machine."); } } private async void Chart_IsVisibleChanged(object sender, EventArgs e) { InsightsChart chart = sender as InsightsChart; if (chart.IsVisible) { if (VisibleCharts.Count >= MAX_VISIBLE_CHARTS) { await NotificationProvider.ShowInfo($"Maximum visible monitors is {MAX_VISIBLE_CHARTS}. Please close another monitor before adding a new one."); chart.IsVisible = false; return; } VisibleCharts.Add(chart); } else { VisibleCharts.Remove(chart); } } #endregion #region Analysis private async void Analyze() { var defaultCharts = Charts.Where(x => _defaultChartForAnomalies.Exists(y => y == x.TechMonitor)).ToList(); if (_lastSelectedForDetection != null) { defaultCharts = _lastSelectedForDetection.ToList(); } var vm = await NotificationProvider.ShowDialog(new AnomaliesDialogViewVM(Charts.ToList(), defaultCharts)); if (vm.DialogResult && vm.SelectedCharts.SynchedSource.Count > 0) { var snackbar = NotificationProvider.PushProgressSnackbar("Anomalies Detection", "Detecting insights anomalies..."); try { IsDetecting = true; IsSettingsBarOpened = false; var chartsToAnalyze = vm.SelectedCharts.SynchedSource.ToList(); _lastSelectedForDetection = chartsToAnalyze.ToList(); AnomaliesDetectionProgresses = chartsToAnalyze.Select(x => new AnomaliesDetectionProgress() { Chart = x, FramesCount = x.MonitorValues.Count }).ToList(); int counter = 0; await Task.Factory.StartNew(() => { Stopwatch watch = new Stopwatch(); watch.Start(); double totalCounter = 0; double totalFrames = chartsToAnalyze.Count * InsightsPackage.Frames.Count; Parallel.ForEach(AnomaliesDetectionProgresses, (progress) => { counter++; Debug.WriteLine($"Analyzing chart '{progress.Chart.Description}'..."); progress.Chart.DetectAnnomalies((index) => { progress.CurrentFrame = index; totalCounter++; //snackbar.Message = $"Detecting insights anomalies ({(int)(totalCounter / totalFrames * 100d)}%)..."; }); foreach (var a in progress.Chart.Anomalies) { Debug.WriteLine($"{progress.Chart.Name} => {a.ToString()}"); } }); watch.Stop(); Debug.WriteLine($"Spike detection completed in {(int)watch.Elapsed.TotalMinutes} minutes."); }); Charts.ToList().ForEach(x => x.Annotations.Clear()); foreach (var chart in chartsToAnalyze) { chart.DrawAnomalies(); } VisibleCharts.ToList().ForEach(x => x.IsVisible = false); chartsToAnalyze.ForEach(x => x.IsVisible = true); IsDetecting = false; snackbar.ProgressCompleted("Insights anomalies detection completed.\nTap to view the results.", IsVisible ? TimeSpan.FromSeconds(1) : default(TimeSpan?), () => { if (!IsVisible) { //Navigate to this view... } }); View.ZoomExtents(); DisplayAnomalies = true; } catch (Exception ex) { LogManager.Log(ex, "Error performing anomaly detection on insights."); snackbar.Close(); NotificationProvider.PushErrorReportingSnackbar(ex, "Insights Module Error", "Error occurred while trying to perform insights anomalies detection."); } finally { IsDetecting = false; IsSearchBarOpened = false; } } } #endregion #region Export CSV private async void ExportToCsv() { var vm = await NotificationProvider.ShowDialog(new ExportToCsvDialogViewVM(Charts.ToList(), VisibleCharts.ToList())); if (!vm.DialogResult) return; var snackbar = NotificationProvider.PushProgressSnackbar("Exporting insights", "Exporting insights to csv..."); try { await Task.Factory.StartNew(() => { List chartsToExport = vm.SelectedCharts.SynchedSource.ToList(); List> delegates = new List>(); foreach (var chart in chartsToExport) { var del = Delegate.CreateDelegate(typeof(Func), chart.InsightsMonitorsProperty.GetGetMethod()) as Func; delegates.Add(del); } using (DynamicCsvFile csvFile = new DynamicCsvFile(vm.SelectedFile)) { csvFile.Columns = chartsToExport.Select(x => new DynamicCsvFileColumn() { Name = x.Description }).ToList(); csvFile.Columns.Insert(0, new DynamicCsvFileColumn() { Name = "Time" }); int progress = 0; foreach (var frame in InsightsPackage.Frames) { List values = new List(); values.Add(frame.Frame.Time.ToLocalTime().ToString("MM/dd/yyyy HH:mm:ss.fff")); foreach (var del in delegates) { double value = del(frame.Monitors); String str = double.IsNaN(value) ? String.Empty : value.ToString(); values.Add(str); } csvFile.Append(values); progress++; snackbar.Message = $"Exporting insights to csv ({(int)((double)progress / (double)InsightsPackage.Frames.Count * 100d)})..."; } } snackbar.ProgressCompleted("File exported successfully.\nTap here to view the file.", TimeSpan.FromSeconds(10), () => { StorageProvider.ShowInExplorer(vm.SelectedFile); }); }); } catch (Exception ex) { LogManager.Log(ex, "Error exporting insights csv file."); snackbar.ProgressFailed($"Error exporting csv file.\n{ex.Message}"); } } #endregion #region Private Methods private (DateTime Start, DateTime End) GetInsightsRange(bool add1DayWhenEndDateIsNow = true) { DateTime start; if (StartTime != null) { start = new DateTime( StartDate.Value.Year, StartDate.Value.Month, StartDate.Value.Day, StartTime.Value.Hour, StartTime.Value.Minute, StartTime.Value.Second); } else { start = StartDate.Value.Date; } DateTime end; if (EndDate != null) { if (EndTime != null) { end = new DateTime( EndDate.Value.Year, EndDate.Value.Month, EndDate.Value.Day, EndTime.Value.Hour, EndTime.Value.Minute, EndTime.Value.Second); } else { end = EndDate.Value.Date.AddDays(1); } } else { end = add1DayWhenEndDateIsNow ? DateTime.Now.AddDays(1) : DateTime.Now; } return (start, end); } private void OnMonitorsFilterChanged() { _monitorsView?.Refresh(); } private void OnIsSettingsBarOpenedChanged() { if (!IsSettingsBarOpened) { InitMonitorsView(); } } private void InitMonitorsView() { Charts = Charts.OrderByAlphaNumeric(x => x.Description).OrderByDescending(x => x.IsVisible).ToObservableCollection(); _monitorsView = CollectionViewSource.GetDefaultView(Charts); _monitorsView.Filter = (x) => { if (!MonitorsFilter.IsNotNullOrEmpty()) return true; InsightsChart chart = x as InsightsChart; if (chart != null) { return chart.Description.ToLower().Contains(MonitorsFilter.ToLower()); } return false; }; } private List GetDemoDates() { List dates = new List(); for (int i = 0; i < 30; i++) { dates.Add(DateTime.Now.AddDays(-30).AddDays(i)); } return dates; } private List GetDemoValues() { List values = new List(); int counter2 = 0; for (int i = 0; i < 30; i++) { if (counter2 == 2) { values.Add(double.NaN); counter2 = 0; } else { values.Add(i); counter2++; } } return values; } #endregion #region Settings private void SaveSettings() { if (ApplicationManager != null && ApplicationManager.IsReady && StartDate != null) { var range = GetInsightsRange(false); ModuleSettings.DefaultDiffDays = (int)(range.End - range.Start).TotalDays; ModuleSettings.DisplayAnnotations = DisplayAnnotations; ModuleSettings.VisibleMonitors = VisibleCharts.Select(x => x.TechMonitor).ToList(); ModuleSettings.AnnotationsConfiguration = AnnotationsConfiguration; } } #endregion #region Annotations Configuration private void InitEventsSelection() { SelectedEventsCategories = new SelectedObjectCollection(new ObservableCollection() { EventTypeCategories.Info, EventTypeCategories.Warning, EventTypeCategories.Error, EventTypeCategories.Critical }, AnnotationsConfiguration.EventsCategories.ToObservableCollection()); SelectedEventsGroups = new SelectedObjectCollection(new ObservableCollection() { //EventTypeGroups.Jobs, Job are artificially from a check box. EventTypeGroups.Application, EventTypeGroups.Dispensers, EventTypeGroups.Dryer, EventTypeGroups.DyeingHead, EventTypeGroups.ElectricalCabinet, EventTypeGroups.GeneralHardware, EventTypeGroups.InkDeliverySystem, EventTypeGroups.InkFillingSystem, EventTypeGroups.Mixer, EventTypeGroups.ThreadFeedingSystem, EventTypeGroups.WasteHandlingSystem }, AnnotationsConfiguration.EventsGroups.ToObservableCollection()); SelectedEventsCategories.SelectionChanged += Events_SelectionChanged; SelectedEventsGroups.SelectionChanged += Events_SelectionChanged; } private void Events_SelectionChanged(object sender, EventArgs e) { AnnotationsConfiguration.EventsCategories = SelectedEventsCategories.SynchedSource.ToObservableCollection(); AnnotationsConfiguration.EventsGroups = SelectedEventsGroups.SynchedSource.ToObservableCollection(); } private void OnAnnotationsConfigurationChanged() { if (AnnotationsConfiguration != null) { AnnotationsConfiguration.PropertyChanged -= AnnotationsConfiguration_PropertyChanged; AnnotationsConfiguration.PropertyChanged += AnnotationsConfiguration_PropertyChanged; } } private void AnnotationsConfiguration_PropertyChanged(object sender, PropertyChangedEventArgs e) { RaisePropertyChanged(nameof(AnnotationsConfiguration)); Annotations.OfType().ToList().ForEach(x => x.IsHidden = !AnnotationsConfiguration.DisplayConnectionMarkers); } #endregion } }