From 02ae577faa0bd4938507061d603e4f9447e2b64f Mon Sep 17 00:00:00 2001 From: Roy Ben Shabat Date: Sun, 23 Aug 2020 08:44:31 +0300 Subject: Fixed issue with insights file datetime utc. added insights application exception/crash. added remote actions service. fixed issue with LiteDB hang on application start/exit. reduced insights listener empty frames. --- .../Tango.FSE.Insights/Images/app_crash.png | Bin 0 -> 5249 bytes .../Tango.FSE.Insights/Images/app_exception.png | Bin 0 -> 2720 bytes .../Tango.FSE.Insights/Images/bug_report.png | Bin 0 -> 3831 bytes .../Tango.FSE.Insights/Tango.FSE.Insights.csproj | 7 + .../Modules/Tango.FSE.Insights/Themes/Generic.xaml | 146 +++++++++++++++++++-- .../ViewModels/InsightsViewVM.cs | 49 +++++-- .../Tango.FSE.Common/Insights/InsightsPackage.cs | 2 + .../Insights/InsightsReadyApplicationException.cs | 14 ++ .../RemoteActions/IRemoteActionsProvider.cs | 13 ++ .../FSE/Tango.FSE.Common/Tango.FSE.Common.csproj | 2 + .../Insights/DefaultInsightsProvider.cs | 15 ++- .../RemoteActions/DefaultRemoteActionsProvider.cs | 26 ++++ .../FSE/Tango.FSE.UI/Tango.FSE.UI.csproj | 1 + .../FSE/Tango.FSE.UI/ViewModelLocator.cs | 4 + .../Insights/DefaultInsightsService.cs | 5 + .../RemoteActions/IRemoteActionsService.cs | 13 ++ .../PPC/Tango.PPC.Common/Tango.PPC.Common.csproj | 3 +- .../Tango.PPC.Shared/Insights/InsightsRequest.cs | 2 + .../SimulateApplicationExceptionRequest.cs | 13 ++ .../SimulateApplicationExceptionResponse.cs | 12 ++ .../PPC/Tango.PPC.Shared/Tango.PPC.Shared.csproj | 5 + .../Visual_Studio/PPC/Tango.PPC.UI/App.xaml.cs | 33 ++++- .../PPCApplication/DefaultPPCApplicationManager.cs | 3 + .../RemoteActions/DefaultRemoteActionsService.cs | 54 ++++++++ .../PPC/Tango.PPC.UI/Tango.PPC.UI.csproj | 6 +- .../PPC/Tango.PPC.UI/ViewModelLocator.cs | 4 + .../Tango.Core/Bson/BsonUtcSerializer.cs | 68 ++++++++++ .../Visual_Studio/Tango.Core/Tango.Core.csproj | 3 +- .../Tango.Insights/InsightsApplicationException.cs | 20 +++ .../Visual_Studio/Tango.Insights/InsightsFile.cs | 17 ++- .../Tango.Insights/InsightsListener.cs | 19 ++- .../Tango.Insights/InsightsManager.cs | 42 +++++- .../Tango.Insights/Tango.Insights.csproj | 1 + .../Tango.Logging/GlobalExceptionTrapper.cs | 13 ++ .../Tango.Transport/GenericMessageSerializer.cs | 52 +------- 35 files changed, 578 insertions(+), 89 deletions(-) create mode 100644 Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/Images/app_crash.png create mode 100644 Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/Images/app_exception.png create mode 100644 Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/Images/bug_report.png create mode 100644 Software/Visual_Studio/FSE/Tango.FSE.Common/Insights/InsightsReadyApplicationException.cs create mode 100644 Software/Visual_Studio/FSE/Tango.FSE.Common/RemoteActions/IRemoteActionsProvider.cs create mode 100644 Software/Visual_Studio/FSE/Tango.FSE.UI/RemoteActions/DefaultRemoteActionsProvider.cs create mode 100644 Software/Visual_Studio/PPC/Tango.PPC.Common/RemoteActions/IRemoteActionsService.cs create mode 100644 Software/Visual_Studio/PPC/Tango.PPC.Shared/RemoteActions/SimulateApplicationExceptionRequest.cs create mode 100644 Software/Visual_Studio/PPC/Tango.PPC.Shared/RemoteActions/SimulateApplicationExceptionResponse.cs create mode 100644 Software/Visual_Studio/PPC/Tango.PPC.UI/RemoteActions/DefaultRemoteActionsService.cs create mode 100644 Software/Visual_Studio/Tango.Core/Bson/BsonUtcSerializer.cs create mode 100644 Software/Visual_Studio/Tango.Insights/InsightsApplicationException.cs (limited to 'Software/Visual_Studio') diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/Images/app_crash.png b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/Images/app_crash.png new file mode 100644 index 000000000..303952d86 Binary files /dev/null and b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/Images/app_crash.png differ diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/Images/app_exception.png b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/Images/app_exception.png new file mode 100644 index 000000000..d14bb84af Binary files /dev/null and b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/Images/app_exception.png differ diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/Images/bug_report.png b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/Images/bug_report.png new file mode 100644 index 000000000..05d00014d Binary files /dev/null and b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/Images/bug_report.png differ 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 ea6616985..617ca2d99 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 @@ -312,6 +312,13 @@ + + + + + + + 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 5426ba4ea..b4b3fe746 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 @@ -11,7 +11,7 @@ - + @@ -139,7 +139,7 @@ - + @@ -167,7 +167,7 @@ - - - - + + + Bug Report + + + + + + + + + Unhandled Application Exception + + Time: + + + + Exception + + + + + + + + + + + Application Exception + + + + + + + + + Application Crash! + + Time: + + + + + + + + + + + + + Application Crash! + \ 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 6ef92e525..ccbb77cf1 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 @@ -20,6 +20,7 @@ using Tango.BL.Entities; using Tango.BL.Enumerations; using Tango.Core.Commands; using Tango.FSE.Common; +using Tango.FSE.Common.Dialogs; using Tango.FSE.Common.Insights; using Tango.FSE.Insights.Contracts; using Tango.FSE.Insights.Dialogs; @@ -189,6 +190,8 @@ namespace Tango.FSE.Insights.ViewModels public RelayCommand OpenBugReportCommand { get; set; } + public RelayCommand ViewApplicationExceptionCommand { get; set; } + public RelayCommand AnalyzeCommand { get; set; } #endregion @@ -210,6 +213,7 @@ namespace Tango.FSE.Insights.ViewModels PeekApplicationLogsCommand = new RelayCommand(PeekAnnotationApplicationLogs); PeekFirmwareLogsCommand = new RelayCommand(PeekAnnotationFirmwareLogs); OpenBugReportCommand = new RelayCommand(OpenAnnotationBugReport); + ViewApplicationExceptionCommand = new RelayCommand(ViewAnnotationApplicationException); AnalyzeCommand = new RelayCommand(Analyze, () => InsightsPackage != null); } @@ -482,12 +486,12 @@ namespace Tango.FSE.Insights.ViewModels line.Stroke = connected ? Brushes.Green : Brushes.DarkGray; line.X1 = date; line.X2 = date; - line.ShowLabel = false; - line.AnnotationLabels.Add(new AnnotationLabel() - { - LabelPlacement = connected ? LabelPlacement.TopRight : LabelPlacement.BottomLeft, - Text = connected ? "machine connected" : "machine disconnected" - }); + //line.ShowLabel = false; + //line.AnnotationLabels.Add(new AnnotationLabel() + //{ + // LabelPlacement = connected ? LabelPlacement.TopRight : LabelPlacement.BottomLeft, + // Text = connected ? "machine connected" : "machine disconnected" + //}); Annotations.Add(line); } @@ -495,7 +499,7 @@ namespace Tango.FSE.Insights.ViewModels { var y = 0.05; - foreach (var insight in package.Events.Cast().Concat(package.Statuses).Concat(package.Bugs).OrderBy(x => x.Time)) + foreach (var insight in package.Events.Cast().Concat(package.Statuses).Concat(package.Bugs).Concat(package.ApplicationExceptions).OrderBy(x => x.Time)) { CustomAnnotation annotation = new CustomAnnotation(); annotation.X1 = insight.Time.ToLocalTime(); @@ -509,14 +513,30 @@ namespace Tango.FSE.Insights.ViewModels } else if (insight is InsightsReadyStatus status) { + Panel.SetZIndex(annotation, 1); annotation.Content = status; annotation.ContentTemplate = Application.Current.Resources["InsightStatusTemplate"] as DataTemplate; } else if (insight is InsightsReadyBug bug) { + Panel.SetZIndex(annotation, 2); annotation.Content = bug; annotation.ContentTemplate = Application.Current.Resources["InsightBugTemplate"] as DataTemplate; } + else if (insight is InsightsReadyApplicationException appException) + { + annotation.Content = appException; + Panel.SetZIndex(annotation, 3); + + if (appException.IsApplicationCrash) + { + annotation.ContentTemplate = Application.Current.Resources["InsightApplicationCrashTemplate"] as DataTemplate; + } + else + { + annotation.ContentTemplate = Application.Current.Resources["InsightApplicationExceptionTemplate"] as DataTemplate; + } + } Annotations.Add(annotation); @@ -531,7 +551,7 @@ namespace Tango.FSE.Insights.ViewModels #endregion - #region Logs Peeking + #region Menu Items Handling private async void PeekAnnotationFirmwareLogs(InsightReadyBase ev) { @@ -551,15 +571,20 @@ namespace Tango.FSE.Insights.ViewModels } } - #endregion - - #region Bug Report - private void OpenAnnotationBugReport(InsightsReadyBug bug) { Process.Start(bug.Bug.URL); } + private async void ViewAnnotationApplicationException(InsightsReadyApplicationException appException) + { + MessageLogItem logItem = new MessageLogItem(); + logItem.Category = LogCategory.Critical; + logItem.TimeStamp = appException.Time; + logItem.Message = appException.Exception; + await NotificationProvider.ShowDialog(new ApplicationLogItemViewVM() { LogItem = logItem }); + } + #endregion #region Event Handlers diff --git a/Software/Visual_Studio/FSE/Tango.FSE.Common/Insights/InsightsPackage.cs b/Software/Visual_Studio/FSE/Tango.FSE.Common/Insights/InsightsPackage.cs index 3f57a50d9..a823295a9 100644 --- a/Software/Visual_Studio/FSE/Tango.FSE.Common/Insights/InsightsPackage.cs +++ b/Software/Visual_Studio/FSE/Tango.FSE.Common/Insights/InsightsPackage.cs @@ -14,6 +14,7 @@ namespace Tango.FSE.Common.Insights public List Frames { get; set; } public List Events { get; set; } public List Statuses { get; set; } + public List ApplicationExceptions { get; set; } public List Bugs { get; set; } public InsightsPackage() @@ -22,6 +23,7 @@ namespace Tango.FSE.Common.Insights Events = new List(); Statuses = new List(); Bugs = new List(); + ApplicationExceptions = new List(); } } } diff --git a/Software/Visual_Studio/FSE/Tango.FSE.Common/Insights/InsightsReadyApplicationException.cs b/Software/Visual_Studio/FSE/Tango.FSE.Common/Insights/InsightsReadyApplicationException.cs new file mode 100644 index 000000000..9f4524100 --- /dev/null +++ b/Software/Visual_Studio/FSE/Tango.FSE.Common/Insights/InsightsReadyApplicationException.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Tango.FSE.Common.Insights +{ + public class InsightsReadyApplicationException : InsightReadyBase + { + public String Exception { get; set; } + public bool IsApplicationCrash { get; set; } + } +} diff --git a/Software/Visual_Studio/FSE/Tango.FSE.Common/RemoteActions/IRemoteActionsProvider.cs b/Software/Visual_Studio/FSE/Tango.FSE.Common/RemoteActions/IRemoteActionsProvider.cs new file mode 100644 index 000000000..024a8ac39 --- /dev/null +++ b/Software/Visual_Studio/FSE/Tango.FSE.Common/RemoteActions/IRemoteActionsProvider.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Tango.FSE.Common.RemoteActions +{ + public interface IRemoteActionsProvider + { + void SimulateApplicationException(bool causeCrash); + } +} diff --git a/Software/Visual_Studio/FSE/Tango.FSE.Common/Tango.FSE.Common.csproj b/Software/Visual_Studio/FSE/Tango.FSE.Common/Tango.FSE.Common.csproj index b89d30f82..cbd70e665 100644 --- a/Software/Visual_Studio/FSE/Tango.FSE.Common/Tango.FSE.Common.csproj +++ b/Software/Visual_Studio/FSE/Tango.FSE.Common/Tango.FSE.Common.csproj @@ -228,6 +228,7 @@ + @@ -262,6 +263,7 @@ + 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 5b17bd5e0..08120c24f 100644 --- a/Software/Visual_Studio/FSE/Tango.FSE.UI/Insights/DefaultInsightsProvider.cs +++ b/Software/Visual_Studio/FSE/Tango.FSE.UI/Insights/DefaultInsightsProvider.cs @@ -55,7 +55,10 @@ namespace Tango.FSE.UI.Insights var response = await MachineProvider.MachineOperator.SendGenericRequest(new InsightsRequest() { StartDateUTC = startDateUTC, - EndDateUTC = endTimeUTC + EndDateUTC = endTimeUTC, + IncludeStatuses = true, + IncludeEvents = true, + IncludeApplicationExceptions = true }, new TransportRequestConfig() { Timeout = TimeSpan.FromMinutes(1), @@ -142,6 +145,16 @@ namespace Tango.FSE.UI.Insights }); } + foreach (var appException in insightsFile.ApplicationExceptions) + { + package.ApplicationExceptions.Add(new InsightsReadyApplicationException() + { + Time = appException.Time, + Exception = appException.Exception, + IsApplicationCrash = appException.IsApplicationCrash + }); + } + if (AuthenticationProvider.CurrentUser.HasPermission(Permissions.FSE_ModifyBugReport)) { try diff --git a/Software/Visual_Studio/FSE/Tango.FSE.UI/RemoteActions/DefaultRemoteActionsProvider.cs b/Software/Visual_Studio/FSE/Tango.FSE.UI/RemoteActions/DefaultRemoteActionsProvider.cs new file mode 100644 index 000000000..98256fa3c --- /dev/null +++ b/Software/Visual_Studio/FSE/Tango.FSE.UI/RemoteActions/DefaultRemoteActionsProvider.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Tango.Core.DI; +using Tango.FSE.Common.Connection; +using Tango.FSE.Common.RemoteActions; +using Tango.PPC.Shared.RemoteActions; + +namespace Tango.FSE.UI.RemoteActions +{ + public class DefaultRemoteActionsProvider : IRemoteActionsProvider + { + [TangoInject] + private IMachineProvider MachineProvider { get; set; } + + public void SimulateApplicationException(bool causeCrash) + { + var response = MachineProvider.MachineOperator.SendGenericRequest(new SimulateApplicationExceptionRequest() + { + CrashApplication = causeCrash, + }).Result; + } + } +} 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 8974407d4..17fa594fd 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 @@ -330,6 +330,7 @@ + diff --git a/Software/Visual_Studio/FSE/Tango.FSE.UI/ViewModelLocator.cs b/Software/Visual_Studio/FSE/Tango.FSE.UI/ViewModelLocator.cs index 5da32809f..e70b25d09 100644 --- a/Software/Visual_Studio/FSE/Tango.FSE.UI/ViewModelLocator.cs +++ b/Software/Visual_Studio/FSE/Tango.FSE.UI/ViewModelLocator.cs @@ -68,6 +68,8 @@ using Tango.FSE.Common.FileAssociation; using Tango.FSE.UI.FileAssociation; using Tango.FSE.Common.Insights; using Tango.FSE.UI.Insights; +using Tango.FSE.Common.RemoteActions; +using Tango.FSE.UI.RemoteActions; namespace Tango.FSE.UI { @@ -107,6 +109,7 @@ namespace Tango.FSE.UI TangoIOC.Default.Unregister(); TangoIOC.Default.Unregister(); TangoIOC.Default.Unregister(); + TangoIOC.Default.Unregister(); //TangoIOC.Default.Unregister(); //TangoIOC.Default.Unregister(); //TangoIOC.Default.Unregister(); @@ -146,6 +149,7 @@ namespace Tango.FSE.UI TangoIOC.Default.Register(); TangoIOC.Default.Register(); TangoIOC.Default.Register(); + TangoIOC.Default.Register(); TangoIOC.Default.Register(); 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 f015c3bad..75c5ae9cd 100644 --- a/Software/Visual_Studio/PPC/Tango.PPC.Common/Insights/DefaultInsightsService.cs +++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/Insights/DefaultInsightsService.cs @@ -89,6 +89,11 @@ namespace Tango.PPC.Common.Insights insightsFile.Statuses = InsightsManager.Default.GetStatuses(request.StartDateUTC, request.EndDateUTC); } + if (request.IncludeApplicationExceptions) + { + insightsFile.ApplicationExceptions = InsightsManager.Default.GetApplicationExceptions(request.StartDateUTC, request.EndDateUTC); + } + insightsFile.ToFile(filePath); }); diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/RemoteActions/IRemoteActionsService.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/RemoteActions/IRemoteActionsService.cs new file mode 100644 index 000000000..477663342 --- /dev/null +++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/RemoteActions/IRemoteActionsService.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Tango.PPC.Common.RemoteActions +{ + public interface IRemoteActionsService + { + + } +} diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/Tango.PPC.Common.csproj b/Software/Visual_Studio/PPC/Tango.PPC.Common/Tango.PPC.Common.csproj index 08e49621c..ed0c30755 100644 --- a/Software/Visual_Studio/PPC/Tango.PPC.Common/Tango.PPC.Common.csproj +++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/Tango.PPC.Common.csproj @@ -179,6 +179,7 @@ + @@ -483,7 +484,7 @@ - + \ No newline at end of file diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Shared/Insights/InsightsRequest.cs b/Software/Visual_Studio/PPC/Tango.PPC.Shared/Insights/InsightsRequest.cs index 6603a05b5..b34895e78 100644 --- a/Software/Visual_Studio/PPC/Tango.PPC.Shared/Insights/InsightsRequest.cs +++ b/Software/Visual_Studio/PPC/Tango.PPC.Shared/Insights/InsightsRequest.cs @@ -13,11 +13,13 @@ namespace Tango.PPC.Shared.Insights public bool IncludeEvents { get; set; } public bool IncludeStatuses { get; set; } + public bool IncludeApplicationExceptions { get; set; } public InsightsRequest() { IncludeEvents = true; IncludeStatuses = true; + IncludeApplicationExceptions = true; } } } diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Shared/RemoteActions/SimulateApplicationExceptionRequest.cs b/Software/Visual_Studio/PPC/Tango.PPC.Shared/RemoteActions/SimulateApplicationExceptionRequest.cs new file mode 100644 index 000000000..76216edad --- /dev/null +++ b/Software/Visual_Studio/PPC/Tango.PPC.Shared/RemoteActions/SimulateApplicationExceptionRequest.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.RemoteActions +{ + public class SimulateApplicationExceptionRequest + { + public bool CrashApplication { get; set; } + } +} diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Shared/RemoteActions/SimulateApplicationExceptionResponse.cs b/Software/Visual_Studio/PPC/Tango.PPC.Shared/RemoteActions/SimulateApplicationExceptionResponse.cs new file mode 100644 index 000000000..e0b71ddf4 --- /dev/null +++ b/Software/Visual_Studio/PPC/Tango.PPC.Shared/RemoteActions/SimulateApplicationExceptionResponse.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.RemoteActions +{ + public class SimulateApplicationExceptionResponse + { + } +} 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 de1eb03b1..bfd4d587b 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 @@ -95,6 +95,8 @@ Settings.settings True + + @@ -137,5 +139,8 @@ Tango.SystemInfo + + + \ No newline at end of file diff --git a/Software/Visual_Studio/PPC/Tango.PPC.UI/App.xaml.cs b/Software/Visual_Studio/PPC/Tango.PPC.UI/App.xaml.cs index 16eb656a8..e478dba77 100644 --- a/Software/Visual_Studio/PPC/Tango.PPC.UI/App.xaml.cs +++ b/Software/Visual_Studio/PPC/Tango.PPC.UI/App.xaml.cs @@ -13,6 +13,7 @@ using Tango.BL; using Tango.Core; using Tango.Core.DI; using Tango.Core.Helpers; +using Tango.Insights; using Tango.Integration.Operation; using Tango.Logging; using Tango.PPC.Common; @@ -29,7 +30,7 @@ namespace Tango.PPC.UI /// public partial class App : Application { - private WpfGlobalExceptionTrapper exceptionTrapper; + public static WpfGlobalExceptionTrapper ExceptionTrapper; public static String[] StartupArgs { get; private set; } private LogManager LogManager = LogManager.Default; @@ -80,9 +81,9 @@ namespace Tango.PPC.UI base.OnStartup(e); - exceptionTrapper = new WpfGlobalExceptionTrapper(); - exceptionTrapper.Initialize(this); - exceptionTrapper.ApplicationCrashed += ExceptionTrapper_ApplicationCrashed; + ExceptionTrapper = new WpfGlobalExceptionTrapper(); + ExceptionTrapper.Initialize(this); + ExceptionTrapper.ApplicationCrashed += ExceptionTrapper_ApplicationCrashed; CoreSettings.DefaultDataSource = new DataSource() { @@ -100,10 +101,21 @@ namespace Tango.PPC.UI private async void GetLastApplicationCrashFromWindows() { - var logItem = await exceptionTrapper.GetLastApplicationCrashEventLog(); + var logItem = await ExceptionTrapper.GetLastApplicationCrashEventLog(60); if (logItem != null) { LogManager.Log(logItem); + + try + { + InsightsManager.Default.InsertApplicationException(new InsightsApplicationException() + { + Time = DateTime.UtcNow, + Exception = logItem.Message, + IsApplicationCrash = true + }); + } + catch { } } } @@ -133,6 +145,17 @@ namespace Tango.PPC.UI LogManager.Log(e.Exception, "Application Crashed!"); LogManager.Log("Trying to recover from application crash..."); + try + { + InsightsManager.Default.InsertApplicationException(new InsightsApplicationException() + { + Time = DateTime.UtcNow, + Exception = e.Exception.ToString() + }); + } + catch + { } + try { if (Application.Current == null) diff --git a/Software/Visual_Studio/PPC/Tango.PPC.UI/PPCApplication/DefaultPPCApplicationManager.cs b/Software/Visual_Studio/PPC/Tango.PPC.UI/PPCApplication/DefaultPPCApplicationManager.cs index 54193a793..29f4e65be 100644 --- a/Software/Visual_Studio/PPC/Tango.PPC.UI/PPCApplication/DefaultPPCApplicationManager.cs +++ b/Software/Visual_Studio/PPC/Tango.PPC.UI/PPCApplication/DefaultPPCApplicationManager.cs @@ -476,6 +476,7 @@ namespace Tango.PPC.UI.PPCApplication { var frame = InsightsFrame.CreateEmpty(DateTime.UtcNow); InsightsManager.Default.InsertFrame(frame); + InsightsManager.Default.Dispose(); } catch {} @@ -528,6 +529,7 @@ namespace Tango.PPC.UI.PPCApplication { var frame = InsightsFrame.CreateEmpty(DateTime.UtcNow); InsightsManager.Default.InsertFrame(frame); + InsightsManager.Default.Dispose(); } catch { } @@ -566,6 +568,7 @@ namespace Tango.PPC.UI.PPCApplication { var frame = InsightsFrame.CreateEmpty(DateTime.UtcNow); InsightsManager.Default.InsertFrame(frame); + InsightsManager.Default.Dispose(); } catch { } diff --git a/Software/Visual_Studio/PPC/Tango.PPC.UI/RemoteActions/DefaultRemoteActionsService.cs b/Software/Visual_Studio/PPC/Tango.PPC.UI/RemoteActions/DefaultRemoteActionsService.cs new file mode 100644 index 000000000..1b8780f91 --- /dev/null +++ b/Software/Visual_Studio/PPC/Tango.PPC.UI/RemoteActions/DefaultRemoteActionsService.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Tango.Core.DI; +using Tango.Core.Threading; +using Tango.Integration.ExternalBridge; +using Tango.PPC.Common.ExternalBridge; +using Tango.PPC.Common.RemoteActions; +using Tango.PPC.Common.Threading; +using Tango.PPC.Shared.RemoteActions; + +namespace Tango.PPC.UI.RemoteActions +{ + [TangoCreateWhenRegistered] + public class DefaultRemoteActionsService : IRemoteActionsService, IExternalBridgeRequestHandler + { + [TangoInject] + private IDispatcherProvider DispatcherProvider { get; set; } + + public DefaultRemoteActionsService(IPPCExternalBridgeService externalBridge) + { + externalBridge.RegisterRequestHandler(this); + } + + public void OnReceiverDisconnected(ExternalBridgeReceiver receiver) + { + //Do nothing. + } + + [ExternalBridgeRequestHandlerMethod(typeof(SimulateApplicationExceptionRequest), RequestHandlerLoggingMode.LogRequestName)] + public async Task OnSimulateApplicationExceptionRequest(SimulateApplicationExceptionRequest request, String token, ExternalBridgeReceiver receiver) + { + await receiver.SendGenericResponse(new SimulateApplicationExceptionResponse(), token); + + Thread.Sleep(500); + + DispatcherProvider.Invoke(() => + { + if (request.CrashApplication) + { + App.ExceptionTrapper.Disable(); + throw new OutOfMemoryException("This is a simulated exception to cause the application to crash."); + } + else + { + throw new ApplicationException("This is a simulated exception to cause an unhandled application error."); + } + }); + } + } +} diff --git a/Software/Visual_Studio/PPC/Tango.PPC.UI/Tango.PPC.UI.csproj b/Software/Visual_Studio/PPC/Tango.PPC.UI/Tango.PPC.UI.csproj index 569621432..bfb16f10d 100644 --- a/Software/Visual_Studio/PPC/Tango.PPC.UI/Tango.PPC.UI.csproj +++ b/Software/Visual_Studio/PPC/Tango.PPC.UI/Tango.PPC.UI.csproj @@ -181,6 +181,7 @@ + @@ -446,6 +447,9 @@ + + Tango.ColorLib_v4.dll + @@ -717,7 +721,7 @@ if $(ConfigurationName) == Debug copy /Y "$(TargetDir)Packages" "$(TargetDir)" - + \ No newline at end of file diff --git a/Software/Visual_Studio/PPC/Tango.PPC.UI/ViewModelLocator.cs b/Software/Visual_Studio/PPC/Tango.PPC.UI/ViewModelLocator.cs index b5da9f066..7011cd3be 100644 --- a/Software/Visual_Studio/PPC/Tango.PPC.UI/ViewModelLocator.cs +++ b/Software/Visual_Studio/PPC/Tango.PPC.UI/ViewModelLocator.cs @@ -24,6 +24,7 @@ using Tango.PPC.Common.Notifications; using Tango.PPC.Common.OS; using Tango.PPC.Common.Performance; using Tango.PPC.Common.Printing; +using Tango.PPC.Common.RemoteActions; using Tango.PPC.Common.RemoteAssistance; using Tango.PPC.Common.RemoteDesktop; using Tango.PPC.Common.RemoteJob; @@ -42,6 +43,7 @@ using Tango.PPC.UI.Navigation; using Tango.PPC.UI.Notifications; using Tango.PPC.UI.PPCApplication; using Tango.PPC.UI.Printing; +using Tango.PPC.UI.RemoteActions; using Tango.PPC.UI.Threading; using Tango.PPC.UI.ViewModels; using Tango.PPC.UI.Views; @@ -94,6 +96,7 @@ namespace Tango.PPC.UI TangoIOC.Default.Unregister(); TangoIOC.Default.Unregister(); TangoIOC.Default.Unregister(); + TangoIOC.Default.Unregister(); if (App.StartupArgs != null && App.StartupArgs.Contains("-webDebug")) { @@ -135,6 +138,7 @@ namespace Tango.PPC.UI TangoIOC.Default.Register(); TangoIOC.Default.Register(); TangoIOC.Default.Register(); + TangoIOC.Default.Register(); TangoIOC.Default.Register(); TangoIOC.Default.Register(); diff --git a/Software/Visual_Studio/Tango.Core/Bson/BsonUtcSerializer.cs b/Software/Visual_Studio/Tango.Core/Bson/BsonUtcSerializer.cs new file mode 100644 index 000000000..6f5cb7700 --- /dev/null +++ b/Software/Visual_Studio/Tango.Core/Bson/BsonUtcSerializer.cs @@ -0,0 +1,68 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Tango.Core.Bson +{ + public class BsonUtcSerializer : JsonSerializer + { + private class DateTimeConverter : JsonConverter + { + public override bool CanConvert(Type objectType) + { + return typeof(DateTime) == objectType + || typeof(DateTime?) == objectType; + } + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + var dateTimeOffset = (DateTime)value; + // Serialize DateTimeOffset as round-trip formatted string + serializer.Serialize(writer, dateTimeOffset.ToString("O")); + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + if (reader.TokenType != JsonToken.String && reader.TokenType != JsonToken.Date) + return null; + + DateTime dt; + + var dateWithOffset = (String)reader.Value; + + if (String.IsNullOrEmpty(dateWithOffset)) + return null; + + if (DateTime.TryParseExact(dateWithOffset, "O", CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out dt)) + return dt; + + return null; + } + + } + + private class DateTimeContractResolver : DefaultContractResolver + { + protected override JsonContract CreateContract(Type objectType) + { + var contract = base.CreateContract(objectType); + + if (objectType == typeof(DateTime) || objectType == typeof(DateTime?)) + contract.Converter = new DateTimeConverter(); + + return contract; + } + } + + public BsonUtcSerializer() + { + ContractResolver = new DateTimeContractResolver(); + DateTimeZoneHandling = DateTimeZoneHandling.RoundtripKind; + } + } +} diff --git a/Software/Visual_Studio/Tango.Core/Tango.Core.csproj b/Software/Visual_Studio/Tango.Core/Tango.Core.csproj index 2db9c8643..2ecd441f2 100644 --- a/Software/Visual_Studio/Tango.Core/Tango.Core.csproj +++ b/Software/Visual_Studio/Tango.Core/Tango.Core.csproj @@ -95,6 +95,7 @@ GlobalVersionInfo.cs + @@ -216,7 +217,7 @@ - + diff --git a/Software/Visual_Studio/Tango.Insights/InsightsApplicationException.cs b/Software/Visual_Studio/Tango.Insights/InsightsApplicationException.cs new file mode 100644 index 000000000..193412ae8 --- /dev/null +++ b/Software/Visual_Studio/Tango.Insights/InsightsApplicationException.cs @@ -0,0 +1,20 @@ +using LiteDB; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Tango.Insights +{ + public class InsightsApplicationException + { + [BsonId(true)] + [JsonIgnore] + public int Id { get; set; } + public DateTime Time { get; set; } + public String Exception { get; set; } + public bool IsApplicationCrash { get; set; } + } +} diff --git a/Software/Visual_Studio/Tango.Insights/InsightsFile.cs b/Software/Visual_Studio/Tango.Insights/InsightsFile.cs index 4eba06035..2bcb2f761 100644 --- a/Software/Visual_Studio/Tango.Insights/InsightsFile.cs +++ b/Software/Visual_Studio/Tango.Insights/InsightsFile.cs @@ -6,22 +6,33 @@ using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; +using Tango.Core.Bson; namespace Tango.Insights { public class InsightsFile { + private static JsonSerializer _serializer; + + static InsightsFile() + { + _serializer = new BsonUtcSerializer(); + } + public List Frames { get; set; } public List Events { get; set; } public List Statuses { get; set; } + public List ApplicationExceptions { get; set; } + public InsightsFile() { Frames = new List(); Events = new List(); Statuses = new List(); + ApplicationExceptions = new List(); } public Stream ToStream() @@ -35,8 +46,7 @@ namespace Tango.Insights { using (BsonWriter writer = new BsonWriter(ms)) { - JsonSerializer serializer = new JsonSerializer(); - serializer.Serialize(writer, this); + _serializer.Serialize(writer, this); ms.Position = 0; return ms.ToArray(); } @@ -52,8 +62,7 @@ namespace Tango.Insights { using (BsonReader reader = new BsonReader(stream)) { - JsonSerializer serializer = new JsonSerializer(); - return serializer.Deserialize(reader); + return _serializer.Deserialize(reader); } } diff --git a/Software/Visual_Studio/Tango.Insights/InsightsListener.cs b/Software/Visual_Studio/Tango.Insights/InsightsListener.cs index 6a9e7726b..f57753cfc 100644 --- a/Software/Visual_Studio/Tango.Insights/InsightsListener.cs +++ b/Software/Visual_Studio/Tango.Insights/InsightsListener.cs @@ -25,6 +25,7 @@ namespace Tango.Insights private int _timerCount; private int _deleteTicks; private bool _writing; + private bool _emptyWritten; public bool IsStarted { get; private set; } public TimeSpan SamplingInterval { get; set; } @@ -164,16 +165,21 @@ namespace Tango.Insights queue.Clear(); frame = InsightsFrame.FromInsightsMonitors(monitorsAvg, DateTime.UtcNow.Subtract(SamplingInterval)); + _emptyWritten = false; + _manager.InsertFrame(frame); + Debug.WriteLine("Insights frame inserted."); } else { - frame = InsightsFrame.CreateEmpty(DateTime.UtcNow.Subtract(SamplingInterval)); + if (!_emptyWritten) + { + frame = InsightsFrame.CreateEmpty(DateTime.UtcNow.Subtract(SamplingInterval)); + _emptyWritten = true; + _manager.InsertFrame(frame); + Debug.WriteLine("Insights empty frame inserted."); + } } - _manager.InsertFrame(frame); - - Debug.WriteLine("Insights frame inserted."); - _timerCount++; if (_timerCount >= _deleteTicks) @@ -185,6 +191,9 @@ namespace Tango.Insights deleted = _manager.DeleteStatuses(maxDate); Debug.WriteLine($"{deleted} insights machine statuses deleted."); + + deleted = _manager.DeleteApplicationExceptions(maxDate); + Debug.WriteLine($"{deleted} insights application exceptions deleted."); } } catch (Exception ex) diff --git a/Software/Visual_Studio/Tango.Insights/InsightsManager.cs b/Software/Visual_Studio/Tango.Insights/InsightsManager.cs index 73b74bff6..b187279b4 100644 --- a/Software/Visual_Studio/Tango.Insights/InsightsManager.cs +++ b/Software/Visual_Studio/Tango.Insights/InsightsManager.cs @@ -15,6 +15,9 @@ namespace Tango.Insights { private const string INSIGHTS_COLLECTION = "Insights"; private const string STATUSES_COLLECTION = "Statuses"; + private const string APPLICATION_EXCEPTIONS_COLLECTION = "ApplicationExceptions"; + + private bool _disposed; private static InsightsManager _instance; public static InsightsManager Default @@ -39,6 +42,9 @@ namespace Tango.Insights DatabasePath = databasePath; Directory.CreateDirectory(Path.GetDirectoryName(DatabasePath)); _database = new LiteDatabase($"Filename={DatabasePath}"); + _database.Pragma("TIMEOUT", 10); //Read Timeout + _database.Pragma("UTC_DATE", true); //Keep time as UTC when getting data + _database.Commit(); } public virtual void Dispose() @@ -47,6 +53,7 @@ namespace Tango.Insights { try { + _disposed = true; _database.Dispose(); _database = null; } @@ -69,18 +76,34 @@ namespace Tango.Insights return _database.GetCollection(STATUSES_COLLECTION); } + private ILiteCollection GetApplicationExceptionsCollection() + { + return _database.GetCollection(APPLICATION_EXCEPTIONS_COLLECTION); + } + public virtual void InsertFrame(InsightsFrame frame) { + if (_disposed) return; + var collection = GetInsightsCollection(); collection.Insert(frame); } public virtual void InsertStatus(InsightsStatus status) { + if (_disposed) return; var collection = GetStatusesCollection(); collection.Insert(status); } + public virtual void InsertApplicationException(InsightsApplicationException appException) + { + if (_disposed) return; + var collection = GetApplicationExceptionsCollection(); + appException.Time = appException.Time.Subtract(TimeSpan.FromSeconds(30)); + collection.Insert(appException); + } + public virtual List GetFrames(DateTime startUTC, DateTime endUTC) { var collection = GetInsightsCollection(); @@ -93,20 +116,36 @@ namespace Tango.Insights return collection.Find(x => x.Time >= startUTC && x.Time <= endUTC).ToList().OrderBy(x => x.Time).ToList(); } + public virtual List GetApplicationExceptions(DateTime startUTC, DateTime endUTC) + { + var collection = GetApplicationExceptionsCollection(); + return collection.Find(x => x.Time >= startUTC && x.Time <= endUTC).ToList().OrderBy(x => x.Time).ToList(); + } + public virtual int DeleteFrames(DateTime maxDateUTC) { + if (_disposed) return 0; var collection = GetInsightsCollection(); return collection.DeleteMany(x => x.Time < maxDateUTC); } public virtual int DeleteStatuses(DateTime maxDateUTC) { + if (_disposed) return 0; var collection = GetStatusesCollection(); return collection.DeleteMany(x => x.Time < maxDateUTC); } + public virtual int DeleteApplicationExceptions(DateTime maxDateUTC) + { + if (_disposed) return 0; + var collection = GetApplicationExceptionsCollection(); + return collection.DeleteMany(x => x.Time < maxDateUTC); + } + public DateTime? GetFramesMinDate() { + if (_disposed) return null; var collection = GetInsightsCollection(); if (collection.Count() > 0) @@ -133,9 +172,10 @@ namespace Tango.Insights Description = x.Description }) .ToList() + .Where(x => (EventTypes)x.EventCode != EventTypes.APPLICATION_EXCEPTION) .Select(x => new InsightsEvent() { - Time = new DateTime(x.Time.Ticks, DateTimeKind.Utc), + Time = x.Time, EventCode = x.EventCode, Description = ((EventTypes)x.EventCode == EventTypes.JOB_FAILED ? x.Description : null) }) diff --git a/Software/Visual_Studio/Tango.Insights/Tango.Insights.csproj b/Software/Visual_Studio/Tango.Insights/Tango.Insights.csproj index 1e440a62c..ed136a8c6 100644 --- a/Software/Visual_Studio/Tango.Insights/Tango.Insights.csproj +++ b/Software/Visual_Studio/Tango.Insights/Tango.Insights.csproj @@ -44,6 +44,7 @@ GlobalVersionInfo.cs + diff --git a/Software/Visual_Studio/Tango.Logging/GlobalExceptionTrapper.cs b/Software/Visual_Studio/Tango.Logging/GlobalExceptionTrapper.cs index fc791ff4a..3409ffdc4 100644 --- a/Software/Visual_Studio/Tango.Logging/GlobalExceptionTrapper.cs +++ b/Software/Visual_Studio/Tango.Logging/GlobalExceptionTrapper.cs @@ -21,6 +21,7 @@ namespace Tango.Logging public class WpfGlobalExceptionTrapper : IGlobalExceptionTrapper { private DateTime _lastGlobalExceptionTime = DateTime.Now.AddMinutes(-1); + private Application _app; /// /// Occurs when the global exception trapper has detected an unhandled exception. @@ -33,12 +34,24 @@ namespace Tango.Logging /// The application. public void Initialize(Application app) { + _app = app; AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; app.Dispatcher.UnhandledException += Dispatcher_UnhandledException; Application.Current.DispatcherUnhandledException += Current_DispatcherUnhandledException; TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException; } + /// + /// Use only when need to simulate application crash! + /// + public void Disable() + { + AppDomain.CurrentDomain.UnhandledException -= CurrentDomain_UnhandledException; + _app.Dispatcher.UnhandledException -= Dispatcher_UnhandledException; + Application.Current.DispatcherUnhandledException -= Current_DispatcherUnhandledException; + TaskScheduler.UnobservedTaskException -= TaskScheduler_UnobservedTaskException; + } + /// /// Handles the UnobservedTaskException event of the TaskScheduler control. /// diff --git a/Software/Visual_Studio/Tango.Transport/GenericMessageSerializer.cs b/Software/Visual_Studio/Tango.Transport/GenericMessageSerializer.cs index bb483e19a..6368a7754 100644 --- a/Software/Visual_Studio/Tango.Transport/GenericMessageSerializer.cs +++ b/Software/Visual_Studio/Tango.Transport/GenericMessageSerializer.cs @@ -10,6 +10,7 @@ using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; +using Tango.Core.Bson; using Tango.PMR; using Tango.PMR.Common; using Tango.PMR.Integration; @@ -18,60 +19,11 @@ namespace Tango.Transport { public static class GenericMessageSerializer { - private class DateTimeConverter : JsonConverter - { - public override bool CanConvert(Type objectType) - { - return typeof(DateTime) == objectType - || typeof(DateTime?) == objectType; - } - - public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) - { - var dateTimeOffset = (DateTime)value; - // Serialize DateTimeOffset as round-trip formatted string - serializer.Serialize(writer, dateTimeOffset.ToString("O")); - } - - public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) - { - if (reader.TokenType != JsonToken.String && reader.TokenType != JsonToken.Date) - return null; - - DateTime dt; - - var dateWithOffset = (String)reader.Value; - - if (String.IsNullOrEmpty(dateWithOffset)) - return null; - - if (DateTime.TryParseExact(dateWithOffset, "O", CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out dt)) - return dt; - - return null; - } - - } - - private class DateTimeContractResolver : DefaultContractResolver - { - protected override JsonContract CreateContract(Type objectType) - { - var contract = base.CreateContract(objectType); - - if (objectType == typeof(DateTime) || objectType == typeof(DateTime?)) - contract.Converter = new DateTimeConverter(); - - return contract; - } - } - private static JsonSerializer _serializer; static GenericMessageSerializer() { - _serializer = new JsonSerializer() { ContractResolver = new DateTimeContractResolver() }; - _serializer.DateTimeZoneHandling = DateTimeZoneHandling.RoundtripKind; + _serializer = new BsonUtcSerializer(); ProtoBuf.Meta.RuntimeTypeModel.Default.AutoAddMissingTypes = true; ProtoBuf.Meta.RuntimeTypeModel.Default.AutoAddProtoContractTypesOnly = false; -- cgit v1.3.1