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
}
}