From 7bd70fcb311c808b65b62e774755dcbd6b0d63cd Mon Sep 17 00:00:00 2001 From: Roy Ben Shabat Date: Fri, 21 Aug 2020 12:58:15 +0300 Subject: Insights anomalies. --- .../Dialogs/AnomaliesDialogView.xaml | 54 ++ .../Dialogs/AnomaliesDialogView.xaml.cs | 28 ++ .../Dialogs/AnomaliesDialogViewVM.cs | 66 +++ .../Tango.FSE.Insights/Images/detection.png | Bin 0 -> 5717 bytes .../Tango.FSE.Insights/Images/insight_big.png | Bin 0 -> 7682 bytes .../FSE/Modules/Tango.FSE.Insights/Images/time.png | Bin 9091 -> 4974 bytes .../ML/AnomaliesDetectionProgress.cs | 30 ++ .../Tango.FSE.Insights/ML/AnomaliesDetector.cs | 63 +++ .../Modules/Tango.FSE.Insights/ML/AnomalyType.cs | 13 + .../Tango.FSE.Insights/ML/DetectedAnnomaly.cs | 21 + .../ML/MonitorSeriesPrediction.cs | 15 + .../Modules/Tango.FSE.Insights/ML/MonitorValue.cs | 17 + .../Tango.FSE.Insights/SciChart/InsightsChart.cs | 101 +++- .../Tango.FSE.Insights/Tango.FSE.Insights.csproj | 85 +++- .../Modules/Tango.FSE.Insights/Themes/Generic.xaml | 7 + .../ViewModels/InsightsViewVM.cs | 262 +++++++++- .../Tango.FSE.Insights/Views/InsightsView.xaml | 545 +++++++++++++-------- .../FSE/Modules/Tango.FSE.Insights/app.config | 44 +- .../FSE/Modules/Tango.FSE.Insights/packages.config | 16 +- .../Tango.FSE.Common/Insights/IInsightsProvider.cs | 1 + Software/Visual_Studio/FSE/Tango.FSE.UI/App.config | 10 +- .../Insights/DefaultInsightsProvider.cs | 42 +- .../FSE/Tango.FSE.UI/Tango.FSE.UI.csproj | 4 +- .../Visual_Studio/FSE/Tango.FSE.UI/packages.config | 2 +- .../Tango.PPC.MachineSettings/Views/MainView.xaml | 4 +- .../EventLogging/DefaultEventLogger.cs | 12 +- .../Insights/DefaultInsightsService.cs | 38 ++ .../Insights/InsightsDownloadCompletedRequest.cs | 13 + .../Insights/InsightsDownloadCompletedResponse.cs | 12 + .../Insights/InsightsMinDateRequest.cs | 12 + .../Insights/InsightsMinDateResponse.cs | 13 + .../PPC/Tango.PPC.Shared/Tango.PPC.Shared.csproj | 4 + .../Tango.Core/ExtensionMethods/ListExtensions.cs | 26 + .../Visual_Studio/Tango.Core/Tango.Core.csproj | 3 +- .../Tango.Insights/InsightsManager.cs | 27 +- 35 files changed, 1341 insertions(+), 249 deletions(-) create mode 100644 Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/Dialogs/AnomaliesDialogView.xaml create mode 100644 Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/Dialogs/AnomaliesDialogView.xaml.cs create mode 100644 Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/Dialogs/AnomaliesDialogViewVM.cs create mode 100644 Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/Images/detection.png create mode 100644 Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/Images/insight_big.png create mode 100644 Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/ML/AnomaliesDetectionProgress.cs create mode 100644 Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/ML/AnomaliesDetector.cs create mode 100644 Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/ML/AnomalyType.cs create mode 100644 Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/ML/DetectedAnnomaly.cs create mode 100644 Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/ML/MonitorSeriesPrediction.cs create mode 100644 Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/ML/MonitorValue.cs create mode 100644 Software/Visual_Studio/PPC/Tango.PPC.Shared/Insights/InsightsDownloadCompletedRequest.cs create mode 100644 Software/Visual_Studio/PPC/Tango.PPC.Shared/Insights/InsightsDownloadCompletedResponse.cs create mode 100644 Software/Visual_Studio/PPC/Tango.PPC.Shared/Insights/InsightsMinDateRequest.cs create mode 100644 Software/Visual_Studio/PPC/Tango.PPC.Shared/Insights/InsightsMinDateResponse.cs create mode 100644 Software/Visual_Studio/Tango.Core/ExtensionMethods/ListExtensions.cs (limited to 'Software/Visual_Studio') diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/Dialogs/AnomaliesDialogView.xaml b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/Dialogs/AnomaliesDialogView.xaml new file mode 100644 index 000000000..375f0be0d --- /dev/null +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/Dialogs/AnomaliesDialogView.xaml @@ -0,0 +1,54 @@ + + + + + + + Anomalies Detection + + + + Anomalies detection can help you diagnose unusual events and behaviors. + + Please select monitors for the analysis and press 'ANALYZE'. + + Once the analysis completes, special chart annotations will be overlayed on the selected monitors charts. + + + + + + + + + + Please specify maximum 8 monitors for analysis + + + + + + + + + + + + + + + + + + + + + diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/Dialogs/AnomaliesDialogView.xaml.cs b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/Dialogs/AnomaliesDialogView.xaml.cs new file mode 100644 index 000000000..b8a819b7b --- /dev/null +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/Dialogs/AnomaliesDialogView.xaml.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; + +namespace Tango.FSE.Insights.Dialogs +{ + /// + /// Interaction logic for AnomaliesDialogView.xaml + /// + public partial class AnomaliesDialogView : UserControl + { + public AnomaliesDialogView() + { + InitializeComponent(); + } + } +} diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/Dialogs/AnomaliesDialogViewVM.cs b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/Dialogs/AnomaliesDialogViewVM.cs new file mode 100644 index 000000000..42c71327b --- /dev/null +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/Dialogs/AnomaliesDialogViewVM.cs @@ -0,0 +1,66 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Data; +using Tango.FSE.Common; +using Tango.FSE.Insights.SciChart; +using Tango.SharedUI.Components; + +namespace Tango.FSE.Insights.Dialogs +{ + public class AnomaliesDialogViewVM : FSEDialogViewVM + { + private const int MAX_SELECTION = 8; + private ICollectionView _view; + + public SelectedObjectCollection SelectedCharts { get; set; } + + private String _filter; + public String Filter + { + get { return _filter; } + set { _filter = value; RaisePropertyChangedAuto(); OnFilterChanged(); } + } + + public bool MaximumSelectionReached + { + get { return SelectedCharts.SynchedSource.Count > MAX_SELECTION; } + } + + public AnomaliesDialogViewVM(List availableCharts, List selectedCharts) + { + OKText = "ANALYZE"; + SelectedCharts = new SelectedObjectCollection(availableCharts.OrderByDescending(x => selectedCharts.Contains(x)).ToObservableCollection(), selectedCharts.ToObservableCollection()); + SelectedCharts.SelectionChanged += SelectedCharts_SelectionChanged; + + _view = CollectionViewSource.GetDefaultView(SelectedCharts); + _view.Filter = FilterCharts; + } + + private bool FilterCharts(object obj) + { + if (String.IsNullOrWhiteSpace(Filter)) return true; + return (obj as SelectedObject).Data.Description.ToLower().Contains(Filter.ToLower()); + } + + private void SelectedCharts_SelectionChanged(object sender, EventArgs e) + { + RaisePropertyChanged(nameof(MaximumSelectionReached)); + InvalidateRelayCommands(); + } + + protected override bool CanOK() + { + return base.CanOK() && SelectedCharts.SynchedSource.Count > 0 && !MaximumSelectionReached; + } + + private void OnFilterChanged() + { + _view?.Refresh(); + } + } +} diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/Images/detection.png b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/Images/detection.png new file mode 100644 index 000000000..a29355327 Binary files /dev/null and b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/Images/detection.png differ diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/Images/insight_big.png b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/Images/insight_big.png new file mode 100644 index 000000000..e5cc9e2dc Binary files /dev/null and b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/Images/insight_big.png differ diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/Images/time.png b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/Images/time.png index 1eb9b077f..542bd09db 100644 Binary files a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/Images/time.png and b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/Images/time.png differ diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/ML/AnomaliesDetectionProgress.cs b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/ML/AnomaliesDetectionProgress.cs new file mode 100644 index 000000000..6331e7de0 --- /dev/null +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/ML/AnomaliesDetectionProgress.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Tango.Core; +using Tango.FSE.Insights.SciChart; + +namespace Tango.FSE.Insights.ML +{ + public class AnomaliesDetectionProgress : ExtendedObject + { + public InsightsChart Chart { get; set; } + public int FramesCount { get; set; } + private int _currentFrame; + + public int CurrentFrame + { + get { return _currentFrame; } + set + { + if (value > _currentFrame + 9 || value == FramesCount) + { + _currentFrame = value; + RaisePropertyChanged(nameof(CurrentFrame)); + } + } + } + } +} diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/ML/AnomaliesDetector.cs b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/ML/AnomaliesDetector.cs new file mode 100644 index 000000000..4a81e9eb5 --- /dev/null +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/ML/AnomaliesDetector.cs @@ -0,0 +1,63 @@ +using Microsoft.ML; +using Microsoft.ML.Transforms.TimeSeries; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Tango.FSE.Insights.ML +{ + public class AnomaliesDetector + { + private IDataView _view; + private MLContext _mlContext; + private List _values; + + private AnomaliesDetector() + { + _mlContext = new MLContext(); + } + + public static Task FromMonitor(List monitorValues) + { + return Task.Factory.StartNew(() => + { + AnomaliesDetector engine = new AnomaliesDetector(); + engine._values = monitorValues; + engine._view = engine._mlContext.Data.LoadFromEnumerable(monitorValues); + return engine; + }); + } + + public void DetectSpikes() + { + var iidSpikeEstimator = _mlContext.Transforms.DetectIidSpike(outputColumnName: nameof(MonitorSeriesPrediction.Prediction), inputColumnName: nameof(MonitorValue.Value), confidence: 95, pvalueHistoryLength: _values.Count / 4, side: AnomalySide.TwoSided); + + ITransformer iidSpikeTransform = iidSpikeEstimator.Fit(CreateEmptyDataView()); + + IDataView transformedData = iidSpikeTransform.Transform(_view); + + var predictions = _mlContext.Data.CreateEnumerable(transformedData, reuseRowObject: false); + + foreach (var p in predictions) + { + var results = $"{p.Prediction[0]}\t{p.Prediction[1]:f2}\t{p.Prediction[2]:F2}"; + + if (p.Prediction[0] == 1) + { + results += " <-- Spike detected"; + } + + Debug.WriteLine(results); + } + } + + private IDataView CreateEmptyDataView() + { + IEnumerable enumerableData = new List(); + return _mlContext.Data.LoadFromEnumerable(enumerableData); + } + } +} diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/ML/AnomalyType.cs b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/ML/AnomalyType.cs new file mode 100644 index 000000000..7f24de856 --- /dev/null +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/ML/AnomalyType.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Tango.FSE.Insights.ML +{ + public enum AnomalyType + { + Spike, + } +} diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/ML/DetectedAnnomaly.cs b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/ML/DetectedAnnomaly.cs new file mode 100644 index 000000000..f031179b7 --- /dev/null +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/ML/DetectedAnnomaly.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Tango.FSE.Insights.ML +{ + public class DetectedAnnomaly + { + public AnomalyType Type { get; set; } + public DateTime Time { get; set; } + public double Value { get; set; } + public double Score { get; set; } + + public override string ToString() + { + return $"{Time} => {Type} => {Value}"; + } + } +} diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/ML/MonitorSeriesPrediction.cs b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/ML/MonitorSeriesPrediction.cs new file mode 100644 index 000000000..5c1a1a672 --- /dev/null +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/ML/MonitorSeriesPrediction.cs @@ -0,0 +1,15 @@ +using Microsoft.ML.Data; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Tango.FSE.Insights.ML +{ + public class MonitorSeriesPrediction + { + [VectorType(3)] + public double[] Prediction { get; set; } + } +} diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/ML/MonitorValue.cs b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/ML/MonitorValue.cs new file mode 100644 index 000000000..c9d6fd9ef --- /dev/null +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/ML/MonitorValue.cs @@ -0,0 +1,17 @@ +using Microsoft.ML.Data; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Tango.FSE.Insights.ML +{ + public class MonitorValue + { + public DateTime Time { get; set; } + + [LoadColumn(0)] + public float Value { get; set; } + } +} diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/SciChart/InsightsChart.cs b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/SciChart/InsightsChart.cs index c51015e66..be0bff2dc 100644 --- a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/SciChart/InsightsChart.cs +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/SciChart/InsightsChart.cs @@ -1,4 +1,6 @@ -using SciChart.Charting.Model.ChartSeries; +using Microsoft.ML; +using Microsoft.ML.Transforms.TimeSeries; +using SciChart.Charting.Model.ChartSeries; using SciChart.Charting.Model.DataSeries; using SciChart.Charting.Visuals.Annotations; using SciChart.Charting.Visuals.RenderableSeries; @@ -9,8 +11,11 @@ using System.Linq; using System.Reflection; using System.Text; using System.Threading.Tasks; +using System.Windows; using System.Windows.Media; +using Tango.BL.Enumerations; using Tango.Core; +using Tango.FSE.Insights.ML; namespace Tango.FSE.Insights.SciChart { @@ -21,10 +26,13 @@ namespace Tango.FSE.Insights.SciChart private ObservableCollection ChartsCollection { get; set; } public String Name { get; set; } public String Description { get; set; } + public TechMonitors TechMonitor { get; set; } public PropertyInfo InsightsMonitorsProperty { get; set; } public IRenderableSeriesViewModel RenderableSeries { get; set; } public XyDataSeries DataSeries { get; set; } + public List MonitorValues { get; set; } public AnnotationCollection Annotations { get; set; } + public List Anomalies { get; set; } private bool _isVisible; public bool IsVisible @@ -51,6 +59,8 @@ namespace Tango.FSE.Insights.SciChart public InsightsChart(ObservableCollection chartsCollection) { + Anomalies = new List(); + MonitorValues = new List(); ChartsCollection = chartsCollection; ChartsCollection.CollectionChanged += ChartsCollection_CollectionChanged; RenderableSeries = new LineRenderableSeriesViewModel() { DrawNaNAs = LineDrawMode.Gaps }; @@ -64,5 +74,94 @@ namespace Tango.FSE.Insights.SciChart { RaisePropertyChanged(nameof(IsLast)); } + + public List DetectAnnomalies(Action progress = null) + { + List annomalies = new List(); + + if (MonitorValues.Count > 0 && MonitorValues.Any(x => x.Value != 0)) + { + var chunks = MonitorValues.ChunkBy(1000); + + double lastValue = 0; + int totalIndex = 0; + + foreach (var chunk in chunks) + { + MLContext mlContext = new MLContext(); + IDataView view = mlContext.Data.LoadFromEnumerable(chunk); + + var iidSpikeEstimator = mlContext.Transforms.DetectIidSpike(outputColumnName: nameof(MonitorSeriesPrediction.Prediction), inputColumnName: nameof(MonitorValue.Value), confidence: 95, pvalueHistoryLength: chunk.Count / 4, side: AnomalySide.Positive); + + ITransformer iidSpikeTransform = iidSpikeEstimator.Fit(CreateEmptyDataView(mlContext)); + + IDataView transformedData = iidSpikeTransform.Transform(view); + + var predictions = mlContext.Data.CreateEnumerable(transformedData, reuseRowObject: false); + + int index = 0; + + foreach (var p in predictions) + { + if (p.Prediction[2] < (1 - 0.95)) + { + p.Prediction[0] = 1; + } + + if (p.Prediction[0] == 1) + { + var monitorValue = chunk[index]; + + if (lastValue != monitorValue.Value) + { + annomalies.Add(new DetectedAnnomaly() + { + Type = AnomalyType.Spike, + Time = monitorValue.Time, + Value = monitorValue.Value, + Score = p.Prediction[1], + }); + + lastValue = monitorValue.Value; + } + } + + index++; + totalIndex++; + + progress?.Invoke(totalIndex); + } + } + + annomalies = annomalies.OrderByDescending(x => x.Score).Take(10).OrderBy(x => x.Time).ToList(); + } + + progress?.Invoke(MonitorValues.Count); + + Anomalies = annomalies.ToList(); + + return annomalies; + } + + private IDataView CreateEmptyDataView(MLContext context) + { + IEnumerable enumerableData = new List(); + return context.Data.LoadFromEnumerable(enumerableData); + } + + public void DrawAnomalies() + { + foreach (var anomaly in Anomalies) + { + CustomAnnotation annotation = new CustomAnnotation(); + annotation.CoordinateMode = AnnotationCoordinateMode.Absolute; + annotation.X1 = anomaly.Time.ToLocalTime(); + annotation.Y1 = anomaly.Value; + annotation.Content = anomaly; + annotation.ContentTemplate = Application.Current.Resources["InsightAnomalyTemplate"] as DataTemplate; + + Annotations.Add(annotation); + } + } } } diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/Tango.FSE.Insights.csproj b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/Tango.FSE.Insights.csproj index 65521c65d..ea6616985 100644 --- a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/Tango.FSE.Insights.csproj +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/Tango.FSE.Insights.csproj @@ -1,5 +1,8 @@  + + + Debug @@ -55,8 +58,36 @@ ..\..\..\packages\MaterialDesignThemes.3.0.1\lib\net45\MaterialDesignThemes.Wpf.dll - - ..\..\..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll + + ..\..\..\packages\Microsoft.ML.1.5.1\lib\netstandard2.0\Microsoft.ML.Core.dll + + + ..\..\..\packages\Microsoft.ML.CpuMath.1.5.1\lib\netstandard2.0\Microsoft.ML.CpuMath.dll + + + ..\..\..\packages\Microsoft.ML.1.5.1\lib\netstandard2.0\Microsoft.ML.Data.dll + + + ..\..\..\packages\Microsoft.ML.DataView.1.5.1\lib\netstandard2.0\Microsoft.ML.DataView.dll + + + ..\..\..\packages\Microsoft.ML.1.5.1\lib\netstandard2.0\Microsoft.ML.KMeansClustering.dll + + + ..\..\..\packages\Microsoft.ML.1.5.1\lib\netstandard2.0\Microsoft.ML.PCA.dll + + + ..\..\..\packages\Microsoft.ML.1.5.1\lib\netstandard2.0\Microsoft.ML.StandardTrainers.dll + + + ..\..\..\packages\Microsoft.ML.TimeSeries.1.5.1\lib\netstandard2.0\Microsoft.ML.TimeSeries.dll + + + ..\..\..\packages\Microsoft.ML.1.5.1\lib\netstandard2.0\Microsoft.ML.Transforms.dll + + + ..\..\..\packages\Newtonsoft.Json.12.0.3\lib\net45\Newtonsoft.Json.dll + False False @@ -87,8 +118,33 @@ ..\..\..\Referenced Assemblies\SciChart\SciChart.Drawing.DirectX.dll + + ..\..\..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll + + + ..\..\..\packages\System.CodeDom.4.7.0\lib\net461\System.CodeDom.dll + + + ..\..\..\packages\System.Collections.Immutable.1.7.1\lib\net461\System.Collections.Immutable.dll + + + ..\..\..\packages\System.Memory.4.5.4\lib\net461\System.Memory.dll + + + + ..\..\..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll + + + ..\..\..\packages\System.Runtime.CompilerServices.Unsafe.4.7.1\lib\net461\System.Runtime.CompilerServices.Unsafe.dll + + + ..\..\..\packages\System.Threading.Channels.4.7.1\lib\net461\System.Threading.Channels.dll + + + ..\..\..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll + ..\..\..\packages\ControlzEx.3.0.2.4\lib\net45\System.Windows.Interactivity.dll @@ -107,6 +163,16 @@ + + AnomaliesDialogView.xaml + + + + + + + + @@ -202,6 +268,10 @@ MSBuild:Compile Designer + + Designer + MSBuild:Compile + MSBuild:Compile Designer @@ -236,6 +306,12 @@ + + + + + + @@ -243,5 +319,10 @@ This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + \ No newline at end of file diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/Themes/Generic.xaml b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/Themes/Generic.xaml index f4cd75c76..cf9c18b68 100644 --- a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/Themes/Generic.xaml +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/Themes/Generic.xaml @@ -4,6 +4,7 @@ xmlns:entities="clr-namespace:Tango.BL.Entities;assembly=Tango.BL" xmlns:material="http://materialdesigninxaml.net/winfx/xaml/themes" xmlns:commonInsights="clr-namespace:Tango.FSE.Common.Insights;assembly=Tango.FSE.Common" + xmlns:ML="clr-namespace:Tango.FSE.Insights.ML" xmlns:views="clr-namespace:Tango.FSE.Insights.Views" xmlns:local="clr-namespace:Tango.FSE.Diagnostics.Themes"> @@ -129,4 +130,10 @@ + + + + + + \ No newline at end of file diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/ViewModels/InsightsViewVM.cs b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/ViewModels/InsightsViewVM.cs index 0486955e0..b36ef163a 100644 --- a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/ViewModels/InsightsViewVM.cs +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/ViewModels/InsightsViewVM.cs @@ -6,6 +6,7 @@ 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; @@ -21,6 +22,8 @@ using Tango.Core.Commands; using Tango.FSE.Common; using Tango.FSE.Common.Insights; 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; @@ -32,15 +35,37 @@ namespace Tango.FSE.Insights.ViewModels { 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; } - public ObservableCollection Charts { 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 { @@ -108,7 +133,7 @@ namespace Tango.FSE.Insights.ViewModels public InsightsPackage InsightsPackage { get { return _insightsPackage; } - set { _insightsPackage = value; RaisePropertyChangedAuto(); RaisePropertyChanged(nameof(InsightsPackageAvailable)); } + set { _insightsPackage = value; RaisePropertyChangedAuto(); RaisePropertyChanged(nameof(InsightsPackageAvailable)); InvalidateRelayCommands(); } } public bool InsightsPackageAvailable @@ -123,6 +148,13 @@ namespace Tango.FSE.Insights.ViewModels set { _displayAnnotations = value; RaisePropertyChangedAuto(); } } + private bool _displayAnomalies; + public bool DisplayAnomalies + { + get { return _displayAnomalies; } + set { _displayAnomalies = value; RaisePropertyChangedAuto(); } + } + private bool _isSearchBarOpened; public bool IsSearchBarOpened { @@ -130,6 +162,21 @@ namespace Tango.FSE.Insights.ViewModels 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; } #endregion @@ -140,19 +187,27 @@ namespace Tango.FSE.Insights.ViewModels public RelayCommand PeekFirmwareLogsCommand { get; set; } + public RelayCommand AnalyzeCommand { get; set; } + #endregion #region Constructors public InsightsViewVM() { + MinDate = DateTime.Now.AddDays(-30); + MaxDate = DateTime.Now; + IsSearchBarOpened = true; + DisplayAnnotations = true; + DisplayAnomalies = true; Charts = new ObservableCollection(); VisibleCharts = new ObservableCollection(); Annotations = new AnnotationCollection(); GetInsightsCommand = new RelayCommand(GetInsights, () => IsFree); PeekApplicationLogsCommand = new RelayCommand(PeekAnnotationApplicationLogs); PeekFirmwareLogsCommand = new RelayCommand(PeekAnnotationFirmwareLogs); + AnalyzeCommand = new RelayCommand(Analyze, () => InsightsPackage != null); } #endregion @@ -166,6 +221,8 @@ namespace Tango.FSE.Insights.ViewModels StartTime = new DateTime(1, 1, 1, 0, 0, 0); EndDate = null; EndTime = null; + + MachineProvider.MachineConnected += MachineProvider_MachineConnected; } public async override void OnApplicationReady() @@ -176,13 +233,14 @@ namespace Tango.FSE.Insights.ViewModels { try { - var monitors = (await Services.TechComponentsService.Monitors.FindAll()).OrderBy(x => x.Description).ToList(); + 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()); @@ -195,25 +253,14 @@ namespace Tango.FSE.Insights.ViewModels LogManager.Log($"Error loading insights chart '{chart.Name}'. Could not find property on InsightsMonitors.", LogCategory.Error); } - _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; - }; + InitMonitorsView(); _chartsLoaded = true; } catch (Exception ex) { - //TODO: Report Insights module error with BugReporting Snackbar... + LogManager.Log(ex, "Error initializing insights module."); + NotificationProvider.PushErrorReportingSnackbar(ex, "Insights Module Error", "Error initializing insights module."); } } } @@ -225,7 +272,6 @@ namespace Tango.FSE.Insights.ViewModels public override void OnNavigatedTo() { base.OnNavigatedTo(); - MinDate = DateTime.Now.AddDays(-30); MaxDate = DateTime.Now; } @@ -298,7 +344,7 @@ namespace Tango.FSE.Insights.ViewModels } catch (Exception ex) { - throw; + LogManager.Log(ex, "Error aborting insights request."); } })) { @@ -333,6 +379,7 @@ namespace Tango.FSE.Insights.ViewModels 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 { @@ -359,9 +406,20 @@ namespace Tango.FSE.Insights.ViewModels dates.Add(frame.Frame.Time.ToLocalTime()); + if (value != null) { - values.Add((double)value); + double v = (double)value; + values.Add(v); + + if (!double.IsNaN(v)) + { + chart.MonitorValues.Add(new MonitorValue() + { + Time = frame.Frame.Time, + Value = (float)v + }); + } } else { @@ -376,6 +434,8 @@ namespace Tango.FSE.Insights.ViewModels InvokeUI(() => { Annotations.Clear(); + Charts.ToList().ForEach(x => x.Anomalies.Clear()); + Charts.ToList().ForEach(x => x.Annotations.Clear()); //Load annotations bool disconnected = false; @@ -416,13 +476,13 @@ namespace Tango.FSE.Insights.ViewModels { VerticalLineAnnotation line = new VerticalLineAnnotation(); line.StrokeThickness = 2; - line.Stroke = connected ? Brushes.Green : Brushes.Red; + line.Stroke = connected ? Brushes.Green : Brushes.DarkGray; line.X1 = date; line.X2 = date; line.ShowLabel = false; line.AnnotationLabels.Add(new AnnotationLabel() { - LabelPlacement = LabelPlacement.TopRight, + LabelPlacement = connected ? LabelPlacement.TopRight : LabelPlacement.BottomLeft, Text = connected ? "machine connected" : "machine disconnected" }); Annotations.Add(line); @@ -487,6 +547,33 @@ namespace Tango.FSE.Insights.ViewModels #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 (remoteMinDate > MinDate) + { + MinDate = remoteMinDate; + } + } + } + catch (Exception ex) + { + LogManager.Log(ex, "Error retrieving minimum insights date from the remote machine."); + } + } + private void Chart_IsVisibleChanged(object sender, EventArgs e) { InsightsChart chart = sender as InsightsChart; @@ -503,6 +590,109 @@ namespace Tango.FSE.Insights.ViewModels #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(); + } + 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 Private Methods private void OnMonitorsFilterChanged() @@ -510,6 +700,32 @@ namespace Tango.FSE.Insights.ViewModels _monitorsView?.Refresh(); } + private void OnIsSettingsBarOpenedChanged() + { + if (!IsSettingsBarOpened) + { + Charts = Charts.OrderByAlphaNumeric(x => x.Description).OrderByDescending(x => x.IsVisible).ToObservableCollection(); + InitMonitorsView(); + } + } + + private void InitMonitorsView() + { + _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(); @@ -544,7 +760,7 @@ namespace Tango.FSE.Insights.ViewModels } return values; - } + } #endregion } } diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/Views/InsightsView.xaml b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/Views/InsightsView.xaml index dda5861f2..65246662e 100644 --- a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/Views/InsightsView.xaml +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/Views/InsightsView.xaml @@ -13,243 +13,370 @@ mc:Ignorable="d" d:DesignHeight="1080" d:DesignWidth="1920" d:DataContext="{d:DesignInstance Type=vm:InsightsViewVM, IsDesignTimeCreatable=False}" DataContext="{x:Static global:ViewModelLocator.InsightsViewVM}"> - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + Insights + + Welcome to the Insights module + + This module allows you to collect, view and analyze important machine data using interactive charts and annotations. + + + + Use the search panel above to select target dates for data collection, then press 'Get Insights'. + + + Toggle the settings panel on the left to configure your workspace. + + + + The workspace supports panning and zooming. + Hold and drag the mouse to pan the charts, or hold the Ctrl key to zoom. + + + + + Insights available ! + Use the pane on the left to select monitors to display. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/app.config b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/app.config index 36bc04f85..391504c17 100644 --- a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/app.config +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/app.config @@ -12,7 +12,7 @@ - + @@ -52,7 +52,7 @@ - + @@ -74,6 +74,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/packages.config b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/packages.config index dd8c723e4..e06be2064 100644 --- a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/packages.config +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/packages.config @@ -6,5 +6,19 @@ - + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Software/Visual_Studio/FSE/Tango.FSE.Common/Insights/IInsightsProvider.cs b/Software/Visual_Studio/FSE/Tango.FSE.Common/Insights/IInsightsProvider.cs index f90e398fd..28af42d5f 100644 --- a/Software/Visual_Studio/FSE/Tango.FSE.Common/Insights/IInsightsProvider.cs +++ b/Software/Visual_Studio/FSE/Tango.FSE.Common/Insights/IInsightsProvider.cs @@ -8,6 +8,7 @@ namespace Tango.FSE.Common.Insights { public interface IInsightsProvider { + Task GetInsightsMinDate(); Task GetInsights(DateTime startDateUTC, DateTime endTimeUTC); } } diff --git a/Software/Visual_Studio/FSE/Tango.FSE.UI/App.config b/Software/Visual_Studio/FSE/Tango.FSE.UI/App.config index 60973a611..ed0662bd0 100644 --- a/Software/Visual_Studio/FSE/Tango.FSE.UI/App.config +++ b/Software/Visual_Studio/FSE/Tango.FSE.UI/App.config @@ -82,7 +82,15 @@ - + + + + + + + + + diff --git a/Software/Visual_Studio/FSE/Tango.FSE.UI/Insights/DefaultInsightsProvider.cs b/Software/Visual_Studio/FSE/Tango.FSE.UI/Insights/DefaultInsightsProvider.cs index 0f0531bce..1ddf0fa22 100644 --- a/Software/Visual_Studio/FSE/Tango.FSE.UI/Insights/DefaultInsightsProvider.cs +++ b/Software/Visual_Studio/FSE/Tango.FSE.UI/Insights/DefaultInsightsProvider.cs @@ -12,12 +12,14 @@ using Tango.FSE.Common.FileSystem; using Tango.FSE.Common.Insights; using Tango.Insights; using Tango.PPC.Shared.Insights; +using Tango.Transport; namespace Tango.FSE.UI.Insights { public class DefaultInsightsProvider : ExtendedObject, IInsightsProvider { - [TangoInject] + private DateTime? minDate; + private IMachineProvider MachineProvider { get; set; } [TangoInject] @@ -26,6 +28,17 @@ namespace Tango.FSE.UI.Insights [TangoInject] private FSEServicesContainer Services { get; set; } + public DefaultInsightsProvider(IMachineProvider machineProvider) + { + MachineProvider = machineProvider; + MachineProvider.MachineDisconnected += MachineProvider_MachineDisconnected; + } + + private void MachineProvider_MachineDisconnected(object sender, MachineDisconnectedEventArgs e) + { + minDate = null; + } + public async Task GetInsights(DateTime startDateUTC, DateTime endTimeUTC) { InsightsHandler handler = null; @@ -126,6 +139,17 @@ namespace Tango.FSE.UI.Insights { handler.RaiseFailed(ex); } + finally + { + try + { + var result = MachineProvider.MachineOperator.SendGenericRequest(new InsightsDownloadCompletedRequest() + { + InisightsFilePath = response.InisightsFilePath + }).Result; + } + catch { } + } }); } else if (status == FileSystemHandlerStatus.Failed) @@ -142,5 +166,21 @@ namespace Tango.FSE.UI.Insights return handler; } + + public async Task GetInsightsMinDate() + { + if (minDate != null) + { + return minDate; + } + + var response = await MachineProvider.MachineOperator.SendGenericRequest(new InsightsMinDateRequest(), new TransportRequestConfig() + { + Timeout = TimeSpan.FromSeconds(10) + }); + + minDate = response.MinDate; + return minDate; + } } } diff --git a/Software/Visual_Studio/FSE/Tango.FSE.UI/Tango.FSE.UI.csproj b/Software/Visual_Studio/FSE/Tango.FSE.UI/Tango.FSE.UI.csproj index 24cbfd55f..8974407d4 100644 --- a/Software/Visual_Studio/FSE/Tango.FSE.UI/Tango.FSE.UI.csproj +++ b/Software/Visual_Studio/FSE/Tango.FSE.UI/Tango.FSE.UI.csproj @@ -130,8 +130,8 @@ ..\..\packages\System.AppContext.4.3.0\lib\net46\System.AppContext.dll - - ..\..\packages\System.Collections.Immutable.1.3.1\lib\portable-net45+win8+wp8+wpa81\System.Collections.Immutable.dll + + ..\..\packages\System.Collections.Immutable.1.7.1\lib\net461\System.Collections.Immutable.dll diff --git a/Software/Visual_Studio/FSE/Tango.FSE.UI/packages.config b/Software/Visual_Studio/FSE/Tango.FSE.UI/packages.config index 4fda0a885..acd5512bc 100644 --- a/Software/Visual_Studio/FSE/Tango.FSE.UI/packages.config +++ b/Software/Visual_Studio/FSE/Tango.FSE.UI/packages.config @@ -21,7 +21,7 @@ - + diff --git a/Software/Visual_Studio/PPC/Modules/Tango.PPC.MachineSettings/Views/MainView.xaml b/Software/Visual_Studio/PPC/Modules/Tango.PPC.MachineSettings/Views/MainView.xaml index d436c58b7..3e7162616 100644 --- a/Software/Visual_Studio/PPC/Modules/Tango.PPC.MachineSettings/Views/MainView.xaml +++ b/Software/Visual_Studio/PPC/Modules/Tango.PPC.MachineSettings/Views/MainView.xaml @@ -345,12 +345,12 @@ Insights Storage Cleanup Interval (min) - + Insights Max Storage Duration (days) - + diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/EventLogging/DefaultEventLogger.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/EventLogging/DefaultEventLogger.cs index ade15af7b..841dfa4fc 100644 --- a/Software/Visual_Studio/PPC/Tango.PPC.Common/EventLogging/DefaultEventLogger.cs +++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/EventLogging/DefaultEventLogger.cs @@ -104,14 +104,22 @@ namespace Tango.PPC.Common.EventLogging foreach (var type in _db.EventTypes) { - _eventTypesGuids.Add((EventTypes)type.Code, type); + try + { + _eventTypesGuids.Add((EventTypes)type.Code, type); + } + catch (Exception ex) + { + LogManager.Log(ex, $"Error initializing event type '{type.Name}'."); + } } _isInitialized = true; } - catch + catch (Exception ex) { _isInitialized = false; + LogManager.Log(ex, "Error initializing event types."); } } } diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/Insights/DefaultInsightsService.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/Insights/DefaultInsightsService.cs index fbe606af9..f015c3bad 100644 --- a/Software/Visual_Studio/PPC/Tango.PPC.Common/Insights/DefaultInsightsService.cs +++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/Insights/DefaultInsightsService.cs @@ -39,6 +39,7 @@ namespace Tango.PPC.Common.Insights try { var settings = SettingsManager.Default.GetOrCreate(); + settings.Save(); if (settings.InsightsEnabled) { @@ -103,6 +104,43 @@ namespace Tango.PPC.Common.Insights } } + [ExternalBridgeRequestHandlerMethod(typeof(InsightsDownloadCompletedRequest), RequestHandlerLoggingMode.LogRequestName)] + public async Task OnInsightsDownloadCompletedRequest(InsightsDownloadCompletedRequest request, String token, ExternalBridgeReceiver receiver) + { + await Task.Factory.StartNew(() => + { + try + { + File.Delete(request.InisightsFilePath); + } + catch (Exception ex) + { + LogManager.Log(ex, "Error deleting insights request temp file after download completion."); + } + }); + await receiver.SendGenericResponse(new InsightsResponse(), token); + } + + [ExternalBridgeRequestHandlerMethod(typeof(InsightsMinDateRequest), RequestHandlerLoggingMode.LogRequestName)] + public async Task OnInsightsMinDateRequest(InsightsMinDateRequest request, String token, ExternalBridgeReceiver receiver) + { + DateTime? minDate = null; + + try + { + await Task.Factory.StartNew(() => + { + minDate = InsightsManager.Default.GetFramesMinDate(); + }); + } + catch (Exception ex) + { + LogManager.Log(ex, "Error retrieving insights frames minimum date."); + } + + await receiver.SendGenericResponse(new InsightsMinDateResponse() { MinDate = minDate }, token); + } + public void OnReceiverDisconnected(ExternalBridgeReceiver receiver) { //Do Nothing... diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Shared/Insights/InsightsDownloadCompletedRequest.cs b/Software/Visual_Studio/PPC/Tango.PPC.Shared/Insights/InsightsDownloadCompletedRequest.cs new file mode 100644 index 000000000..f298f6a6b --- /dev/null +++ b/Software/Visual_Studio/PPC/Tango.PPC.Shared/Insights/InsightsDownloadCompletedRequest.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Tango.PPC.Shared.Insights +{ + public class InsightsDownloadCompletedRequest + { + public String InisightsFilePath { get; set; } + } +} diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Shared/Insights/InsightsDownloadCompletedResponse.cs b/Software/Visual_Studio/PPC/Tango.PPC.Shared/Insights/InsightsDownloadCompletedResponse.cs new file mode 100644 index 000000000..04fd3d1b9 --- /dev/null +++ b/Software/Visual_Studio/PPC/Tango.PPC.Shared/Insights/InsightsDownloadCompletedResponse.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Tango.PPC.Shared.Insights +{ + public class InsightsDownloadCompletedResponse + { + } +} diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Shared/Insights/InsightsMinDateRequest.cs b/Software/Visual_Studio/PPC/Tango.PPC.Shared/Insights/InsightsMinDateRequest.cs new file mode 100644 index 000000000..1bb70c396 --- /dev/null +++ b/Software/Visual_Studio/PPC/Tango.PPC.Shared/Insights/InsightsMinDateRequest.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Tango.PPC.Shared.Insights +{ + public class InsightsMinDateRequest + { + } +} diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Shared/Insights/InsightsMinDateResponse.cs b/Software/Visual_Studio/PPC/Tango.PPC.Shared/Insights/InsightsMinDateResponse.cs new file mode 100644 index 000000000..7d0e0db84 --- /dev/null +++ b/Software/Visual_Studio/PPC/Tango.PPC.Shared/Insights/InsightsMinDateResponse.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Tango.PPC.Shared.Insights +{ + public class InsightsMinDateResponse + { + public DateTime? MinDate { get; set; } + } +} diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Shared/Tango.PPC.Shared.csproj b/Software/Visual_Studio/PPC/Tango.PPC.Shared/Tango.PPC.Shared.csproj index cc39c8746..de1eb03b1 100644 --- a/Software/Visual_Studio/PPC/Tango.PPC.Shared/Tango.PPC.Shared.csproj +++ b/Software/Visual_Studio/PPC/Tango.PPC.Shared/Tango.PPC.Shared.csproj @@ -65,6 +65,10 @@ + + + + diff --git a/Software/Visual_Studio/Tango.Core/ExtensionMethods/ListExtensions.cs b/Software/Visual_Studio/Tango.Core/ExtensionMethods/ListExtensions.cs new file mode 100644 index 000000000..f3e57b3ea --- /dev/null +++ b/Software/Visual_Studio/Tango.Core/ExtensionMethods/ListExtensions.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + + +public static class ListExtensions +{ + /// + /// Splits the list to chunks. + /// + /// + /// The source. + /// Size of the chunk. + /// + public static List> ChunkBy(this List source, int chunkSize) + { + return source + .Select((x, i) => new { Index = i, Value = x }) + .GroupBy(x => x.Index / chunkSize) + .Select(x => x.Select(v => v.Value).ToList()) + .ToList(); + } +} + diff --git a/Software/Visual_Studio/Tango.Core/Tango.Core.csproj b/Software/Visual_Studio/Tango.Core/Tango.Core.csproj index ef8ab0a30..2db9c8643 100644 --- a/Software/Visual_Studio/Tango.Core/Tango.Core.csproj +++ b/Software/Visual_Studio/Tango.Core/Tango.Core.csproj @@ -100,6 +100,7 @@ + @@ -215,7 +216,7 @@ - + diff --git a/Software/Visual_Studio/Tango.Insights/InsightsManager.cs b/Software/Visual_Studio/Tango.Insights/InsightsManager.cs index b0c7b78ac..73b74bff6 100644 --- a/Software/Visual_Studio/Tango.Insights/InsightsManager.cs +++ b/Software/Visual_Studio/Tango.Insights/InsightsManager.cs @@ -105,7 +105,21 @@ namespace Tango.Insights return collection.DeleteMany(x => x.Time < maxDateUTC); } - public virtual List GetEvents(DateTime startUTC, DateTime endUTC) + public DateTime? GetFramesMinDate() + { + var collection = GetInsightsCollection(); + + if (collection.Count() > 0) + { + return collection.Min(x => x.Time); + } + else + { + return null; + } + } + + public virtual List GetEvents(DateTime startUTC, DateTime endUTC, bool limitToMinFramesTime = true) { using (ObservablesContext db = ObservablesContext.CreateDefault()) { @@ -128,6 +142,17 @@ namespace Tango.Insights .OrderBy(x => x.Time) .ToList(); + if (limitToMinFramesTime) + { + var collection = GetInsightsCollection(); + + if (collection.Count() > 0) + { + var minTime = collection.Min(x => x.Time); + events.RemoveAll(x => x.Time < minTime); + } + } + return events; } } -- cgit v1.3.1