diff options
| author | Roy Ben Shabat <Roy.mail.net@gmail.com> | 2020-08-21 12:58:15 +0300 |
|---|---|---|
| committer | Roy Ben Shabat <Roy.mail.net@gmail.com> | 2020-08-21 12:58:15 +0300 |
| commit | 7bd70fcb311c808b65b62e774755dcbd6b0d63cd (patch) | |
| tree | 56db3c489de3665252c44d9ff477bb47bb722dc1 /Software/Visual_Studio | |
| parent | b0255ed4ae2827801d13cec175e57108a0666db9 (diff) | |
| download | Tango-7bd70fcb311c808b65b62e774755dcbd6b0d63cd.tar.gz Tango-7bd70fcb311c808b65b62e774755dcbd6b0d63cd.zip | |
Insights anomalies.
Diffstat (limited to 'Software/Visual_Studio')
35 files changed, 1337 insertions, 245 deletions
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 @@ +<UserControl x:Class="Tango.FSE.Insights.Dialogs.AnomaliesDialogView" + xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:local="clr-namespace:Tango.FSE.Insights.Dialogs" + xmlns:material="http://materialdesigninxaml.net/winfx/xaml/themes" + mc:Ignorable="d" + Width="700" Height="500" d:DataContext="{d:DesignInstance Type=local:AnomaliesDialogViewVM, IsDesignTimeCreatable=False}" Background="{StaticResource FSE_PrimaryBackgroundLightBrush}" Foreground="{StaticResource FSE_PrimaryForegroundBrush}"> + <Grid Margin="10"> + <DockPanel> + <StackPanel DockPanel.Dock="Top" > + <StackPanel Orientation="Horizontal"> + <Image Source="../Images/insight_big.png" Stretch="Uniform" Width="50" RenderOptions.BitmapScalingMode="Fant" /> + <TextBlock Margin="10 0 0 0" FontSize="{StaticResource FSE_LargerFontSize}" VerticalAlignment="Center">Anomalies Detection</TextBlock> + </StackPanel> + + <TextBlock Margin="0 10 0 0" TextWrapping="Wrap" FontSize="{StaticResource FSE_SmallFontSize}"> + <Run>Anomalies detection can help you diagnose unusual events and behaviors.</Run> + <LineBreak/> + <Run>Please select monitors for the analysis and press 'ANALYZE'.</Run> + <LineBreak/> + <Run>Once the analysis completes, special chart annotations will be overlayed on the selected monitors charts.</Run> + </TextBlock> + + <DockPanel Margin="0 10 0 0"> + <material:PackIcon Kind="Search" VerticalAlignment="Center" Width="20" Height="20" /> + <TextBox Margin="5 0 0 0" Padding="1" material:HintAssist.Hint="Find Monitor" Text="{Binding Filter,UpdateSourceTrigger=PropertyChanged,Delay=300}" Width="300" HorizontalAlignment="Left"></TextBox> + </DockPanel> + </StackPanel> + + <DockPanel> + <TextBlock Margin="0 5 0 0" Visibility="{Binding MaximumSelectionReached,Converter={StaticResource BooleanToVisibilityConverter}}" DockPanel.Dock="Bottom" Foreground="{StaticResource FSE_ErrorBrush}">Please specify maximum 8 monitors for analysis</TextBlock> + <Border Margin="0 10 0 0" BorderThickness="1" BorderBrush="{StaticResource FSE_BorderBrush}" Background="{StaticResource FSE_PrimaryBackgroundDarkBrush}" CornerRadius="5"> + <ScrollViewer Margin="2" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled"> + <ItemsControl ItemsSource="{Binding SelectedCharts}"> + <ItemsControl.ItemTemplate> + <DataTemplate> + <Border Padding="5"> + <StackPanel Orientation="Horizontal"> + <CheckBox VerticalAlignment="Center" IsChecked="{Binding IsSelected,Mode=TwoWay}" /> + <material:PackIcon Margin="5 0 0 0" VerticalAlignment="Center" Kind="ChartLine" Width="20" Height="20" /> + <TextBlock Margin="10 0 0 0" Text="{Binding Data.Description}" VerticalAlignment="Center"></TextBlock> + </StackPanel> + </Border> + </DataTemplate> + </ItemsControl.ItemTemplate> + </ItemsControl> + </ScrollViewer> + </Border> + </DockPanel> + </DockPanel> + </Grid> +</UserControl> 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 +{ + /// <summary> + /// Interaction logic for AnomaliesDialogView.xaml + /// </summary> + 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<InsightsChart> 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<InsightsChart> availableCharts, List<InsightsChart> selectedCharts) + { + OKText = "ANALYZE"; + SelectedCharts = new SelectedObjectCollection<InsightsChart>(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<InsightsChart>).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 Binary files differnew file mode 100644 index 000000000..a29355327 --- /dev/null +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/Images/detection.png 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 Binary files differnew file mode 100644 index 000000000..e5cc9e2dc --- /dev/null +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/Images/insight_big.png 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 Binary files differindex 1eb9b077f..542bd09db 100644 --- a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/Images/time.png +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/Images/time.png 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<MonitorValue> _values; + + private AnomaliesDetector() + { + _mlContext = new MLContext(); + } + + public static Task<AnomaliesDetector> FromMonitor(List<MonitorValue> monitorValues) + { + return Task.Factory.StartNew<AnomaliesDetector>(() => + { + AnomaliesDetector engine = new AnomaliesDetector(); + engine._values = monitorValues; + engine._view = engine._mlContext.Data.LoadFromEnumerable<MonitorValue>(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<MonitorSeriesPrediction>(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<MonitorValue> enumerableData = new List<MonitorValue>(); + 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<InsightsChart> 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<DateTime, double> DataSeries { get; set; } + public List<MonitorValue> MonitorValues { get; set; } public AnnotationCollection Annotations { get; set; } + public List<DetectedAnnomaly> Anomalies { get; set; } private bool _isVisible; public bool IsVisible @@ -51,6 +59,8 @@ namespace Tango.FSE.Insights.SciChart public InsightsChart(ObservableCollection<InsightsChart> chartsCollection) { + Anomalies = new List<DetectedAnnomaly>(); + MonitorValues = new List<MonitorValue>(); 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<DetectedAnnomaly> DetectAnnomalies(Action<int> progress = null) + { + List<DetectedAnnomaly> annomalies = new List<DetectedAnnomaly>(); + + 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<MonitorValue>(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<MonitorSeriesPrediction>(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<MonitorValue> enumerableData = new List<MonitorValue>(); + 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 @@ <?xml version="1.0" encoding="utf-8"?> <Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <Import Project="..\..\..\packages\Microsoft.ML.Mkl.Redist.1.5.1\build\netstandard2.0\Microsoft.ML.Mkl.Redist.props" Condition="Exists('..\..\..\packages\Microsoft.ML.Mkl.Redist.1.5.1\build\netstandard2.0\Microsoft.ML.Mkl.Redist.props')" /> + <Import Project="..\..\..\packages\Microsoft.ML.1.5.1\build\netstandard2.0\Microsoft.ML.props" Condition="Exists('..\..\..\packages\Microsoft.ML.1.5.1\build\netstandard2.0\Microsoft.ML.props')" /> + <Import Project="..\..\..\packages\Microsoft.ML.CpuMath.1.5.1\build\netstandard2.0\Microsoft.ML.CpuMath.props" Condition="Exists('..\..\..\packages\Microsoft.ML.CpuMath.1.5.1\build\netstandard2.0\Microsoft.ML.CpuMath.props')" /> <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> <PropertyGroup> <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> @@ -55,8 +58,36 @@ <Reference Include="MaterialDesignThemes.Wpf, Version=3.0.1.920, Culture=neutral, processorArchitecture=MSIL"> <HintPath>..\..\..\packages\MaterialDesignThemes.3.0.1\lib\net45\MaterialDesignThemes.Wpf.dll</HintPath> </Reference> - <Reference Include="Newtonsoft.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL"> - <HintPath>..\..\..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll</HintPath> + <Reference Include="Microsoft.ML.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL"> + <HintPath>..\..\..\packages\Microsoft.ML.1.5.1\lib\netstandard2.0\Microsoft.ML.Core.dll</HintPath> + </Reference> + <Reference Include="Microsoft.ML.CpuMath, Version=1.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL"> + <HintPath>..\..\..\packages\Microsoft.ML.CpuMath.1.5.1\lib\netstandard2.0\Microsoft.ML.CpuMath.dll</HintPath> + </Reference> + <Reference Include="Microsoft.ML.Data, Version=1.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL"> + <HintPath>..\..\..\packages\Microsoft.ML.1.5.1\lib\netstandard2.0\Microsoft.ML.Data.dll</HintPath> + </Reference> + <Reference Include="Microsoft.ML.DataView, Version=1.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL"> + <HintPath>..\..\..\packages\Microsoft.ML.DataView.1.5.1\lib\netstandard2.0\Microsoft.ML.DataView.dll</HintPath> + </Reference> + <Reference Include="Microsoft.ML.KMeansClustering, Version=1.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL"> + <HintPath>..\..\..\packages\Microsoft.ML.1.5.1\lib\netstandard2.0\Microsoft.ML.KMeansClustering.dll</HintPath> + </Reference> + <Reference Include="Microsoft.ML.PCA, Version=1.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL"> + <HintPath>..\..\..\packages\Microsoft.ML.1.5.1\lib\netstandard2.0\Microsoft.ML.PCA.dll</HintPath> + </Reference> + <Reference Include="Microsoft.ML.StandardTrainers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL"> + <HintPath>..\..\..\packages\Microsoft.ML.1.5.1\lib\netstandard2.0\Microsoft.ML.StandardTrainers.dll</HintPath> + </Reference> + <Reference Include="Microsoft.ML.TimeSeries, Version=1.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL"> + <HintPath>..\..\..\packages\Microsoft.ML.TimeSeries.1.5.1\lib\netstandard2.0\Microsoft.ML.TimeSeries.dll</HintPath> + </Reference> + <Reference Include="Microsoft.ML.Transforms, Version=1.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL"> + <HintPath>..\..\..\packages\Microsoft.ML.1.5.1\lib\netstandard2.0\Microsoft.ML.Transforms.dll</HintPath> + </Reference> + <Reference Include="Newtonsoft.Json, Version=12.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL"> + <HintPath>..\..\..\packages\Newtonsoft.Json.12.0.3\lib\net45\Newtonsoft.Json.dll</HintPath> + <Private>False</Private> </Reference> <Reference Include="SciChart.Charting, Version=6.2.1.13304, Culture=neutral, PublicKeyToken=b55dd9efe817e823, processorArchitecture=MSIL"> <SpecificVersion>False</SpecificVersion> @@ -87,8 +118,33 @@ <HintPath>..\..\..\Referenced Assemblies\SciChart\SciChart.Drawing.DirectX.dll</HintPath> </Reference> <Reference Include="System" /> + <Reference Include="System.Buffers, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL"> + <HintPath>..\..\..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll</HintPath> + </Reference> + <Reference Include="System.CodeDom, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL"> + <HintPath>..\..\..\packages\System.CodeDom.4.7.0\lib\net461\System.CodeDom.dll</HintPath> + </Reference> + <Reference Include="System.Collections.Immutable, Version=1.2.5.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> + <HintPath>..\..\..\packages\System.Collections.Immutable.1.7.1\lib\net461\System.Collections.Immutable.dll</HintPath> + </Reference> <Reference Include="System.ComponentModel.DataAnnotations" /> <Reference Include="System.Data" /> + <Reference Include="System.Memory, Version=4.0.1.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL"> + <HintPath>..\..\..\packages\System.Memory.4.5.4\lib\net461\System.Memory.dll</HintPath> + </Reference> + <Reference Include="System.Numerics" /> + <Reference Include="System.Numerics.Vectors, Version=4.1.4.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> + <HintPath>..\..\..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll</HintPath> + </Reference> + <Reference Include="System.Runtime.CompilerServices.Unsafe, Version=4.0.6.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> + <HintPath>..\..\..\packages\System.Runtime.CompilerServices.Unsafe.4.7.1\lib\net461\System.Runtime.CompilerServices.Unsafe.dll</HintPath> + </Reference> + <Reference Include="System.Threading.Channels, Version=4.0.2.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL"> + <HintPath>..\..\..\packages\System.Threading.Channels.4.7.1\lib\net461\System.Threading.Channels.dll</HintPath> + </Reference> + <Reference Include="System.Threading.Tasks.Extensions, Version=4.2.0.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL"> + <HintPath>..\..\..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll</HintPath> + </Reference> <Reference Include="System.Windows.Interactivity, Version=4.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL"> <HintPath>..\..\..\packages\ControlzEx.3.0.2.4\lib\net45\System.Windows.Interactivity.dll</HintPath> </Reference> @@ -107,6 +163,16 @@ </ItemGroup> <ItemGroup> <Compile Include="Contracts\IInsightsView.cs" /> + <Compile Include="Dialogs\AnomaliesDialogView.xaml.cs"> + <DependentUpon>AnomaliesDialogView.xaml</DependentUpon> + </Compile> + <Compile Include="Dialogs\AnomaliesDialogViewVM.cs" /> + <Compile Include="ML\AnomaliesDetectionProgress.cs" /> + <Compile Include="ML\AnomaliesDetector.cs" /> + <Compile Include="ML\AnomalyType.cs" /> + <Compile Include="ML\DetectedAnnomaly.cs" /> + <Compile Include="ML\MonitorSeriesPrediction.cs" /> + <Compile Include="ML\MonitorValue.cs" /> <Compile Include="Navigation\PeekLogsNavigationObject.cs" /> <Compile Include="SciChart\InsightsChart.cs" /> <Compile Include="SciChart\InsightsRubberBandXYZoomModifier.cs" /> @@ -202,6 +268,10 @@ <Generator>MSBuild:Compile</Generator> <SubType>Designer</SubType> </Page> + <Page Include="Dialogs\AnomaliesDialogView.xaml"> + <SubType>Designer</SubType> + <Generator>MSBuild:Compile</Generator> + </Page> <Page Include="Themes\Generic.xaml"> <Generator>MSBuild:Compile</Generator> <SubType>Designer</SubType> @@ -236,6 +306,12 @@ <ItemGroup> <Resource Include="Images\stand_by.png" /> </ItemGroup> + <ItemGroup> + <Resource Include="Images\insight_big.png" /> + </ItemGroup> + <ItemGroup> + <Resource Include="Images\detection.png" /> + </ItemGroup> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="..\..\..\packages\MaterialDesignThemes.3.0.1\build\MaterialDesignThemes.targets" Condition="Exists('..\..\..\packages\MaterialDesignThemes.3.0.1\build\MaterialDesignThemes.targets')" /> <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild"> @@ -243,5 +319,10 @@ <ErrorText>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}.</ErrorText> </PropertyGroup> <Error Condition="!Exists('..\..\..\packages\MaterialDesignThemes.3.0.1\build\MaterialDesignThemes.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\MaterialDesignThemes.3.0.1\build\MaterialDesignThemes.targets'))" /> + <Error Condition="!Exists('..\..\..\packages\Microsoft.ML.CpuMath.1.5.1\build\netstandard2.0\Microsoft.ML.CpuMath.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.ML.CpuMath.1.5.1\build\netstandard2.0\Microsoft.ML.CpuMath.props'))" /> + <Error Condition="!Exists('..\..\..\packages\Microsoft.ML.1.5.1\build\netstandard2.0\Microsoft.ML.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.ML.1.5.1\build\netstandard2.0\Microsoft.ML.props'))" /> + <Error Condition="!Exists('..\..\..\packages\Microsoft.ML.1.5.1\build\netstandard2.0\Microsoft.ML.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.ML.1.5.1\build\netstandard2.0\Microsoft.ML.targets'))" /> + <Error Condition="!Exists('..\..\..\packages\Microsoft.ML.Mkl.Redist.1.5.1\build\netstandard2.0\Microsoft.ML.Mkl.Redist.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.ML.Mkl.Redist.1.5.1\build\netstandard2.0\Microsoft.ML.Mkl.Redist.props'))" /> </Target> + <Import Project="..\..\..\packages\Microsoft.ML.1.5.1\build\netstandard2.0\Microsoft.ML.targets" Condition="Exists('..\..\..\packages\Microsoft.ML.1.5.1\build\netstandard2.0\Microsoft.ML.targets')" /> </Project>
\ 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 @@ </Image.Style> </Image> </DataTemplate> + + <DataTemplate x:Key="InsightAnomalyTemplate" DataType="{x:Type ML:DetectedAnnomaly}"> + <Border ToolTip="Spike Anomaly" Visibility="{Binding RelativeSource={RelativeSource AncestorType=views:InsightsView},Path=DataContext.DisplayAnomalies,Converter={StaticResource BooleanToVisibilityConverter}}"> + <material:PackIcon Kind="Spider" Foreground="{StaticResource FSE_ErrorBrush}" Width="20" Height="20" Margin="-10 -10 0 0" /> + </Border> + </DataTemplate> </ResourceDictionary>
\ 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<InsightsChart> _lastSelectedForDetection; + private List<TechMonitors> _defaultChartForAnomalies = new List<TechMonitors>() + { + TechMonitors.IncomingVoltage, + TechMonitors.ChillerTemperature, + TechMonitors.OverallTemperature, + TechMonitors.GasSensor, + TechMonitors.BlowerVoltage, + TechMonitors.HeadZone1Temperature + }; #region Properties public RelayCommand GetInsightsCommand { get; set; } - public ObservableCollection<InsightsChart> Charts { get; set; } + private ObservableCollection<InsightsChart> _charts; + public ObservableCollection<InsightsChart> Charts + { + get { return _charts; } + set { _charts = value; RaisePropertyChangedAuto(); } + } public ObservableCollection<InsightsChart> VisibleCharts { get; set; } + private List<AnomaliesDetectionProgress> _anomaliesDetectionProgresses; + public List<AnomaliesDetectionProgress> 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<InsightsReadyEvent> 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<InsightsChart>(); VisibleCharts = new ObservableCollection<InsightsChart>(); Annotations = new AnnotationCollection(); GetInsightsCommand = new RelayCommand(GetInsights, () => IsFree); PeekApplicationLogsCommand = new RelayCommand<InsightsReadyEvent>(PeekAnnotationApplicationLogs); PeekFirmwareLogsCommand = new RelayCommand<InsightsReadyEvent>(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<DateTime> GetDemoDates() { List<DateTime> dates = new List<DateTime>(); @@ -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}"> - <Grid IsEnabled="{Binding IsFree}"> <Grid> + <!--Actual Visible Charts--> <Grid> - <Grid Margin="5 15 5 0"> - <Grid> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="Auto" MinWidth="200" /> - <ColumnDefinition Width="*" /> - </Grid.ColumnDefinitions> - <Border Margin="5 5 0 5"> - <DockPanel> - <Menu DockPanel.Dock="Top" Margin="0 -10 0 0"> - <MenuItem Header="_File"></MenuItem> - <MenuItem Header="_View"> - <MenuItem Header="Display Annotations" IsCheckable="True" IsChecked="{Binding DisplayAnnotations}" /> - </MenuItem> - </Menu> - <Grid DockPanel.Dock="Top" Width="330" Margin="0 10 0 0" HorizontalAlignment="Center"> - <Polygon Stroke="{StaticResource FSE_PrimaryAccentBrush}" StrokeThickness="1" Stretch="Fill" Height="50" Points="0,15 15,0 285,0 300,15 285,30 15,30"></Polygon> - <TextBox VerticalAlignment="Center" Margin="30 0" material:HintAssist.Hint="Find Monitor" Text="{Binding MonitorsFilter,UpdateSourceTrigger=PropertyChanged,Delay=200}"></TextBox> - </Grid> - <ScrollViewer Margin="0 20 0 0" HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto"> - <ItemsControl ItemsSource="{Binding Charts}"> - <ItemsControl.ItemTemplate> - <DataTemplate> - <ToggleButton Margin="5" IsChecked="{Binding IsVisible}"> - <ToggleButton.Template> - <ControlTemplate TargetType="ToggleButton"> - <Grid> - <Polygon x:Name="poly" Fill="{StaticResource FSE_PrimaryBackgroundDarkBrush}" StrokeThickness="1" Stroke="{StaticResource FSE_PrimaryBackgroundLightBrush}" Stretch="Fill" Height="40" Points="0,15 15,0 285,0 300,15 285,30 15,30"></Polygon> - <TextBlock Text="{Binding Description}" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="{StaticResource FSE_SmallFontSize}"></TextBlock> - </Grid> - <ControlTemplate.Triggers> - <Trigger Property="IsChecked" Value="True"> - <Setter TargetName="poly" Property="Fill" Value="{StaticResource FSE_PrimaryAccentBrush}"></Setter> - </Trigger> - <Trigger Property="IsMouseOver" Value="True"> - <Setter TargetName="poly" Property="Stroke" Value="{StaticResource FSE_PrimaryAccentBrush}"></Setter> - </Trigger> - </ControlTemplate.Triggers> - </ControlTemplate> - </ToggleButton.Template> - </ToggleButton> - </DataTemplate> - </ItemsControl.ItemTemplate> - </ItemsControl> - </ScrollViewer> - </DockPanel> - </Border> - <Grid Margin="10 0 0 0" Grid.Column="1" > - <ItemsControl x:Name="chartsItemsControl" ItemsSource="{Binding VisibleCharts}"> - <ItemsControl.ItemsPanel> - <ItemsPanelTemplate> - <UniformGrid IsItemsHost="True" Columns="1" /> - </ItemsPanelTemplate> - </ItemsControl.ItemsPanel> - <ItemsControl.ItemTemplate> - <DataTemplate> - <Grid Loaded="SciChartSurface_Loaded"> - <Border BorderBrush="{StaticResource FSE_PrimaryBackgroundLightBrush}" BorderThickness="1" Margin="0 5" CornerRadius="5" Padding="1"> - <s:SciChartSurface Annotations="{Binding Annotations}" Background="{StaticResource FSE_PrimaryBackgroundDarkBrush}" Visibility="{Binding IsVisible,Converter={StaticResource BooleanToVisibilityConverter}}" s:SciChartGroup.VerticalChartGroup="SyncedChartsSameAxisSizes" RenderableSeries="{s:SeriesBinding RenderableSeries}"> - <s:SciChartSurface.GridLinesPanelStyle> - <Style TargetType="s:GridLinesPanel"> - <Setter Property="Background" Value="#191919"></Setter> - </Style> - </s:SciChartSurface.GridLinesPanelStyle> + <ItemsControl x:Name="chartsItemsControl" ItemsSource="{Binding VisibleCharts}"> + <ItemsControl.ItemsPanel> + <ItemsPanelTemplate> + <UniformGrid IsItemsHost="True" Columns="1" /> + </ItemsPanelTemplate> + </ItemsControl.ItemsPanel> + <ItemsControl.ItemTemplate> + <DataTemplate> + <Grid Loaded="SciChartSurface_Loaded"> + <Border BorderBrush="{StaticResource FSE_BorderBrush}" BorderThickness="0 1 0 1" Margin="0 2" CornerRadius="0" Padding="0"> + <s:SciChartSurface Annotations="{Binding Annotations}" Background="{StaticResource FSE_PrimaryBackgroundDarkBrush}" Visibility="{Binding IsVisible,Converter={StaticResource BooleanToVisibilityConverter}}" s:SciChartGroup.VerticalChartGroup="SyncedChartsSameAxisSizes" RenderableSeries="{s:SeriesBinding RenderableSeries}"> + <s:SciChartSurface.GridLinesPanelStyle> + <Style TargetType="s:GridLinesPanel"> + <Setter Property="Background" Value="#191919"></Setter> + </Style> + </s:SciChartSurface.GridLinesPanelStyle> - <!-- Define X and Y Axis. Optional bands give a cool look and feel for minimal performance impact --> - <s:SciChartSurface.YAxis> - <s:NumericAxis GrowBy="0.1,0.1" MinorsPerMajor="2" VisibleRange="0,1"/> - </s:SciChartSurface.YAxis> - <s:SciChartSurface.XAxis> - <s:DateTimeAxis Visibility="{Binding IsLast,Converter={StaticResource BooleanToVisibilityConverter}}" DrawMajorBands="True" VisibleRange="{Binding RelativeSource={RelativeSource AncestorType=UserControl},Path=DataContext.VisibleChartsRange, Mode=TwoWay}"/> - </s:SciChartSurface.XAxis> - <s:SciChartSurface.ChartModifier> + <!-- Define X and Y Axis. Optional bands give a cool look and feel for minimal performance impact --> + <s:SciChartSurface.YAxis> + <s:NumericAxis AxisBandsFill="{StaticResource FSE_PrimaryBackgroundDarkColor}" GrowBy="0.1,0.1" MinorsPerMajor="2" VisibleRange="0,1"/> + </s:SciChartSurface.YAxis> + <s:SciChartSurface.XAxis> + <s:DateTimeAxis AxisBandsFill="{StaticResource FSE_PrimaryBackgroundDarkColor}" Visibility="{Binding IsLast,Converter={StaticResource BooleanToVisibilityConverter}}" DrawMajorBands="True" VisibleRange="{Binding RelativeSource={RelativeSource AncestorType=UserControl},Path=DataContext.VisibleChartsRange, Mode=TwoWay}"/> + </s:SciChartSurface.XAxis> + <s:SciChartSurface.ChartModifier> - <!-- Whats going on here? --> - <!-- We share the mouse events by using MouseManager.MouseEventGroup="..." --> - <!-- We ensure modifiers receive events even when another has handled by setting ReceiveHandledEvents=true --> - <!-- We bind both charts XAxis to shared property on the viewmodel to ensure they stay in sync --> - <s:ModifierGroup s:MouseManager.MouseEventGroup="myCustomGroup"> - <insights:InsightsRubberBandXYZoomModifier IsXAxisOnly="True" ReceiveHandledEvents="True" /> - <insights:InsightsZoomPanModifier ReceiveHandledEvents="True" XyDirection="XDirection" /> - <!--<s:MouseWheelZoomModifier/>--> - <!--<s:RolloverModifier ReceiveHandledEvents="True"/>--> - <s:CursorModifier ReceiveHandledEvents="True"/> - <!--<s:YAxisDragModifier Tag="FirstYAxis"/>--> - <!--<s:XAxisDragModifier />--> - <s:ZoomExtentsModifier/> - </s:ModifierGroup> - </s:SciChartSurface.ChartModifier> - </s:SciChartSurface> - </Border> - </Grid> - </DataTemplate> - </ItemsControl.ItemTemplate> - </ItemsControl> - </Grid> + <!-- Whats going on here? --> + <!-- We share the mouse events by using MouseManager.MouseEventGroup="..." --> + <!-- We ensure modifiers receive events even when another has handled by setting ReceiveHandledEvents=true --> + <!-- We bind both charts XAxis to shared property on the viewmodel to ensure they stay in sync --> + <s:ModifierGroup s:MouseManager.MouseEventGroup="myCustomGroup"> + <insights:InsightsRubberBandXYZoomModifier IsXAxisOnly="True" ReceiveHandledEvents="True" /> + <insights:InsightsZoomPanModifier ReceiveHandledEvents="True" XyDirection="XDirection" /> + <!--<s:MouseWheelZoomModifier/>--> + <!--<s:RolloverModifier ReceiveHandledEvents="True"/>--> + <s:CursorModifier ReceiveHandledEvents="True"/> + <!--<s:YAxisDragModifier Tag="FirstYAxis"/>--> + <!--<s:XAxisDragModifier />--> + <s:ZoomExtentsModifier/> + </s:ModifierGroup> + </s:SciChartSurface.ChartModifier> + </s:SciChartSurface> + </Border> + </Grid> + </DataTemplate> + </ItemsControl.ItemTemplate> + </ItemsControl> + </Grid> - <Grid x:Name="gridAnnotations" Loaded="SciChartSurface_Loaded" Grid.Column="1" Margin="10 5 0 25" IsHitTestVisible="True"> - <Grid.Style> - <Style TargetType="Grid"> - <Setter Property="Visibility" Value="Hidden"></Setter> - <Style.Triggers> - <MultiDataTrigger> - <MultiDataTrigger.Conditions> - <Condition Binding="{Binding DisplayAnnotations}" Value="True" /> - <Condition Binding="{Binding VisibleCharts.Count,Converter={StaticResource GreaterThanToBooleanConverter}}" Value="True" /> - </MultiDataTrigger.Conditions> - <Setter Property="Visibility" Value="Visible"></Setter> - </MultiDataTrigger> - </Style.Triggers> - </Style> - </Grid.Style> - <s:SciChartSurface Annotations="{Binding Annotations}" Opacity="1" Background="Transparent" GridLinesPanelStyle="{x:Null}" s:SciChartGroup.VerticalChartGroup="SyncedChartsSameAxisSizes"> + <!--Welcome / Busy--> + <Grid> + <Grid.Style> + <Style TargetType="Grid"> + <Setter Property="Visibility" Value="Collapsed"></Setter> + <Style.Triggers> + <DataTrigger Binding="{Binding VisibleCharts.Count}" Value="0"> + <Setter Property="Visibility" Value="Visible"></Setter> + </DataTrigger> + <DataTrigger Binding="{Binding IsFree}" Value="False"> + <Setter Property="Visibility" Value="Visible"></Setter> + </DataTrigger> + <DataTrigger Binding="{Binding InsightsPackage}" Value="{x:Null}"> + <Setter Property="Visibility" Value="Visible"></Setter> + </DataTrigger> + <DataTrigger Binding="{Binding IsDetecting}" Value="True"> + <Setter Property="Visibility" Value="Collapsed"></Setter> + </DataTrigger> + </Style.Triggers> + </Style> + </Grid.Style> + <s:SciChartSurface Background="{StaticResource FSE_PrimaryBackgroundDarkBrush}"> + <s:SciChartSurface.GridLinesPanelStyle> + <Style TargetType="s:GridLinesPanel"> + <Setter Property="Background" Value="#191919"></Setter> + </Style> + </s:SciChartSurface.GridLinesPanelStyle> - <!-- Define X and Y Axis. Optional bands give a cool look and feel for minimal performance impact --> - <s:SciChartSurface.YAxis> - <s:NumericAxis Visibility="Hidden" DrawMajorBands="False" DrawMajorGridLines="False" DrawMajorTicks="False" DrawMinorGridLines="False" DrawMinorTicks="False" GrowBy="0.1,0.1" MinorsPerMajor="2" VisibleRange="0,1"/> - </s:SciChartSurface.YAxis> - <s:SciChartSurface.XAxis> - <s:DateTimeAxis Visibility="Visible" FontSize="0.1" Background="Transparent" Foreground="Transparent" DrawMajorBands="False" DrawMajorGridLines="False" DrawMajorTicks="False" DrawMinorGridLines="False" DrawMinorTicks="False" VisibleRange="{Binding VisibleChartsRange, Mode=TwoWay}"/> - </s:SciChartSurface.XAxis> - <s:SciChartSurface.ChartModifier> + <!-- Define X and Y Axis. Optional bands give a cool look and feel for minimal performance impact --> + <s:SciChartSurface.YAxis> + <s:NumericAxis GrowBy="0.1,0.1" MinorsPerMajor="2" AxisBandsFill="{StaticResource FSE_PrimaryBackgroundDarkColor}" VisibleRange="0,1"/> + </s:SciChartSurface.YAxis> + <s:SciChartSurface.XAxis> + <s:DateTimeAxis DrawMajorBands="True" AxisBandsFill="{StaticResource FSE_PrimaryBackgroundDarkColor}" /> + </s:SciChartSurface.XAxis> + </s:SciChartSurface> - <!-- Whats going on here? --> - <!-- We share the mouse events by using MouseManager.MouseEventGroup="..." --> - <!-- We ensure modifiers receive events even when another has handled by setting ReceiveHandledEvents=true --> - <!-- We bind both charts XAxis to shared property on the viewmodel to ensure they stay in sync --> - <s:ModifierGroup s:MouseManager.MouseEventGroup="myCustomGroup"> - <insights:InsightsRubberBandXYZoomModifier RubberBandFill="Transparent" RubberBandStroke="Transparent" IsXAxisOnly="True" ReceiveHandledEvents="True" /> - <insights:InsightsZoomPanModifier ReceiveHandledEvents="True" XyDirection="XDirection" /> - <s:CursorModifier ReceiveHandledEvents="True"/> - <!--<s:MouseWheelZoomModifier/>--> - <!--<s:RolloverModifier ReceiveHandledEvents="True"/>--> - <!--<s:CursorModifier ReceiveHandledEvents="True"/>--> - <!--<s:YAxisDragModifier Tag="FirstYAxis"/>--> - <!--<s:XAxisDragModifier />--> - <s:ZoomExtentsModifier/> - </s:ModifierGroup> - </s:SciChartSurface.ChartModifier> - </s:SciChartSurface> - </Grid> + <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center" Height="420"> + <Image Stretch="Uniform" RenderOptions.BitmapScalingMode="Fant" Source="../Images/insight_big.png" Opacity="0.5" Width="128" /> + <TextBlock Margin="0 10 0 0" FontSize="70" HorizontalAlignment="Center" FontFamily="{StaticResource hand}" Foreground="{StaticResource FSE_PrimaryAccentBrush}">Insights</TextBlock> + <TextBlock Visibility="{Binding InsightsPackageAvailable,Converter={StaticResource BooleanToVisibilityInverseConverter}}" Margin="0 10 0 0" TextAlignment="Center" MaxWidth="700" TextWrapping="Wrap" LineHeight="20" Foreground="{StaticResource FSE_GrayBrush}"> + <Run>Welcome to the Insights module</Run> + <Run> + This module allows you to collect, view and analyze important machine data using interactive charts and annotations. + </Run> + <LineBreak/> + <LineBreak/> + <Run>Use the search panel above to select target dates for data collection, then press 'Get Insights'.</Run> + <LineBreak/> + <LineBreak/> + <Run>Toggle the settings panel on the left to configure your workspace.</Run> + <LineBreak/> + <LineBreak/> + <Run> + The workspace supports panning and zooming. + Hold and drag the mouse to pan the charts, or hold the Ctrl key to zoom. + </Run> + </TextBlock> - <ItemsControl ItemsSource="{Binding VisibleCharts}" Grid.Column="1" Margin="10 0 0 0"> - <ItemsControl.ItemsPanel> - <ItemsPanelTemplate> - <UniformGrid IsItemsHost="True" Columns="1" /> - </ItemsPanelTemplate> - </ItemsControl.ItemsPanel> - <ItemsControl.ItemTemplate> - <DataTemplate> - <Grid> - <Grid HorizontalAlignment="Left" VerticalAlignment="Top" Width="260" Height="30" Margin="3 8 0 0"> - <Polygon Points="1,0 300,0 240,40 0,40 0,1" Fill="#3800AFFF" Stretch="Fill" Stroke="Black" /> - <TextBlock Margin="30 0 0 0" Text="{Binding Description}" HorizontalAlignment="Left" VerticalAlignment="Center" FontSize="{StaticResource FSE_SmallFontSize}"></TextBlock> - <controls:ToggleIconButton CheckedIcon="Close" IsChecked="{Binding IsVisible}" HorizontalAlignment="Left" VerticalAlignment="Center" Width="16" Height="16" Margin="5 -2 0 0" /> - </Grid> - </Grid> - </DataTemplate> - </ItemsControl.ItemTemplate> - </ItemsControl> - </Grid> - </Grid> + <StackPanel Visibility="{Binding InsightsPackageAvailable,Converter={StaticResource BooleanToVisibilityConverter}}" > + <TextBlock Margin="0 10 0 0" FontSize="20" HorizontalAlignment="Center" Foreground="{StaticResource FSE_SuccessBrush}">Insights available !</TextBlock> + <TextBlock Margin="0 10 0 0" HorizontalAlignment="Center">Use the pane on the left to select monitors to display.</TextBlock> + </StackPanel> + </StackPanel> </Grid> - <Grid VerticalAlignment="Top"> + <!--Annotations--> + <Grid x:Name="gridAnnotations" Loaded="SciChartSurface_Loaded" Grid.Column="1" Margin="10 5 0 25" IsHitTestVisible="True"> <Grid.Style> <Style TargetType="Grid"> - <Setter Property="Margin" Value="0 20 0 0"></Setter> + <Setter Property="Visibility" Value="Hidden"></Setter> + <Style.Triggers> + <MultiDataTrigger> + <MultiDataTrigger.Conditions> + <Condition Binding="{Binding DisplayAnnotations}" Value="True" /> + <Condition Binding="{Binding VisibleCharts.Count,Converter={StaticResource GreaterThanToBooleanConverter}}" Value="True" /> + </MultiDataTrigger.Conditions> + <Setter Property="Visibility" Value="Visible"></Setter> + </MultiDataTrigger> + </Style.Triggers> + </Style> + </Grid.Style> + <s:SciChartSurface Annotations="{Binding Annotations}" Opacity="1" Background="Transparent" GridLinesPanelStyle="{x:Null}" s:SciChartGroup.VerticalChartGroup="SyncedChartsSameAxisSizes"> + + <!-- Define X and Y Axis. Optional bands give a cool look and feel for minimal performance impact --> + <s:SciChartSurface.YAxis> + <s:NumericAxis Visibility="Hidden" DrawMajorBands="False" DrawMajorGridLines="False" DrawMajorTicks="False" DrawMinorGridLines="False" DrawMinorTicks="False" GrowBy="0.1,0.1" MinorsPerMajor="2" VisibleRange="0,1"/> + </s:SciChartSurface.YAxis> + <s:SciChartSurface.XAxis> + <s:DateTimeAxis Visibility="Visible" FontSize="0.1" Background="Transparent" Foreground="Transparent" DrawMajorBands="False" DrawMajorGridLines="False" DrawMajorTicks="False" DrawMinorGridLines="False" DrawMinorTicks="False" VisibleRange="{Binding VisibleChartsRange, Mode=TwoWay}"/> + </s:SciChartSurface.XAxis> + <s:SciChartSurface.ChartModifier> + + <!-- Whats going on here? --> + <!-- We share the mouse events by using MouseManager.MouseEventGroup="..." --> + <!-- We ensure modifiers receive events even when another has handled by setting ReceiveHandledEvents=true --> + <!-- We bind both charts XAxis to shared property on the viewmodel to ensure they stay in sync --> + <s:ModifierGroup s:MouseManager.MouseEventGroup="myCustomGroup"> + <insights:InsightsRubberBandXYZoomModifier RubberBandFill="Transparent" RubberBandStroke="Transparent" IsXAxisOnly="True" ReceiveHandledEvents="True" /> + <insights:InsightsZoomPanModifier ReceiveHandledEvents="True" XyDirection="XDirection" /> + <s:CursorModifier ReceiveHandledEvents="True"/> + <!--<s:MouseWheelZoomModifier/>--> + <!--<s:RolloverModifier ReceiveHandledEvents="True"/>--> + <!--<s:CursorModifier ReceiveHandledEvents="True"/>--> + <!--<s:YAxisDragModifier Tag="FirstYAxis"/>--> + <!--<s:XAxisDragModifier />--> + <s:ZoomExtentsModifier/> + </s:ModifierGroup> + </s:SciChartSurface.ChartModifier> + </s:SciChartSurface> + </Grid> + + <!--Charts Labels--> + <ItemsControl ItemsSource="{Binding VisibleCharts}" Margin="0 0 0 0"> + <ItemsControl.ItemsPanel> + <ItemsPanelTemplate> + <UniformGrid IsItemsHost="True" Columns="1" /> + </ItemsPanelTemplate> + </ItemsControl.ItemsPanel> + <ItemsControl.ItemTemplate> + <DataTemplate> + <Grid> + <Grid HorizontalAlignment="Left" VerticalAlignment="Top" Width="320" Height="30" Margin="0 3 0 0"> + <Polygon Points="0,0 300,0 240,40 0,40" Fill="#3800AFFF" Stretch="Fill" Stroke="Black" /> + <DockPanel Margin="45 0 0 0" HorizontalAlignment="Left"> + <controls:ToggleIconButton CheckedIcon="Close" IsChecked="{Binding IsVisible}" VerticalAlignment="Center" Width="16" Height="16" Margin="0 -2 0 0" /> + <TextBlock Margin="5 0 0 0" Text="{Binding Description}" VerticalAlignment="Center" FontSize="{StaticResource FSE_SmallFontSize}"></TextBlock> + </DockPanel> + </Grid> + </Grid> + </DataTemplate> + </ItemsControl.ItemTemplate> + </ItemsControl> + + <!--Left Panel--> + <Border HorizontalAlignment="Left" Background="{StaticResource FSE_PrimaryBackgroundMidBrush}" BorderThickness="0 0 1 0" BorderBrush="{StaticResource FSE_BorderBrush}"> + <Border.Style> + <Style TargetType="Border"> + <Setter Property="Margin" Value="-350 0 0 0"></Setter> <Style.Triggers> - <DataTrigger Binding="{Binding ElementName=chkSearchBar,Path=IsChecked}" Value="True"> + <DataTrigger Binding="{Binding ElementName=chkSettingsBar,Path=IsChecked}" Value="True"> <DataTrigger.EnterActions> <BeginStoryboard> <Storyboard> - <ThicknessAnimation Storyboard.TargetProperty="Margin" To="0 20 0 0" Duration="00:00:0.2" /> + <ThicknessAnimation Storyboard.TargetProperty="Margin" To="0 0 0 0" Duration="00:00:0.2" /> </Storyboard> </BeginStoryboard> </DataTrigger.EnterActions> <DataTrigger.ExitActions> <BeginStoryboard> <Storyboard> - <ThicknessAnimation Storyboard.TargetProperty="Margin" To="0 -160 0 0" Duration="00:00:0.2" /> + <ThicknessAnimation Storyboard.TargetProperty="Margin" To="-350 0 0 0" Duration="00:00:0.2" /> </Storyboard> </BeginStoryboard> </DataTrigger.ExitActions> </DataTrigger> </Style.Triggers> </Style> - </Grid.Style> - <Ellipse Fill="{StaticResource FSE_PrimaryBackgroundMidBrush}" Width="1500" Height="560" VerticalAlignment="Top" HorizontalAlignment="Center" Margin="0 -380 0 0" Opacity="0.9" StrokeThickness="1" Stroke="{StaticResource FSE_GrayBrush}" /> - <Grid DockPanel.Dock="Top" Panel.ZIndex="100"> - <StackPanel> - <DockPanel HorizontalAlignment="Center" VerticalAlignment="Top"> - <StackPanel Orientation="Horizontal" DockPanel.Dock="Left" VerticalAlignment="Bottom" Margin="0 0 0 -20"> - <Grid VerticalAlignment="Bottom" Margin="0 0 20 14"> - <StackPanel> - <Grid Margin="-300 -70 0 0" Width="200"> - <Polygon Stroke="{StaticResource FSE_PrimaryAccentBrush}" StrokeThickness="1" Stretch="Fill" Height="40" Points="0,15 15,0 285,0 300,15 285,30 15,30"></Polygon> - <material:TimePicker SelectedTime="{Binding StartTime}" Is24Hours="True" VerticalAlignment="Center" Width="150" material:HintAssist.Hint="Time (00:00:00)" /> - </Grid> - <Grid Width="250"> - <Polygon Stroke="{StaticResource FSE_PrimaryAccentBrush}" StrokeThickness="1" Stretch="Fill" Height="50" Points="0,15 15,0 285,0 300,15 285,30 15,30"></Polygon> - <DatePicker DisplayDateStart="{Binding MinDate}" DisplayDateEnd="{Binding MaxDate}" SelectedDate="{Binding StartDate}" VerticalAlignment="Center" Width="200" material:HintAssist.Hint="Start Date" /> - </Grid> - </StackPanel> - </Grid> - <Image RenderOptions.BitmapScalingMode="Fant" RenderTransformOrigin="0.5,0.5" Source="../Images/arrow_right.png" DockPanel.Dock="Left" Stretch="Fill" Height="80" Width="100" VerticalAlignment="Bottom"> - <Image.RenderTransform> - <RotateTransform Angle="180"/> - </Image.RenderTransform> - </Image> - </StackPanel> - <StackPanel DockPanel.Dock="Right" VerticalAlignment="Bottom" Orientation="Horizontal" Margin="0 0 0 -20"> - <Image RenderOptions.BitmapScalingMode="Fant" Source="../Images/arrow_right.png" DockPanel.Dock="Right" Stretch="Fill" Height="80" Width="100" VerticalAlignment="Bottom" /> - <Grid VerticalAlignment="Bottom" Margin="20 0 0 14"> - <StackPanel> - <Grid Margin="0 -70 -300 0" Width="200"> - <Polygon Stroke="{StaticResource FSE_PrimaryAccentBrush}" StrokeThickness="1" Stretch="Fill" Height="40" Points="0,15 15,0 285,0 300,15 285,30 15,30"></Polygon> - <material:TimePicker SelectedTime="{Binding EndTime}" Is24Hours="True" VerticalAlignment="Center" Width="150" material:HintAssist.Hint="End Time (All Day)" /> - </Grid> - <Grid Width="250"> - <Polygon Stroke="{StaticResource FSE_PrimaryAccentBrush}" StrokeThickness="1" Stretch="Fill" Height="50" Points="0,15 15,0 285,0 300,15 285,30 15,30"></Polygon> - <DatePicker DisplayDateStart="{Binding MinDate}" DisplayDateEnd="{Binding MaxDate}" SelectedDate="{Binding EndDate}" VerticalAlignment="Center" Width="200" material:HintAssist.Hint="End Date (Today)" /> - </Grid> - </StackPanel> - </Grid> - </StackPanel> - <Image RenderOptions.BitmapScalingMode="Fant" Margin="40 0" Source="../Images/time.png" Stretch="Uniform" Width="100" /> - </DockPanel> - <Button Command="{Binding GetInsightsCommand}" HorizontalAlignment="Center" Width="220" Style="{StaticResource FSE_Button_Polygon}" Height="40" FontSize="14" Margin="0 20 0 0" IsEnabled="{Binding MachineProvider.IsPPCAvailable}">Get Insights</Button> - </StackPanel> + </Border.Style> + <Grid Width="350" ClipToBounds="False"> + <DockPanel> + <Menu DockPanel.Dock="Top" Margin="0 0 0 0" Background="{StaticResource FSE_PrimaryBackgroundMidBrush}"> + <MenuItem Header="_File"></MenuItem> + <MenuItem Header="_Tools"> + <MenuItem Header="Display Annotations" IsCheckable="True" IsChecked="{Binding DisplayAnnotations}" /> + <MenuItem Header="Display Anomalies" IsCheckable="True" IsChecked="{Binding DisplayAnomalies}" /> + <Separator/> + <MenuItem Header="Detect Anomalies" Command="{Binding AnalyzeCommand}"> + <MenuItem.Icon> + <material:PackIcon Kind="Spider" Foreground="{StaticResource FSE_ErrorBrush}" /> + </MenuItem.Icon> + </MenuItem> + </MenuItem> + </Menu> + <Grid DockPanel.Dock="Top" Width="330" Margin="0 10 0 0" HorizontalAlignment="Center"> + <Polygon Stroke="{StaticResource FSE_PrimaryAccentBrush}" StrokeThickness="1" Stretch="Fill" Height="50" Points="0,15 15,0 285,0 300,15 285,30 15,30"></Polygon> + <TextBox VerticalAlignment="Center" Margin="30 0" material:HintAssist.Hint="Find Monitor" Text="{Binding MonitorsFilter,UpdateSourceTrigger=PropertyChanged,Delay=200}"></TextBox> + </Grid> + <ScrollViewer Margin="0 20 0 0" HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto"> + <ItemsControl ItemsSource="{Binding Charts}"> + <ItemsControl.ItemTemplate> + <DataTemplate> + <ToggleButton Margin="5" IsChecked="{Binding IsVisible}"> + <ToggleButton.Template> + <ControlTemplate TargetType="ToggleButton"> + <Grid> + <Polygon x:Name="poly" Fill="{StaticResource FSE_PrimaryBackgroundDarkBrush}" StrokeThickness="1" Stroke="{StaticResource FSE_PrimaryBackgroundLightBrush}" Stretch="Fill" Height="40" Points="0,15 15,0 285,0 300,15 285,30 15,30"></Polygon> + <TextBlock Text="{Binding Description}" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="{StaticResource FSE_SmallFontSize}"></TextBlock> + </Grid> + <ControlTemplate.Triggers> + <Trigger Property="IsChecked" Value="True"> + <Setter TargetName="poly" Property="Fill" Value="{StaticResource FSE_PrimaryAccentBrush}"></Setter> + </Trigger> + <Trigger Property="IsMouseOver" Value="True"> + <Setter TargetName="poly" Property="Stroke" Value="{StaticResource FSE_PrimaryAccentBrush}"></Setter> + </Trigger> + </ControlTemplate.Triggers> + </ControlTemplate> + </ToggleButton.Template> + </ToggleButton> + </DataTemplate> + </ItemsControl.ItemTemplate> + </ItemsControl> + </ScrollViewer> + </DockPanel> + + <Grid HorizontalAlignment="Right" Margin="0 0 -30 0" VerticalAlignment="Center" Width="30" Height="350" ClipToBounds="False"> + <Grid ClipToBounds="True"> + <Polygon Fill="{StaticResource FSE_PrimaryBackgroundMidBrush}" Width="95" Margin="-65 0 0 0" Points="0,0 100,30 100,320 0,350" Stroke="{StaticResource FSE_BorderBrush}" Stretch="Fill"></Polygon> + </Grid> + <controls:ToggleIconButton x:Name="chkSettingsBar" ToolTip="Toggle settings" Cursor="Hand" CheckedIcon="ChevronLeftCircle" UncheckedIcon="ChevronRightCircle" Width="32" Height="32" VerticalAlignment="Center" HorizontalAlignment="Right" Margin="0 0 -15 0" IsChecked="{Binding IsSettingsBarOpened}" /> + </Grid> </Grid> + </Border> + </Grid> - <controls:ToggleIconButton x:Name="chkSearchBar" Cursor="Hand" CheckedIcon="ChevronUpCircle" UncheckedIcon="ChevronDownCircle" Width="32" Height="32" VerticalAlignment="Bottom" Margin="0 0 0 -15" IsChecked="{Binding IsSearchBarOpened}" /> + <!--Top Panel--> + <Grid VerticalAlignment="Top"> + <Grid.Style> + <Style TargetType="Grid"> + <Setter Property="Margin" Value="0 20 0 0"></Setter> + <Style.Triggers> + <DataTrigger Binding="{Binding ElementName=chkSearchBar,Path=IsChecked}" Value="True"> + <DataTrigger.EnterActions> + <BeginStoryboard> + <Storyboard> + <ThicknessAnimation Storyboard.TargetProperty="Margin" To="0 20 0 0" Duration="00:00:0.2" /> + </Storyboard> + </BeginStoryboard> + </DataTrigger.EnterActions> + <DataTrigger.ExitActions> + <BeginStoryboard> + <Storyboard> + <ThicknessAnimation Storyboard.TargetProperty="Margin" To="0 -160 0 0" Duration="00:00:0.2" /> + </Storyboard> + </BeginStoryboard> + </DataTrigger.ExitActions> + </DataTrigger> + </Style.Triggers> + </Style> + </Grid.Style> + <Polygon Fill="{StaticResource FSE_PrimaryBackgroundMidBrush}" Stretch="None" Width="1400" Height="220" Points="0,0 200,200 1200,200 1400,0" HorizontalAlignment="Center" Margin="-10 -20 0 0" Opacity="0.9" StrokeThickness="1" Stroke="{StaticResource FSE_GrayBrush}" /> + <Grid DockPanel.Dock="Top" Panel.ZIndex="100"> + <StackPanel> + <DockPanel HorizontalAlignment="Center" VerticalAlignment="Top"> + <StackPanel Orientation="Horizontal" DockPanel.Dock="Left" VerticalAlignment="Bottom" Margin="0 0 0 -20"> + <Grid VerticalAlignment="Bottom" Margin="0 0 20 14"> + <StackPanel> + <Grid Margin="-300 -70 0 0" Width="200"> + <Polygon Stroke="{StaticResource FSE_PrimaryAccentBrush}" StrokeThickness="1" Stretch="Fill" Height="40" Points="0,15 15,0 285,0 300,15 285,30 15,30"></Polygon> + <material:TimePicker SelectedTime="{Binding StartTime}" Is24Hours="True" VerticalAlignment="Center" Width="150" material:HintAssist.Hint="Time (00:00:00)" /> + </Grid> + <Grid Width="250"> + <Polygon Stroke="{StaticResource FSE_PrimaryAccentBrush}" StrokeThickness="1" Stretch="Fill" Height="50" Points="0,15 15,0 285,0 300,15 285,30 15,30"></Polygon> + <DatePicker DisplayDateStart="{Binding MinDate}" DisplayDateEnd="{Binding MaxDate}" SelectedDate="{Binding StartDate}" VerticalAlignment="Center" Width="200" material:HintAssist.Hint="Start Date" /> + </Grid> + </StackPanel> + </Grid> + <Image RenderOptions.BitmapScalingMode="Fant" RenderTransformOrigin="0.5,0.5" Source="../Images/arrow_right.png" DockPanel.Dock="Left" Stretch="Fill" Height="80" Width="100" VerticalAlignment="Bottom"> + <Image.RenderTransform> + <RotateTransform Angle="180"/> + </Image.RenderTransform> + </Image> + </StackPanel> + <StackPanel DockPanel.Dock="Right" VerticalAlignment="Bottom" Orientation="Horizontal" Margin="0 0 0 -20"> + <Image RenderOptions.BitmapScalingMode="Fant" Source="../Images/arrow_right.png" DockPanel.Dock="Right" Stretch="Fill" Height="80" Width="100" VerticalAlignment="Bottom" /> + <Grid VerticalAlignment="Bottom" Margin="20 0 0 14"> + <StackPanel> + <Grid Margin="0 -70 -300 0" Width="200"> + <Polygon Stroke="{StaticResource FSE_PrimaryAccentBrush}" StrokeThickness="1" Stretch="Fill" Height="40" Points="0,15 15,0 285,0 300,15 285,30 15,30"></Polygon> + <material:TimePicker SelectedTime="{Binding EndTime}" Is24Hours="True" VerticalAlignment="Center" Width="150" material:HintAssist.Hint="End Time (All Day)" /> + </Grid> + <Grid Width="250"> + <Polygon Stroke="{StaticResource FSE_PrimaryAccentBrush}" StrokeThickness="1" Stretch="Fill" Height="50" Points="0,15 15,0 285,0 300,15 285,30 15,30"></Polygon> + <DatePicker DisplayDateStart="{Binding MinDate}" DisplayDateEnd="{Binding MaxDate}" SelectedDate="{Binding EndDate}" VerticalAlignment="Center" Width="200" material:HintAssist.Hint="End Date (Today)" /> + </Grid> + </StackPanel> + </Grid> + </StackPanel> + <Image RenderOptions.BitmapScalingMode="Fant" Margin="40 0" Source="../Images/time.png" Stretch="Uniform" Width="100" /> + </DockPanel> + <Button Command="{Binding GetInsightsCommand}" HorizontalAlignment="Center" Width="220" Style="{StaticResource FSE_Button_Polygon}" Height="40" FontSize="14" Margin="0 20 0 0" IsEnabled="{Binding MachineProvider.IsPPCAvailable}">Get Insights</Button> + </StackPanel> </Grid> + + <controls:ToggleIconButton x:Name="chkSearchBar" ToolTip="Toggle range selection" Cursor="Hand" CheckedIcon="ChevronUpCircle" UncheckedIcon="ChevronDownCircle" Width="32" Height="32" VerticalAlignment="Bottom" Margin="0 0 0 0" IsChecked="{Binding IsSearchBarOpened}" /> + </Grid> + + <!--Anomalies Detection Progress--> + <Grid Background="#D4000000" Visibility="{Binding IsDetecting,Converter={StaticResource BooleanToVisibilityConverter}}"> + <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center"> + <Image Source="../Images/detection.png" Stretch="Uniform" Width="80" RenderOptions.BitmapScalingMode="Fant" Opacity="0.8" /> + <TextBlock Margin="0 20 0 0" Text="Detecting Anomalies" FontSize="30" HorizontalAlignment="Center"></TextBlock> + + <ItemsControl ItemsSource="{Binding AnomaliesDetectionProgresses}" Margin="0 40 0 0" Width="700"> + <ItemsControl.ItemsPanel> + <ItemsPanelTemplate> + <UniformGrid Rows="1" IsItemsHost="True"></UniformGrid> + </ItemsPanelTemplate> + </ItemsControl.ItemsPanel> + <ItemsControl.ItemTemplate> + <DataTemplate> + <StackPanel> + <ProgressBar Height="5" Maximum="{Binding FramesCount}" Value="{Binding CurrentFrame}"></ProgressBar> + </StackPanel> + </DataTemplate> + </ItemsControl.ItemTemplate> + </ItemsControl> + + <Button HorizontalAlignment="Center" Style="{StaticResource FSE_RaisedButton_Dark_Hover}" Margin="0 40 0 0" Width="200" Height="40">CANCEL</Button> + </StackPanel> </Grid> </Grid> </UserControl> 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 @@ </dependentAssembly> <dependentAssembly> <assemblyIdentity name="System.Collections.Immutable" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" /> - <bindingRedirect oldVersion="0.0.0.0-1.2.2.0" newVersion="1.2.2.0" /> + <bindingRedirect oldVersion="0.0.0.0-1.2.5.0" newVersion="1.2.5.0" /> </dependentAssembly> <dependentAssembly> <assemblyIdentity name="System.Reflection.Metadata" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" /> @@ -52,7 +52,7 @@ </dependentAssembly> <dependentAssembly> <assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" /> - <bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0" /> + <bindingRedirect oldVersion="0.0.0.0-12.0.0.0" newVersion="12.0.0.0" /> </dependentAssembly> <dependentAssembly> <assemblyIdentity name="Microsoft.Data.Edm" publicKeyToken="31bf3856ad364e35" culture="neutral" /> @@ -74,6 +74,46 @@ <assemblyIdentity name="WebGrease" publicKeyToken="31bf3856ad364e35" culture="neutral" /> <bindingRedirect oldVersion="0.0.0.0-1.5.2.14234" newVersion="1.5.2.14234" /> </dependentAssembly> + <dependentAssembly> + <assemblyIdentity name="SharpDX.Mathematics" publicKeyToken="b4dcf0f35e5521f1" culture="neutral" /> + <bindingRedirect oldVersion="0.0.0.0-3.1.0.0" newVersion="3.1.0.0" /> + </dependentAssembly> + <dependentAssembly> + <assemblyIdentity name="SharpDX.Direct3D11" publicKeyToken="b4dcf0f35e5521f1" culture="neutral" /> + <bindingRedirect oldVersion="0.0.0.0-3.1.0.0" newVersion="3.1.0.0" /> + </dependentAssembly> + <dependentAssembly> + <assemblyIdentity name="SharpDX.DXGI" publicKeyToken="b4dcf0f35e5521f1" culture="neutral" /> + <bindingRedirect oldVersion="0.0.0.0-3.1.0.0" newVersion="3.1.0.0" /> + </dependentAssembly> + <dependentAssembly> + <assemblyIdentity name="SharpDX" publicKeyToken="b4dcf0f35e5521f1" culture="neutral" /> + <bindingRedirect oldVersion="0.0.0.0-3.1.0.0" newVersion="3.1.0.0" /> + </dependentAssembly> + <dependentAssembly> + <assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" /> + <bindingRedirect oldVersion="0.0.0.0-4.0.6.0" newVersion="4.0.6.0" /> + </dependentAssembly> + <dependentAssembly> + <assemblyIdentity name="System.Memory" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" /> + <bindingRedirect oldVersion="0.0.0.0-4.0.1.1" newVersion="4.0.1.1" /> + </dependentAssembly> + <dependentAssembly> + <assemblyIdentity name="System.Threading.Tasks.Extensions" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" /> + <bindingRedirect oldVersion="0.0.0.0-4.2.0.1" newVersion="4.2.0.1" /> + </dependentAssembly> + <dependentAssembly> + <assemblyIdentity name="Z.EntityFramework.Extensions" publicKeyToken="59b66d028979105b" culture="neutral" /> + <bindingRedirect oldVersion="0.0.0.0-4.0.50.0" newVersion="4.0.50.0" /> + </dependentAssembly> + <dependentAssembly> + <assemblyIdentity name="System.Net.Http.Formatting" publicKeyToken="31bf3856ad364e35" culture="neutral" /> + <bindingRedirect oldVersion="0.0.0.0-5.2.3.0" newVersion="5.2.3.0" /> + </dependentAssembly> + <dependentAssembly> + <assemblyIdentity name="Microsoft.IdentityModel.Clients.ActiveDirectory.Platform" publicKeyToken="31bf3856ad364e35" culture="neutral" /> + <bindingRedirect oldVersion="0.0.0.0-3.19.8.16603" newVersion="3.19.8.16603" /> + </dependentAssembly> </assemblyBinding> </runtime> <entityFramework> 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 @@ <package id="MahApps.Metro" version="1.6.5" targetFramework="net461" /> <package id="MaterialDesignColors" version="1.2.2" targetFramework="net461" /> <package id="MaterialDesignThemes" version="3.0.1" targetFramework="net461" /> - <package id="Newtonsoft.Json" version="9.0.1" targetFramework="net461" /> + <package id="Microsoft.ML" version="1.5.1" targetFramework="net461" /> + <package id="Microsoft.ML.CpuMath" version="1.5.1" targetFramework="net461" /> + <package id="Microsoft.ML.DataView" version="1.5.1" targetFramework="net461" /> + <package id="Microsoft.ML.Mkl.Redist" version="1.5.1" targetFramework="net461" /> + <package id="Microsoft.ML.TimeSeries" version="1.5.1" targetFramework="net461" /> + <package id="Newtonsoft.Json" version="12.0.3" targetFramework="net461" /> + <package id="System.Buffers" version="4.5.1" targetFramework="net461" /> + <package id="System.CodeDom" version="4.7.0" targetFramework="net461" /> + <package id="System.Collections.Immutable" version="1.7.1" targetFramework="net461" /> + <package id="System.Memory" version="4.5.4" targetFramework="net461" /> + <package id="System.Numerics.Vectors" version="4.5.0" targetFramework="net461" /> + <package id="System.Reflection.Emit.Lightweight" version="4.7.0" targetFramework="net461" /> + <package id="System.Runtime.CompilerServices.Unsafe" version="4.7.1" targetFramework="net461" /> + <package id="System.Threading.Channels" version="4.7.1" targetFramework="net461" /> + <package id="System.Threading.Tasks.Extensions" version="4.5.4" targetFramework="net461" /> </packages>
\ 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<DateTime?> GetInsightsMinDate(); Task<InsightsHandler> 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 @@ <dependentAssembly> <assemblyIdentity name="Z.EntityFramework.Extensions" publicKeyToken="59b66d028979105b" culture="neutral" /> <bindingRedirect oldVersion="0.0.0.0-4.0.50.0" newVersion="4.0.50.0" /> - </dependentAssembly> + </dependentAssembly> + <dependentAssembly> + <assemblyIdentity name="System.Collections.Immutable" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" /> + <bindingRedirect oldVersion="0.0.0.0-1.2.5.0" newVersion="1.2.5.0" /> + </dependentAssembly> + <dependentAssembly> + <assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" /> + <bindingRedirect oldVersion="0.0.0.0-4.0.6.0" newVersion="4.0.6.0" /> + </dependentAssembly> </assemblyBinding> </runtime> <entityFramework> 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<InsightsHandler> 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<InsightsDownloadCompletedRequest, InsightsDownloadCompletedResponse>(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<DateTime?> GetInsightsMinDate() + { + if (minDate != null) + { + return minDate; + } + + var response = await MachineProvider.MachineOperator.SendGenericRequest<InsightsMinDateRequest, InsightsMinDateResponse>(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 @@ <Reference Include="System.AppContext, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> <HintPath>..\..\packages\System.AppContext.4.3.0\lib\net46\System.AppContext.dll</HintPath> </Reference> - <Reference Include="System.Collections.Immutable, Version=1.2.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> - <HintPath>..\..\packages\System.Collections.Immutable.1.3.1\lib\portable-net45+win8+wp8+wpa81\System.Collections.Immutable.dll</HintPath> + <Reference Include="System.Collections.Immutable, Version=1.2.5.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> + <HintPath>..\..\packages\System.Collections.Immutable.1.7.1\lib\net461\System.Collections.Immutable.dll</HintPath> </Reference> <Reference Include="System.ComponentModel.Composition" /> <Reference Include="System.ComponentModel.DataAnnotations" /> 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 @@ <package id="System.AppContext" version="4.3.0" targetFramework="net461" /> <package id="System.Collections" version="4.3.0" targetFramework="net461" /> <package id="System.Collections.Concurrent" version="4.3.0" targetFramework="net461" /> - <package id="System.Collections.Immutable" version="1.3.1" targetFramework="net461" /> + <package id="System.Collections.Immutable" version="1.7.1" targetFramework="net461" /> <package id="System.Console" version="4.3.0" targetFramework="net461" /> <package id="System.Diagnostics.Debug" version="4.3.0" targetFramework="net461" /> <package id="System.Diagnostics.FileVersionInfo" version="4.3.0" targetFramework="net461" /> 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 @@ <DockPanel Margin="0 20 0 0" TextElement.FontSize="{StaticResource TangoDefaultFontSize}"> <TextBlock VerticalAlignment="Center">Insights Storage Cleanup Interval (min)</TextBlock> - <touch:TouchNumericTextBox Minimum="5" Maximum="120" KeyboardContainer="{Binding ElementName=Container}" Value="{Binding Settings.InsightsStorageCleanupInterval,Converter={StaticResource TimeSpanToMinutesConverter}}" Margin="0 0 100 0" DockPanel.Dock="Right" HorizontalAlignment="Right" Width="90"></touch:TouchNumericTextBox> + <touch:TouchNumericTextBox Minimum="1" Maximum="120" KeyboardContainer="{Binding ElementName=Container}" Value="{Binding Settings.InsightsStorageCleanupInterval,Converter={StaticResource TimeSpanToMinutesConverter}}" Margin="0 0 100 0" DockPanel.Dock="Right" HorizontalAlignment="Right" Width="90"></touch:TouchNumericTextBox> </DockPanel> <DockPanel Margin="0 20 0 0" TextElement.FontSize="{StaticResource TangoDefaultFontSize}"> <TextBlock VerticalAlignment="Center">Insights Max Storage Duration (days)</TextBlock> - <touch:TouchNumericTextBox Minimum="5" Maximum="120" KeyboardContainer="{Binding ElementName=Container}" Value="{Binding Settings.InsightsMaxStorageDuration,Converter={StaticResource TimeSpanToDaysConverter}}" Margin="0 0 100 0" DockPanel.Dock="Right" HorizontalAlignment="Right" Width="90"></touch:TouchNumericTextBox> + <touch:TouchNumericTextBox Minimum="1" Maximum="120" KeyboardContainer="{Binding ElementName=Container}" Value="{Binding Settings.InsightsMaxStorageDuration,Converter={StaticResource TimeSpanToDaysConverter}}" Margin="0 0 100 0" DockPanel.Dock="Right" HorizontalAlignment="Right" Width="90"></touch:TouchNumericTextBox> </DockPanel> <DockPanel Margin="0 20 0 0"> 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<PPCSettings>(); + 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 @@ <Compile Include="Information\GetMachineInformationRequest.cs" /> <Compile Include="Information\GetMachineInformationResponse.cs" /> <Compile Include="Information\InformationPackage.cs" /> + <Compile Include="Insights\InsightsDownloadCompletedRequest.cs" /> + <Compile Include="Insights\InsightsDownloadCompletedResponse.cs" /> + <Compile Include="Insights\InsightsMinDateRequest.cs" /> + <Compile Include="Insights\InsightsMinDateResponse.cs" /> <Compile Include="Insights\InsightsRequest.cs" /> <Compile Include="Insights\InsightsResponse.cs" /> <Compile Include="Jobs\RemoteJobProgress.cs" /> 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 +{ + /// <summary> + /// Splits the list to chunks. + /// </summary> + /// <typeparam name="T"></typeparam> + /// <param name="source">The source.</param> + /// <param name="chunkSize">Size of the chunk.</param> + /// <returns></returns> + public static List<List<T>> ChunkBy<T>(this List<T> 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 @@ <Compile Include="CustomAttributes\StringFormatAttribute.cs" /> <Compile Include="ExtensionMethods\BooleanExtensions.cs" /> <Compile Include="ExtensionMethods\ByteArrayExtensions.cs" /> + <Compile Include="ExtensionMethods\ListExtensions.cs" /> <Compile Include="ExtensionMethods\TimeSpanExtensions.cs" /> <Compile Include="ExtensionMethods\ZipArchiveExtensions.cs" /> <Compile Include="IO\KnownFolders.cs" /> @@ -215,7 +216,7 @@ <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <ProjectExtensions> <VisualStudio> - <UserProperties BuildVersion_StartDate="2000/1/1" BuildVersion_UseGlobalSettings="False" BuildVersion_BuildVersioningStyle="None.None.Increment.TimeStamp" BuildVersion_UpdateAssemblyVersion="True" BuildVersion_AssemblyInfoFilename="Properties\AssemblyInfo.cs" /> + <UserProperties BuildVersion_AssemblyInfoFilename="Properties\AssemblyInfo.cs" BuildVersion_UpdateAssemblyVersion="True" BuildVersion_BuildVersioningStyle="None.None.Increment.TimeStamp" BuildVersion_UseGlobalSettings="False" BuildVersion_StartDate="2000/1/1" /> </VisualStudio> </ProjectExtensions> <Import Project="..\packages\System.Data.SQLite.Core.1.0.108.0\build\net46\System.Data.SQLite.Core.targets" Condition="Exists('..\packages\System.Data.SQLite.Core.1.0.108.0\build\net46\System.Data.SQLite.Core.targets')" /> 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<InsightsEvent> 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<InsightsEvent> 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; } } |
