using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Data; using Tango.Core.Commands; using Tango.Core.Helpers; using Tango.FSE.Common; using Tango.FSE.Common.Connection; using Tango.FSE.Common.Dialogs; using Tango.FSE.Common.Logging; using Tango.FSE.Firmware.Contracts; using Tango.FSE.Firmware.Navigation; using Tango.Integration.Logging; using Tango.Logging; using Tango.SharedUI.Components; using static Tango.SharedUI.Controls.NavigationControl; namespace Tango.FSE.Firmware.ViewModels { public class LogsViewVM : FSEViewModel, INavigationViewModel { private bool _loaded; private PeekLogsNavigationObject _peekNavigationObject; private List> _logFiles; /// /// Gets or sets the remote log files. /// public List> LogFiles { get { return _logFiles; } set { _logFiles = value; RaisePropertyChangedAuto(); } } private RemoteLogFileModel _selectedLogFile; /// /// Gets or sets the selected remote log file. /// public RemoteLogFileModel SelectedLogFile { get { return _selectedLogFile; } set { _selectedLogFile = value; RaisePropertyChangedAuto(); OnSelectedLogFileChanged(); InvalidateRelayCommands(); } } private ObservableCollection _firmwareLogs; /// /// Gets or sets the firmware logs. /// public ObservableCollection FirmwareLogs { get { return _firmwareLogs; } set { _firmwareLogs = value; RaisePropertyChangedAuto(); } } /// /// Gets or sets the firmware logs view. /// public ICollectionView FirmwareLogsView { get; set; } /// /// Gets or sets the selected firmware logs categories. /// public SelectedObjectCollection SelectedFirmwareLogsCategories { get; set; } private String _firmwareLogsFilter; /// /// Gets or sets the firmware logs filter. /// public String FirmwareLogsFilter { get { return _firmwareLogsFilter; } set { _firmwareLogsFilter = value; RaisePropertyChangedAuto(); FirmwareLogsView.Refresh(); } } /// /// Opens the detailed firmware log dialog. /// public RelayCommand OpenFirmwareLogItemCommand { get; set; } /// /// Exports the selected log file to local disk. /// public RelayCommand ExportLogFileCommand { get; set; } /// /// Exports all the downloaded log files to disk. /// public RelayCommand ExportAllDownloadedLogFilesCommand { get; set; } /// /// Downloads all the available log files. /// public RelayCommand DownloadAllLogFilesCommand { get; set; } /// /// Re-downloads the selected log file. /// public RelayCommand ReDownloadSelectedLogFileCommand { get; set; } /// /// Initializes a new instance of the class. /// public LogsViewVM() { FirmwareLogs = new ObservableCollection(); InitCollectionView(); SelectedFirmwareLogsCategories = new SelectedObjectCollection(new ObservableCollection() { LogCategory.Info, LogCategory.Warning, LogCategory.Error, LogCategory.Critical, LogCategory.Debug, }, new ObservableCollection() { LogCategory.Info, LogCategory.Warning, LogCategory.Error, LogCategory.Critical, LogCategory.Debug, }); SelectedFirmwareLogsCategories.SynchedSource.CollectionChanged += (_, __) => FirmwareLogsView.Refresh(); OpenFirmwareLogItemCommand = new RelayCommand(OpenFirmwareLogItem); ExportLogFileCommand = new RelayCommand(ExportSelectedLogFile, () => SelectedLogFile != null); ExportAllDownloadedLogFilesCommand = new RelayCommand(ExportAllDownloadedLogFiles); DownloadAllLogFilesCommand = new RelayCommand(DownloadAllLogFiles); ReDownloadSelectedLogFileCommand = new RelayCommand(ReDownloadSelectedLogFile, () => SelectedLogFile != null); RegisterForMessage(HandlePeekLogsNavigationObject); } private void InitCollectionView() { FirmwareLogsView = CollectionViewSource.GetDefaultView(FirmwareLogs); FirmwareLogsView.Filter = FilterFirmwareLogs; } public override void OnApplicationStarted() { base.OnApplicationStarted(); MachineProvider.MachineConnected += MachineProvider_MachineConnected; } private void MachineProvider_MachineConnected(object sender, MachineConnectedEventArgs e) { _loaded = false; if (MachineProvider.IsPPCAvailable && IsVisible) { InvokeUI(async () => { await LoadLogFiles(); }); } } public override async void OnNavigatedTo() { base.OnNavigatedTo(); if (!_loaded && MachineProvider.IsPPCAvailable) { await LoadLogFiles(); } if (MachineProvider.IsPPCAvailable) { if (_peekNavigationObject != null) { PeekApplicationLogs(_peekNavigationObject); } } } private async Task LoadLogFiles() { if (!IsFree || !MachineProvider.IsPPCAvailable) return; try { IsFree = false; var logFiles = await LoggingProvider.GetFirmwareLogFiles(); LogFiles = logFiles.Select(x => { var model = new RemoteLogFileModel(new EmbeddedLogFileParser()); model.RemoteLogFile = x; model.DownloadCompleted += OnRemoteLogFileDownloadCompleted; return model; }).ToList(); SelectedLogFile = LogFiles.FirstOrDefault(); _loaded = true; } catch (Exception ex) { LogManager.Log(ex, "Error loading remote firmware log files."); NotificationProvider.PushErrorReportingSnackbar(ex, "Firmware Module Error", "Could not initialize the remote Firmware logs history."); } finally { IsFree = true; } } private void OnRemoteLogFileDownloadCompleted(object sender, EventArgs e) { if (SelectedLogFile == sender) { OnSelectedLogFileChanged(); } } private void OnSelectedLogFileChanged() { if (SelectedLogFile == null) return; InvokeUI(() => { FirmwareLogs = new ObservableCollection(SelectedLogFile.LogItems); InitCollectionView(); }); } private async void OpenFirmwareLogItem(EmbeddedLogItem logItem) { await NotificationProvider.ShowDialog(new FirmwareLogItemViewVM() { LogItem = logItem }); } private bool FilterFirmwareLogs(object obj) { var log = obj as LogItemBase; return SelectedFirmwareLogsCategories.SynchedSource.Contains(log.Category) && (String.IsNullOrWhiteSpace(FirmwareLogsFilter) || log.Message.ToLower().Contains(FirmwareLogsFilter.ToLower())); } private async void ExportSelectedLogFile() { if (SelectedLogFile != null && SelectedLogFile.Status == RemoteLogFileStatus.Downloaded) { var result = await StorageProvider.SaveFile("Export Log File", "Firmware Log Files|*.log", SelectedLogFile.RemoteLogFile.Name, ".log"); if (result) { using (NotificationProvider.PushTaskItem("Exporting log file...")) { try { File.Copy(SelectedLogFile.TemporaryFile, result.SelectedItem, true); await NotificationProvider.ShowSuccess("Log file exported successfully."); } catch (Exception ex) { LogManager.Log(ex, "Error exporting firmware log file."); await NotificationProvider.ShowError($"Could not export the log file.\n{ex.FlattenMessage()}"); } } } } else { await NotificationProvider.ShowError("Please download the selected log file before trying to export."); } } private async void ExportAllDownloadedLogFiles() { var toExport = LogFiles.Where(x => x.Status == RemoteLogFileStatus.Downloaded).ToList(); if (toExport.Count == 0) { await NotificationProvider.ShowError("Please download log files before trying to export."); return; } var result = await StorageProvider.SelectFolder("Export Log Files"); if (result) { var count = toExport.Count; using (var task = NotificationProvider.PushTaskItem("Exporting log files...")) { foreach (var logFile in toExport.ToList()) { try { await Task.Delay(500); File.Copy(logFile.TemporaryFile, Path.Combine(result.SelectedItem, logFile.RemoteLogFile.Name), true); toExport.Remove(logFile); task.UpdateProgress("Exporting log files...", count - toExport.Count, count, false); } catch (Exception ex) { LogManager.Log(ex, $"Error exporting firmware log file '{logFile.RemoteLogFile.Name}'."); await NotificationProvider.ShowError($"Could not export '{logFile.RemoteLogFile.Name}'.\n{ex.FlattenMessage()}"); } } } await NotificationProvider.ShowSuccess($"Successfully exported {count - toExport.Count} out of {count} log files."); } } private async void DownloadAllLogFiles() { var toDownload = LogFiles.Where(x => x.Status == RemoteLogFileStatus.None || x.Status == RemoteLogFileStatus.Failed).ToList(); if (toDownload.Count == 0) { await NotificationProvider.ShowInfo("All log files have been downloaded."); return; } var totalSize = FileHelper.GetFriendlyFileSize(toDownload.Select(x => x.RemoteLogFile.Length).Sum()); if (await NotificationProvider.ShowWarningQuestion($"Are you sure you wish to download the entire history of log files?\nTotal size: {totalSize}", "DOWNLOAD", "CANCEL")) { foreach (var logFile in toDownload) { logFile.DownloadLogFile(); } } } private void ReDownloadSelectedLogFile() { if (SelectedLogFile != null) { SelectedLogFile.Status = RemoteLogFileStatus.None; SelectedLogFile.DownloadLogFile(); } } private void HandlePeekLogsNavigationObject(PeekLogsNavigationObject obj) { if (!IsVisible) { _peekNavigationObject = obj; } else { PeekApplicationLogs(obj); } } private async void PeekApplicationLogs(PeekLogsNavigationObject obj) { _peekNavigationObject = null; var closestLogFile = LogFiles.Where(x => x.RemoteLogFile.DateCreated < obj.Time).OrderBy(x => x.RemoteLogFile.DateCreated).LastOrDefault(); if (closestLogFile != null) { if (closestLogFile.RemoteLogFile.DateModified > obj.Time) { using (NotificationProvider.PushTaskItem("Peeking firmware log items...")) { SelectedLogFile = closestLogFile; //Found proper log file.. if (closestLogFile.Status != RemoteLogFileStatus.Downloaded) { bool completed = false; closestLogFile.DownloadCompleted += async (_, __) => { if (!completed) { using (NotificationProvider.PushTaskItem("Peeking firmware log items...")) { completed = true; var localTime = obj.Time.ToLocalTime(); var log = closestLogFile.LogItems.Where(x => x.TimeStamp < localTime).OrderBy(x => x.TimeStamp).LastOrDefault(); if (log != null) { await Task.Delay(2000); InvokeUI(() => { View.ScrollToLogFile(SelectedLogFile); View.ScrollToLog(log); }); } } } }; closestLogFile.DownloadLogFile(); } else { var localTime = obj.Time.ToLocalTime(); var log = closestLogFile.LogItems.Where(x => x.TimeStamp < localTime).OrderBy(x => x.TimeStamp).LastOrDefault(); if (log != null) { await Task.Delay(2000); View.ScrollToLogFile(SelectedLogFile); View.ScrollToLog(log); } } } } else { await NotificationProvider.ShowError("Could not correlate any log item from the available log files."); } } else { await NotificationProvider.ShowError("Could not correlate any log item from the available log items."); } } } }