using System; using System.Collections.Generic; using System.Diagnostics; using System.Drawing; using System.IO; using System.Linq; using System.Management; using System.Text; using System.Threading.Tasks; using System.Windows; using Tango.BL.Entities; using Tango.BL.Enumerations; using Tango.Core; using Tango.Core.DI; using Tango.Core.ExtensionMethods; using Tango.Core.Helpers; using Tango.FSE.BL; using Tango.FSE.Common; using Tango.FSE.Common.Authentication; using Tango.FSE.Common.BugReporting; using Tango.FSE.Common.Connection; using Tango.FSE.Common.Connectivity; using Tango.FSE.Common.FSEApplication; using Tango.FSE.Common.Helpers; using Tango.FSE.Common.Logging; using Tango.FSE.Common.Notifications; using Tango.FSE.Common.RemoteDesktop; using Tango.FSE.UI.Dialogs; using Tango.FSE.Web.Messages; using Tango.Integration.ExternalBridge; using Tango.Integration.Operation; using Tango.Logging; using Tango.RemoteDesktop; using Tango.RemoteDesktop.CaptureMethods; using Tango.RemoteDesktop.Encoders; using Tango.TFS; namespace Tango.FSE.UI.BugReporting { [TangoCreateWhenRegistered] public class DefaultBugReporter : FSEExtendedObject, IBugReporter { private bool _isInitialized; private Project Project; private TeamFoundationServiceClient _tfsClient; private BugReportingInfoResponse _bugReportingInfoResponse; private Bitmap _desktopBitmap; private IFSEApplicationManager ApplicationManager { get; set; } [TangoInject(TangoInjectMode.WhenAvailable)] private INotificationProvider NotificationProvider { get; set; } [TangoInject(TangoInjectMode.WhenAvailable)] private IMachineProvider MachineProvider { get; set; } [TangoInject(TangoInjectMode.WhenAvailable)] private IAuthenticationProvider AuthenticationProvider { get; set; } [TangoInject(TangoInjectMode.WhenAvailable)] private IConnectivityProvider ConnectivityProvider { get; set; } [TangoInject(TangoInjectMode.WhenAvailable)] private FSEServicesContainer Services { get; set; } [TangoInject(TangoInjectMode.WhenAvailable)] private ILoggingProvider LoggingProvider { get; set; } [TangoInject(TangoInjectMode.WhenAvailable)] private IRemoteDesktopProvider RemoteDesktopProvider { get; set; } /// /// Initializes a new instance of the class. /// /// The application manager. public DefaultBugReporter(IFSEApplicationManager applicationManager) { ApplicationManager = applicationManager; applicationManager.ApplicationReady += ApplicationManager_ApplicationReady; } /// /// Submits the specified bug. /// /// The bug. /// public Task SubmitBug(Bug bug) { return Task.Factory.StartNew(() => { if (!_isInitialized) { Initialize().GetAwaiter().GetResult(); } else { if (!ConnectivityProvider.CheckOnline()) { throw new InternetConnectionException(); } } LogManager.Log("Submitting report..."); WorkItem item = new WorkItem(); var tempFolder = TemporaryManager.CreateFolder(); item.Title = bug.Title; item.Description = bug.Description; item.LoggedInUser = AuthenticationProvider.CurrentUser.Email; if (bug.Area != null) { item.Area = bug.Area; } else { item.Area = Project.GetAreaByName("FSE Public"); if (item.Area == null) { throw new NullReferenceException("Area 'FSE Public' not found."); } } item.Iteration = Project.Iterations.FirstOrDefault(); var twineUser = Project.Members.SingleOrDefault(x => x.Email.ToLower().Trim() == AuthenticationProvider.CurrentUser.Email.ToLower().Trim()); if (twineUser != null && bug.AssignedTo != null) { LogManager.Log($"Bug is created by '{twineUser.Email}' and is assigned to '{bug.AssignedTo.Email}'..."); item.CreatedBy = twineUser; item.ChangedBy = twineUser; item.AuthorizedAs = twineUser; item.AssignedTo = bug.AssignedTo; } else { var fseMember = Project.Members.SingleOrDefault(x => x.Email.ToLower() == _bugReportingInfoResponse.UserEmail.ToLower()); if (fseMember == null) { throw new KeyNotFoundException("Could not locate FSE team member."); } item.CreatedBy = fseMember; item.ChangedBy = fseMember; item.AuthorizedAs = fseMember; item.AssignedTo = bug.AssignedTo != null ? bug.AssignedTo : fseMember; LogManager.Log($"Bug is created by '{fseMember.Email}' and is assigned to '{item.AssignedTo.Email}'..."); } item.StepsToReproduce = bug.StepsToReproduce.ToStringOrEmpty(); item.FoundInBuild = ApplicationManager.Version.ToString(); item.Priority = Priority.Priority3; item.Severity = Severity.Medium; item.State = State.New; item.Type = WorkItemType.Bug; item.Environment = AuthenticationProvider.CurrentEnvironment.Description; item.MachineType = (MachineType)Settings.LastConnectedMachineType; bool hasPPCLogs = false; bool hasFirmwareLogs = false; bool hasFSELogs = false; FileLogger appFileLogger = LogManager.Default.RegisteredLoggers.FirstOrDefault(x => x.GetType() == typeof(FileLogger)) as FileLogger; if (appFileLogger != null) { LogManager.Log($"Attaching application log file ${appFileLogger.LogFile}"); string[] logFiles = GetLogFiles(appFileLogger); foreach (string file in logFiles) { var appLogFile = tempFolder.CreateImaginaryFile(); File.Copy(file, appLogFile.Path); item.Attachments.Add(new Attachment() { Description = "Application Log File", FilePath = appLogFile.Path, Name = Path.GetFileName(file), }); hasFSELogs = true; } } if (MachineOperator.EnableSessionLogFile) { var file = MachineOperator.SessionLogger.LogFile; if (file != null && File.Exists(file)) { var sessionLogFile = tempFolder.CreateImaginaryFile(); File.Copy(file, sessionLogFile.Path); item.Attachments.Add(new Attachment() { Description = "Session Log File", FilePath = sessionLogFile.Path, Name = Path.GetFileName(file), }); } } if (MachineProvider.IsConnected && MachineProvider.IsPPCAvailable) { var tempLogsFolder = TemporaryManager.CreateFolder(); try { var ppcLogFiles = LoggingProvider.GetApplicationLogFiles().Result; var ppcLogFile = ppcLogFiles.OrderBy(x => x.DateCreated).LastOrDefault(); if (ppcLogFile != null) { var ppcLogFilePath = Path.Combine(tempLogsFolder, ppcLogFile.Name); var handler = LoggingProvider.DownloadLogFile(ppcLogFile, ppcLogFilePath).Result; handler.WaitForCompletion().GetAwaiter().GetResult(); item.Attachments.Add(new Attachment() { Description = "PPC Log File", FilePath = ppcLogFilePath, Name = ppcLogFile.Name }); hasPPCLogs = true; } } catch (Exception ex) { LogManager.Log(ex, "Error attaching remote PPC log file."); } try { var embeddedLogFiles = LoggingProvider.GetFirmwareLogFiles().Result; var embeddedLogFile = embeddedLogFiles.OrderBy(x => x.DateCreated).LastOrDefault(); if (embeddedLogFile != null) { var embeddedLogFilePath = Path.Combine(tempLogsFolder, embeddedLogFile.Name); var handler = LoggingProvider.DownloadLogFile(embeddedLogFile, embeddedLogFilePath).Result; handler.WaitForCompletion().GetAwaiter().GetResult(); item.Attachments.Add(new Attachment() { Description = "Embedded Log File", FilePath = embeddedLogFilePath, Name = embeddedLogFile.Name }); hasFirmwareLogs = true; } } catch (Exception ex) { LogManager.Log(ex, "Error attaching remote embedded log file."); } try { var frame = RemoteDesktopProvider.GetDesktopScreenShot().Result; var ppcDesktopFile = TemporaryManager.CreateImaginaryFile(".jpg"); File.WriteAllBytes(ppcDesktopFile, frame.ToEncoder().ToArray()); frame.Dispose(); item.Attachments.Add(new Attachment() { Description = "PPC Screen Capture", FilePath = ppcDesktopFile, Name = "PPC Screen Capture.jpg" }); } catch (Exception ex) { LogManager.Log(ex, "Error retrieving remote PPC screenshot for bug report."); } } if (_desktopBitmap != null) { var tmpBitmap = TemporaryManager.CreateImaginaryFile(".jpg"); _desktopBitmap.SaveJpeg(tmpBitmap, 80); item.Attachments.Add(new Attachment() { Description = "FSE Screen Capture", FilePath = tmpBitmap, Name = "FSE Screen Capture.jpg" }); _desktopBitmap = null; } foreach (var attachment in bug.Attachments.ToList()) { if (File.Exists(attachment.File)) { item.Attachments.Add(new Attachment() { Description = attachment.Description, FilePath = attachment.File, Name = attachment.Name, }); } } if (MachineProvider.IsConnected) { if (MachineProvider.MachineOperator.DeviceInformation != null) { item.EmbeddedVersion = MachineProvider.MachineOperator.DeviceInformation.Version; } item.MachineSerialNumber = MachineProvider.Machine.SerialNumber; } SystemInformationModel sysModel = new SystemInformationModel(); sysModel.FSEVersion = ApplicationManager.Version.ToString(); sysModel.PPCVersion = "N/A"; sysModel.EmbeddedVersion = "N/A"; sysModel.HostName = Environment.MachineName; sysModel.UserName = AuthenticationProvider.CurrentUser.Contact.FullName; sysModel.OsVersion = GetOsVersion(); sysModel.UserEmail = AuthenticationProvider.CurrentUser.Email; sysModel.Description = bug.Description; sysModel.Comments = bug.Comments != null ? bug.Comments : "No comments."; sysModel.Exception = bug.Exception != null ? bug.Exception.FlattenException() : "No exception."; IMachineOperator op = MachineProvider.MachineOperator; LogManager.Log("Checking machine status..."); if (MachineProvider.IsConnected) { LogManager.Log("Machine is connected. Getting device information and configurations..."); sysModel.Machine = MachineProvider.Machine; if (MachineProvider.MachineOperator.DeviceInformation != null) { sysModel.EmbeddedVersion = op.DeviceInformation.Version; } var m = op as ExternalBridgeTcpClient; if (m != null && m.ApplicationInformation != null) { sysModel.PPCVersion = m.ApplicationInformation.Version; } } else { LogManager.Log("Machine is disconnected."); } String html = String.Empty; LogManager.Log("Generating HTML system information from template..."); using (var stream = EmbeddedResourceHelper.GetEmbeddedResourceStream("Tango.FSE.UI.BugReporting.SystemInformationTemplate.cshtml")) { StreamReader reader = new StreamReader(stream); html = reader.ReadToEnd(); } item.SystemInformation = String.Empty; item.StepsToReproduce = String.Format("
{0}
", item.StepsToReproduce); LogManager.Log("Uploading work item..."); var workItem = _tfsClient.UploadWorkItem(Project, item).Result; bug.URL = $"https://twinetfs.visualstudio.com/Tango/_workitems/edit/{workItem.ID}"; if (hasFSELogs) { String encodedData = StartupArgsHelper.EncodeBase64String($"-remote FSE {workItem.ID}"); sysModel.Actions.Add(new ActionModel() { Name = "Open FSE logs on Log Viewer", Link = $@"Tango.FSE.LogViewer:\\{encodedData}", }); } if (hasPPCLogs) { String encodedData = StartupArgsHelper.EncodeBase64String($"-remote PPC {workItem.ID}"); sysModel.Actions.Add(new ActionModel() { Name = "Open PPC logs on Log Viewer", Link = $@"Tango.FSE.LogViewer:\\{encodedData}", }); } if (hasFirmwareLogs) { String encodedData = StartupArgsHelper.EncodeBase64String($"-remote Embedded {workItem.ID}"); sysModel.Actions.Add(new ActionModel() { Name = "Open Firmware logs on Log Viewer", Link = $@"Tango.FSE.LogViewer:\\{encodedData}", }); } FillPerformance(sysModel); String systemInformation = CodeGeneration.Helper.Parse(html, sysModel); LogManager.Log("Updating work item system information..."); workItem = _tfsClient.UpdateWorkItemSystemInfo(Project, item, systemInformation).Result; LogManager.Log("Deleting temporary folder..."); tempFolder.Delete(); LogManager.Log("Upload completed."); }); } /// /// Displays a bug submission dialog. When confirmed, will submit the specified bug. /// /// The bug. /// public async Task ShowBugReportDialog(Bug bug) { BugReportViewVM vm = null; try { _desktopBitmap?.Dispose(); GdiScreenCapture capture = new GdiScreenCapture(); _desktopBitmap = capture.GetDesktopBitmap(new CaptureRegion() { Left = (int)Application.Current.MainWindow.Left, Top = (int)Application.Current.MainWindow.Top, Width = (int)Application.Current.MainWindow.Width, Height = (int)Application.Current.MainWindow.Height, }); } catch (Exception ex) { LogManager.Log(ex, "Error capturing the user desktop bitmap for bug report."); } try { if (AuthenticationProvider.CurrentUser.HasPermission(Permissions.FSE_ModifyBugReport)) { await Task.Factory.StartNew(() => { if (!_isInitialized) { using (NotificationProvider.PushTaskItem("Initializing issue reporting service...")) { Initialize().GetAwaiter().GetResult(); } } else { if (!ConnectivityProvider.CheckOnline()) { throw new InternetConnectionException(); } } }); TeamMember assignedTo = Project.Members.SingleOrDefault(x => x.Email == Settings.LastReportAssignedTo); Area area = Project.GetAreaByName(Settings.LastReportArea); if (assignedTo == null) { assignedTo = Project.Members.SingleOrDefault(x => x.Email.ToLower() == _bugReportingInfoResponse.UserEmail.ToLower()); } if (area == null) { area = Project.GetAreaByName("FSE"); } vm = await NotificationProvider.ShowDialog(new BugReportFullViewVM(bug) { Areas = GetAreasWithoutFSEPublic(Project.Areas.ToList()), TeamMembers = Project.Members.ToList(), Area = area, AssignedTo = assignedTo }); if (vm.DialogResult) { Settings.LastReportAssignedTo = (vm as BugReportFullViewVM).AssignedTo.Email; Settings.LastReportArea = (vm as BugReportFullViewVM).Area.Name; Settings.Save(); } } else { vm = await NotificationProvider.ShowDialog(new BugReportViewVM(bug)); } } catch (Exception ex) { LogManager.Log(ex, "Error initializing bug report dialog."); await NotificationProvider.ShowError($"Error occurred while trying to initialize the bug report.\n{ex.FlattenMessage()}"); return; } if (vm.DialogResult) { var snackbar = NotificationProvider.PushProgressSnackbar("Report Issue", "Submitting report..."); try { await SubmitBug(vm.Bug); if (AuthenticationProvider.CurrentUser.HasPermission(Permissions.FSE_ModifyBugReport)) { snackbar.ProgressCompleted("Issue submitted successfully.\nTap to view the bug report on your browser.", TimeSpan.FromSeconds(8), () => { Process.Start(vm.Bug.URL); }); } else { snackbar.ProgressCompleted("Issue submitted successfully. Thank you!", TimeSpan.FromSeconds(5)); } } catch (Exception ex) { LogManager.Log(ex, "Error submitting bug report."); snackbar.ProgressFailed("Error occurred while trying to submit the report.", TimeSpan.FromSeconds(2)); await NotificationProvider.ShowError($"Error occurred while trying to submit the report.\n{ex.FlattenMessage()}"); await ShowBugReportDialog(vm.Bug); } } } /// /// Displays a bug submission dialog. When confirmed, will submit the new created bug. /// /// public Task ShowBugReportDialog() { return ShowBugReportDialog(null); } private async void ApplicationManager_ApplicationReady(object sender, EventArgs e) { if (!_isInitialized) { try { await Initialize(); } catch { } } } private async Task Initialize() { if (await ConnectivityProvider.CheckOnlineAsync()) { try { _bugReportingInfoResponse = await Services.BugReportingService.GetBugReportingInfo(); _tfsClient = new TeamFoundationServiceClient(_bugReportingInfoResponse.CollectionUrl, String.Empty, _bugReportingInfoResponse.PersonalToken); Project = await _tfsClient.GetProject("Tango"); _isInitialized = true; } catch (Exception ex) { throw LogManager.Log(ex, "Error initializing bug reporter."); } } else { throw LogManager.Log(new InternetConnectionException()); } } private string[] GetLogFiles(FileLogger logger) { string[] fileEntries = new string[1]; fileEntries[0] = logger.LogFile; string fileName = Path.GetFileNameWithoutExtension(logger.LogFile); int indexPos = fileName.IndexOf(FileLogger.FILE_SET_EXTENSION); if (indexPos > 0) { string extension = Path.GetExtension(logger.LogFile); fileName = fileName.Substring(0, indexPos); fileEntries = Directory.GetFiles(logger.Folder, $"{fileName}*{extension}").Where(x => Path.GetFileName(x).StartsWith(logger.Tag)).OrderBy(x => x.Length).ThenBy(x => x).ToArray(); } return fileEntries; } private String GetOsVersion() { try { string r = ""; using (ManagementObjectSearcher searcher = new ManagementObjectSearcher("SELECT * FROM Win32_OperatingSystem")) { ManagementObjectCollection information = searcher.Get(); if (information != null) { foreach (ManagementObject obj in information) { r = obj["Caption"].ToString() + " - " + obj["OSArchitecture"].ToString(); } } r = r.Replace("NT 5.1.2600", "XP"); r = r.Replace("NT 5.2.3790", "Server 2003"); return r; } } catch { return Environment.OSVersion.ToString(); } } private List GetAreasWithoutFSEPublic(List areas) { List partial = new List(); CloneAreasWithoutFSEPublic(areas.ToList(), partial); return partial; } private void CloneAreasWithoutFSEPublic(List all, List partial) { foreach (var area in all) { if (area.Name != "FSE Public") { var cloned = new Area() { Name = area.Name, Path = area.Path }; partial.Add(cloned); CloneAreasWithoutFSEPublic(area.SubAreas, cloned.SubAreas); } } } public async Task> GetConnectedMachineBugs(DateTime startUtc, DateTime endUtc) { List list = new List(); if (!MachineProvider.IsConnected) { LogManager.Log("Could not get connected machine bugs while machine is not connected.", LogCategory.Warning); return new List(); } try { if (!_isInitialized) { await Initialize(); } var workItems = await _tfsClient.GetWorkItemsForMachine(Project, MachineProvider.Machine.SerialNumber, startUtc, endUtc); if (workItems.Count > 0) { return workItems.Where(x => x.CreatedDate >= startUtc && x.CreatedDate <= endUtc).Select(x => new Bug() { Area = x.Area, AssignedTo = x.AssignedTo, CreatedBy = x.CreatedBy, Comments = x.Comment, CreatedDate = x.CreatedDate, Description = x.Description, StepsToReproduce = x.StepsToReproduce, Title = x.Title, LoggedInUser = x.LoggedInUser.IsNotNullOrEmpty() ? x.LoggedInUser : x.CreatedBy.Email, Type = BugType.PPC, URL = $"https://twinetfs.visualstudio.com/Tango/_workitems/edit/{x.ID}" }).ToList(); } } catch (Exception ex) { LogManager.Log(ex, "Could not get machine work items from VSTS."); } return list; } #region System Performance private void FillPerformance(SystemInformationModel model) { try { var computerInfo = new Microsoft.VisualBasic.Devices.ComputerInfo(); model.RAM = FileHelper.GetFriendlyFileSize((long)computerInfo.TotalPhysicalMemory); } catch { } try { using (ManagementObjectSearcher mos = new ManagementObjectSearcher("root\\CIMV2", "SELECT * FROM Win32_Processor")) { foreach (ManagementObject mo in mos.Get()) { model.Processor = mo["Name"].ToStringSafe(); } } } catch { } try { using (var mos = new ManagementObjectSearcher("select * from Win32_VideoController")) { foreach (ManagementObject mo in mos.Get()) { model.GPU = mo["Name"].ToStringSafe(); } } } catch { } try { bool is64bit = !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("PROCESSOR_ARCHITEW6432")); model.Architecure = is64bit ? "x64" : "x86"; } catch { } try { var cPath = Path.GetPathRoot(Environment.GetFolderPath(Environment.SpecialFolder.System)); DriveInfo drive = new DriveInfo(cPath); model.FreeSpace = FileHelper.GetFriendlyFileSize(drive.TotalFreeSpace); } catch { } } #endregion } }