aboutsummaryrefslogtreecommitdiffstats
path: root/Software/Visual_Studio
diff options
context:
space:
mode:
authorRoy Ben Shabat <Roy.mail.net@gmail.com>2020-08-21 12:58:15 +0300
committerRoy Ben Shabat <Roy.mail.net@gmail.com>2020-08-21 12:58:15 +0300
commit7bd70fcb311c808b65b62e774755dcbd6b0d63cd (patch)
tree56db3c489de3665252c44d9ff477bb47bb722dc1 /Software/Visual_Studio
parentb0255ed4ae2827801d13cec175e57108a0666db9 (diff)
downloadTango-7bd70fcb311c808b65b62e774755dcbd6b0d63cd.tar.gz
Tango-7bd70fcb311c808b65b62e774755dcbd6b0d63cd.zip
Insights anomalies.
Diffstat (limited to 'Software/Visual_Studio')
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/Dialogs/AnomaliesDialogView.xaml54
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/Dialogs/AnomaliesDialogView.xaml.cs28
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/Dialogs/AnomaliesDialogViewVM.cs66
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/Images/detection.pngbin0 -> 5717 bytes
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/Images/insight_big.pngbin0 -> 7682 bytes
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/Images/time.pngbin9091 -> 4974 bytes
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/ML/AnomaliesDetectionProgress.cs30
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/ML/AnomaliesDetector.cs63
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/ML/AnomalyType.cs13
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/ML/DetectedAnnomaly.cs21
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/ML/MonitorSeriesPrediction.cs15
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/ML/MonitorValue.cs17
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/SciChart/InsightsChart.cs101
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/Tango.FSE.Insights.csproj85
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/Themes/Generic.xaml7
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/ViewModels/InsightsViewVM.cs262
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/Views/InsightsView.xaml537
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/app.config44
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/packages.config16
-rw-r--r--Software/Visual_Studio/FSE/Tango.FSE.Common/Insights/IInsightsProvider.cs1
-rw-r--r--Software/Visual_Studio/FSE/Tango.FSE.UI/App.config10
-rw-r--r--Software/Visual_Studio/FSE/Tango.FSE.UI/Insights/DefaultInsightsProvider.cs42
-rw-r--r--Software/Visual_Studio/FSE/Tango.FSE.UI/Tango.FSE.UI.csproj4
-rw-r--r--Software/Visual_Studio/FSE/Tango.FSE.UI/packages.config2
-rw-r--r--Software/Visual_Studio/PPC/Modules/Tango.PPC.MachineSettings/Views/MainView.xaml4
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/EventLogging/DefaultEventLogger.cs12
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/Insights/DefaultInsightsService.cs38
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Shared/Insights/InsightsDownloadCompletedRequest.cs13
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Shared/Insights/InsightsDownloadCompletedResponse.cs12
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Shared/Insights/InsightsMinDateRequest.cs12
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Shared/Insights/InsightsMinDateResponse.cs13
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Shared/Tango.PPC.Shared.csproj4
-rw-r--r--Software/Visual_Studio/Tango.Core/ExtensionMethods/ListExtensions.cs26
-rw-r--r--Software/Visual_Studio/Tango.Core/Tango.Core.csproj3
-rw-r--r--Software/Visual_Studio/Tango.Insights/InsightsManager.cs27
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
new file mode 100644
index 000000000..a29355327
--- /dev/null
+++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/Images/detection.png
Binary files differ
diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/Images/insight_big.png b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/Images/insight_big.png
new file mode 100644
index 000000000..e5cc9e2dc
--- /dev/null
+++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/Images/insight_big.png
Binary files differ
diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/Images/time.png b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/Images/time.png
index 1eb9b077f..542bd09db 100644
--- 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 differ
diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/ML/AnomaliesDetectionProgress.cs b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/ML/AnomaliesDetectionProgress.cs
new file mode 100644
index 000000000..6331e7de0
--- /dev/null
+++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/ML/AnomaliesDetectionProgress.cs
@@ -0,0 +1,30 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Tango.Core;
+using Tango.FSE.Insights.SciChart;
+
+namespace Tango.FSE.Insights.ML
+{
+ public class AnomaliesDetectionProgress : ExtendedObject
+ {
+ public InsightsChart Chart { get; set; }
+ public int FramesCount { get; set; }
+ private int _currentFrame;
+
+ public int CurrentFrame
+ {
+ get { return _currentFrame; }
+ set
+ {
+ if (value > _currentFrame + 9 || value == FramesCount)
+ {
+ _currentFrame = value;
+ RaisePropertyChanged(nameof(CurrentFrame));
+ }
+ }
+ }
+ }
+}
diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/ML/AnomaliesDetector.cs b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/ML/AnomaliesDetector.cs
new file mode 100644
index 000000000..4a81e9eb5
--- /dev/null
+++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/ML/AnomaliesDetector.cs
@@ -0,0 +1,63 @@
+using Microsoft.ML;
+using Microsoft.ML.Transforms.TimeSeries;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tango.FSE.Insights.ML
+{
+ public class AnomaliesDetector
+ {
+ private IDataView _view;
+ private MLContext _mlContext;
+ private List<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;
}
}