diff options
38 files changed, 1076 insertions, 78 deletions
diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/Tango.FSE.PPCConsole.csproj b/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/Tango.FSE.PPCConsole.csproj index 783dfa9fe..287273015 100644 --- a/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/Tango.FSE.PPCConsole.csproj +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/Tango.FSE.PPCConsole.csproj @@ -113,10 +113,14 @@ <Compile Include="PPCConsoleModule.cs" /> <Compile Include="ViewModels\ConsoleViewVM.cs" /> <Compile Include="ViewModels\FileSystemViewVM.cs" /> + <Compile Include="ViewModels\LogsViewVM.cs" /> <Compile Include="ViewModels\MainViewVM.cs" /> <Compile Include="ViewModels\MonitoringViewVM.cs" /> <Compile Include="ViewModels\RemoteDesktopViewVM.cs" /> <Compile Include="ViewModels\UpdatesViewVM.cs" /> + <Compile Include="Views\LogsView.xaml.cs"> + <DependentUpon>LogsView.xaml</DependentUpon> + </Compile> <Compile Include="Views\UpdatesView.xaml.cs"> <DependentUpon>UpdatesView.xaml</DependentUpon> </Compile> @@ -252,6 +256,10 @@ <SubType>Designer</SubType> <Generator>MSBuild:Compile</Generator> </Page> + <Page Include="Views\LogsView.xaml"> + <SubType>Designer</SubType> + <Generator>MSBuild:Compile</Generator> + </Page> <Page Include="Views\UpdatesView.xaml"> <Generator>MSBuild:Compile</Generator> <SubType>Designer</SubType> diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/ViewModelLocator.cs b/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/ViewModelLocator.cs index 4faa72e8e..a3b800b22 100644 --- a/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/ViewModelLocator.cs +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/ViewModelLocator.cs @@ -18,6 +18,7 @@ namespace Tango.FSE.PPCConsole TangoIOC.Default.Register<MonitoringViewVM>(); TangoIOC.Default.Register<FileSystemViewVM>(); TangoIOC.Default.Register<UpdatesViewVM>(); + TangoIOC.Default.Register<LogsViewVM>(); } public static MainViewVM MainViewVM @@ -67,5 +68,13 @@ namespace Tango.FSE.PPCConsole return TangoIOC.Default.GetInstance<UpdatesViewVM>(); } } + + public static LogsViewVM LogsViewVM + { + get + { + return TangoIOC.Default.GetInstance<LogsViewVM>(); + } + } } } diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/ViewModels/LogsViewVM.cs b/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/ViewModels/LogsViewVM.cs new file mode 100644 index 000000000..52955e18a --- /dev/null +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/ViewModels/LogsViewVM.cs @@ -0,0 +1,288 @@ +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; +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.FileSystem; +using Tango.FSE.Common.Logging; +using Tango.Logging; +using Tango.PPC.Shared.Logs; +using Tango.SharedUI.Components; +using static Tango.SharedUI.Controls.NavigationControl; + +namespace Tango.FSE.PPCConsole.ViewModels +{ + public class LogsViewVM : FSEViewModel, INavigationViewModel + { + private bool _loaded; + + private List<RemoteLogFileModel<LogItemBase>> _logFiles; + /// <summary> + /// Gets or sets the remote log files. + /// </summary> + public List<RemoteLogFileModel<LogItemBase>> LogFiles + { + get { return _logFiles; } + set { _logFiles = value; RaisePropertyChangedAuto(); } + } + + private RemoteLogFileModel<LogItemBase> _selectedLogFile; + /// <summary> + /// Gets or sets the selected remote log file. + /// </summary> + public RemoteLogFileModel<LogItemBase> SelectedLogFile + { + get { return _selectedLogFile; } + set { _selectedLogFile = value; RaisePropertyChangedAuto(); OnSelectedLogFileChanged(); } + } + + /// <summary> + /// Gets or sets the application logs. + /// </summary> + public ObservableCollection<LogItemBase> ApplicationLogs { get; set; } + + /// <summary> + /// Gets or sets the application logs view. + /// </summary> + public ICollectionView ApplicationLogsView { get; set; } + + /// <summary> + /// Gets or sets the selected application logs categories. + /// </summary> + public SelectedObjectCollection<LogCategory> SelectedApplicationLogsCategories { get; set; } + + private String _applicationLogsFilter; + /// <summary> + /// Gets or sets the application logs filter. + /// </summary> + public String ApplicationLogsFilter + { + get { return _applicationLogsFilter; } + set { _applicationLogsFilter = value; RaisePropertyChangedAuto(); ApplicationLogsView.Refresh(); } + } + + /// <summary> + /// Opens the detailed application log dialog. + /// </summary> + public RelayCommand<LogItemBase> OpenApplicationLogItemCommand { get; set; } + + /// <summary> + /// Exports the selected log file to local disk. + /// </summary> + public RelayCommand ExportLogFileCommand { get; set; } + + /// <summary> + /// Exports all the downloaded log files to disk. + /// </summary> + public RelayCommand ExportAllDownloadedLogFilesCommand { get; set; } + + /// <summary> + /// Downloads all the available log files. + /// </summary> + public RelayCommand DownloadAllLogFilesCommand { get; set; } + + /// <summary> + /// Initializes a new instance of the <see cref="LogsViewVM"/> class. + /// </summary> + public LogsViewVM() + { + ApplicationLogs = new ObservableCollection<LogItemBase>(); + ApplicationLogsView = CollectionViewSource.GetDefaultView(ApplicationLogs); + ApplicationLogsView.Filter = FilterApplicationLogs; + SelectedApplicationLogsCategories = new SelectedObjectCollection<LogCategory>(new ObservableCollection<LogCategory>() + { + LogCategory.Info, + LogCategory.Warning, + LogCategory.Error, + LogCategory.Critical, + }, new ObservableCollection<LogCategory>() + { + LogCategory.Info, + LogCategory.Warning, + LogCategory.Error, + LogCategory.Critical, + }); + + SelectedApplicationLogsCategories.SynchedSource.CollectionChanged += (_, __) => ApplicationLogsView.Refresh(); + + OpenApplicationLogItemCommand = new RelayCommand<LogItemBase>(OpenApplicationLogItem); + + ExportLogFileCommand = new RelayCommand(ExportSelectedLogFile); + ExportAllDownloadedLogFilesCommand = new RelayCommand(ExportAllDownloadedLogFiles); + DownloadAllLogFilesCommand = new RelayCommand(DownloadAllLogFiles); + } + + public override void OnApplicationStarted() + { + base.OnApplicationStarted(); + MachineProvider.MachineConnected += MachineProvider_MachineConnected; + } + + private void MachineProvider_MachineConnected(object sender, MachineConnectedEventArgs e) + { + if (e.DifferentFromPrevious) + { + _loaded = false; + + if (MachineProvider.ConnectionType.IsRemote() && IsVisible) + { + LoadLogFiles(); + } + } + } + + public override void OnNavigatedTo() + { + base.OnNavigatedTo(); + + if (!_loaded) + { + LoadLogFiles(); + } + } + + private async void LoadLogFiles() + { + if (!MachineProvider.ConnectionType.IsRemote() || !IsFree) return; + + try + { + IsFree = false; + var logFiles = await LoggingProvider.GetApplicationLogFiles(); + LogFiles = logFiles.Select(x => + { + + var model = new RemoteLogFileModel<LogItemBase>(new ApplicationLogFileParser()); + model.RemoteLogFile = x; + model.DownloadCompleted += OnRemoteLogFileDownloadCompleted; + return model; + + }).ToList(); + SelectedLogFile = LogFiles.FirstOrDefault(); + _loaded = true; + } + catch (Exception ex) + { + LogManager.Log(ex, "Error loading log files."); + } + finally + { + IsFree = true; + } + } + + private void OnRemoteLogFileDownloadCompleted(object sender, EventArgs e) + { + if (SelectedLogFile == sender) + { + OnSelectedLogFileChanged(); + } + } + + private void OnSelectedLogFileChanged() + { + if (SelectedLogFile == null) return; + + InvokeUI(() => + { + ApplicationLogs.Clear(); + foreach (var logItem in SelectedLogFile.LogItems) + { + ApplicationLogs.Add(logItem); + } + + ApplicationLogsView.Refresh(); + }); + } + + private async void OpenApplicationLogItem(LogItemBase logItem) + { + await NotificationProvider.ShowDialog(new ApplicationLogItemViewVM() { LogItem = logItem }); + } + + private bool FilterApplicationLogs(object obj) + { + var log = obj as LogItemBase; + return SelectedApplicationLogsCategories.SynchedSource.Contains(log.Category) && (String.IsNullOrWhiteSpace(ApplicationLogsFilter) || log.Message.ToLower().Contains(ApplicationLogsFilter.ToLower())); + } + + private async void ExportSelectedLogFile() + { + if (SelectedLogFile != null && SelectedLogFile.Status == RemoteLogFileStatus.Downloaded) + { + var result = await StorageProvider.SaveFile("Export Log File", "Application 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 application log file."); + await NotificationProvider.ShowError($"Could not export the log file.\n{ex.FlattenMessage()}"); + } + } + } + } + } + + private async void ExportAllDownloadedLogFiles() + { + var result = await StorageProvider.SelectFolder("Export Log Files"); + if (result) + { + var toExport = LogFiles.Where(x => x.Status == RemoteLogFileStatus.Downloaded).ToList(); + 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 application 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); + 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(); + } + } + } + } +} diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/ViewModels/MainViewVM.cs b/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/ViewModels/MainViewVM.cs index e18a75ef6..f4614ccf1 100644 --- a/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/ViewModels/MainViewVM.cs +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/ViewModels/MainViewVM.cs @@ -20,6 +20,7 @@ namespace Tango.FSE.PPCConsole.ViewModels MonitoringView, FileSystemView, UpdatesView, + LogsView, } private NavigationView _selectedView; diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/Views/LogsView.xaml b/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/Views/LogsView.xaml new file mode 100644 index 000000000..81258e971 --- /dev/null +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/Views/LogsView.xaml @@ -0,0 +1,225 @@ +<UserControl x:Class="Tango.FSE.PPCConsole.Views.LogsView" + 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:global="clr-namespace:Tango.FSE.PPCConsole" + xmlns:helpers="clr-namespace:Tango.SharedUI.Helpers;assembly=Tango.SharedUI" + xmlns:vm="clr-namespace:Tango.FSE.PPCConsole.ViewModels" + xmlns:components="clr-namespace:Tango.SharedUI.Components;assembly=Tango.SharedUI" + xmlns:controls="clr-namespace:Tango.FSE.Common.Controls;assembly=Tango.FSE.Common" + xmlns:material="http://materialdesigninxaml.net/winfx/xaml/themes" + xmlns:local="clr-namespace:Tango.FSE.PPCConsole.Views" + mc:Ignorable="d" + d:DesignHeight="450" d:DesignWidth="800" d:DataContext="{d:DesignInstance Type=vm:LogsViewVM, IsDesignTimeCreatable=False}" DataContext="{x:Static global:ViewModelLocator.LogsViewVM}" x:Name="control"> + + <UserControl.Resources> + + <Style x:Key="LogsGridCellStyle" TargetType="DataGridCell" BasedOn="{StaticResource {x:Type DataGridCell}}"> + <Setter Property="BorderThickness" Value="0"/> + <Setter Property="FocusVisualStyle" Value="{x:Null}"/> + <Setter Property="VerticalContentAlignment" Value="Center"></Setter> + </Style> + + <Style x:Key="LogsGridStyle" TargetType="DataGrid" BasedOn="{StaticResource {x:Type DataGrid}}"> + <Setter Property="Background" Value="Transparent"></Setter> + <Setter Property="Foreground" Value="{StaticResource FSE_GrayBrush}"></Setter> + <Setter Property="AutoGenerateColumns" Value="False"></Setter> + <Setter Property="CanUserAddRows" Value="False"></Setter> + <Setter Property="CanUserDeleteRows" Value="False"></Setter> + <Setter Property="CanUserReorderColumns" Value="False"></Setter> + <Setter Property="CanUserResizeColumns" Value="False"></Setter> + <Setter Property="CanUserResizeRows" Value="False"></Setter> + <Setter Property="CanUserSortColumns" Value="False"></Setter> + <Setter Property="IsReadOnly" Value="True"></Setter> + <Setter Property="SelectionMode" Value="Single"></Setter> + <Setter Property="SelectionUnit" Value="FullRow"></Setter> + <Setter Property="RowHeight" Value="35"></Setter> + <Setter Property="HorizontalGridLinesBrush" Value="{StaticResource FSE_PrimaryBackgroundBrush}"></Setter> + <Setter Property="HorizontalScrollBarVisibility" Value="Disabled"></Setter> + <Setter Property="CellStyle" Value="{StaticResource LogsGridCellStyle}" /> + </Style> + </UserControl.Resources> + + <Grid IsEnabled="{Binding IsFree}"> + <Grid Margin="0 20 0 0"> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="270"/> + <ColumnDefinition Width="303*"/> + </Grid.ColumnDefinitions> + + <Grid> + <Border Background="{StaticResource FSE_PrimaryBackgroundDarkBrush}" BorderBrush="{StaticResource FSE_PrimaryBackgroundLightBrush}" BorderThickness="3" CornerRadius="3" Padding="5 5"> + <ListBox Background="Transparent" BorderThickness="0" ItemsSource="{Binding LogFiles}" SelectedItem="{Binding SelectedLogFile}"> + <ListBox.InputBindings> + <KeyBinding Key="Return" Command="{Binding SelectedLogFile.DownloadCommand}" /> + </ListBox.InputBindings> + <ListBox.ItemTemplate> + <DataTemplate> + <DockPanel> + <DockPanel.Style> + <Style TargetType="DockPanel"> + <Setter Property="Opacity" Value="0.6"></Setter> + <Style.Triggers> + <DataTrigger Binding="{Binding Status}" Value="Downloaded"> + <Setter Property="Opacity" Value="1"></Setter> + </DataTrigger> + </Style.Triggers> + </Style> + </DockPanel.Style> + <material:PackIcon Kind="FileDocument" Width="Auto" Height="35" /> + <StackPanel Margin="5 0 0 0"> + <TextBlock Text="{Binding RemoteLogFile.DateCreated,Converter={StaticResource DateTimeUtcToLocalDateTime},StringFormat='MM/dd/yyyy HH:mm'}"></TextBlock> + <TextBlock Margin="0 5 0 0" Foreground="{StaticResource FSE_GrayBrush}" FontSize="{StaticResource FSE_SmallFontSize}"> + <Run Text="{Binding RemoteLogFile.Length,Converter={StaticResource ByteArrayToFileSizeConverter}}"></Run> + <Run>|</Run> + <Run Text="{Binding Duration,Mode=OneWay,StringFormat='hh\\:mm'}"></Run> + </TextBlock> + </StackPanel> + </DockPanel> + </DataTemplate> + </ListBox.ItemTemplate> + </ListBox> + </Border> + </Grid> + + <Border Grid.Column="1" Background="{StaticResource FSE_PrimaryBackgroundDarkBrush}" BorderBrush="{StaticResource FSE_PrimaryBackgroundLightBrush}" BorderThickness="3" CornerRadius="3" Padding="2" Margin="10 0 0 0"> + <Grid> + <Grid> + <DockPanel Margin="10 0 10 10"> + <Grid DockPanel.Dock="Top" Margin="12 10 0 0" Panel.ZIndex="100"> + <TextBlock Foreground="{StaticResource FSE_PrimaryAccentBrush}" FontWeight="SemiBold"> + <Run Text="{Binding SelectedLogFile.RemoteLogFile.DateCreated,Converter={StaticResource DateTimeUtcToLocalDateTime},StringFormat='MM/dd/yyyy HH:mm'}"></Run> + <Run>-</Run> + <Run Text="{Binding SelectedLogFile.RemoteLogFile.DateModified,Converter={StaticResource DateTimeUtcToLocalDateTime},StringFormat='HH:mm'}"></Run> + </TextBlock> + <StackPanel Margin="0 0 0 -70" HorizontalAlignment="Right" > + <StackPanel Margin="0 30 0 0" Orientation="Horizontal"> + <ItemsControl VerticalAlignment="Center" ItemsSource="{Binding SelectedApplicationLogsCategories}" Visibility="{Binding ResolutionService.IsHighResolution,Converter={StaticResource BooleanToVisibilityConverter}}"> + <ItemsControl.ItemsPanel> + <ItemsPanelTemplate> + <StackPanel Orientation="Horizontal" /> + </ItemsPanelTemplate> + </ItemsControl.ItemsPanel> + <ItemsControl.ItemTemplate> + <DataTemplate> + <StackPanel Orientation="Horizontal" Margin="10 0 0 0"> + <CheckBox FocusVisualStyle="{x:Null}" IsChecked="{Binding IsSelected,Delay=200}" VerticalAlignment="Center"> + <CheckBox.Content> + <TextBlock Text="{Binding Data}" VerticalAlignment="Center"></TextBlock> + </CheckBox.Content> + </CheckBox> + </StackPanel> + </DataTemplate> + </ItemsControl.ItemTemplate> + </ItemsControl> + + <DockPanel Margin="20 0 0 0" VerticalAlignment="Center"> + <material:PackIcon Kind="Search" VerticalAlignment="Center" Width="18" Height="18" /> + <TextBox Margin="5 0 0 0" Style="{StaticResource FSE_Rounded_Corners_TextBox}" Background="{StaticResource FSE_PrimaryBackgroundBrush}" FontSize="{StaticResource FSE_SmallFontSize}" material:HintAssist.Hint="filter" material:TextFieldAssist.RippleOnFocusEnabled="True" Text="{Binding ApplicationLogsFilter,UpdateSourceTrigger=PropertyChanged,Delay=1000}" Width="200"/> + </DockPanel> + </StackPanel> + </StackPanel> + </Grid> + + <DataGrid Margin="0 5 0 0" Style="{StaticResource LogsGridStyle}" ItemsSource="{Binding ApplicationLogs}" CellStyle="{StaticResource LogsGridCellStyle}" helpers:DataGridHelper.DoubleClickCommand="{Binding OpenApplicationLogItemCommand}"> + <DataGrid.Resources> + <components:BindingProxy x:Key="proxy" Data="{Binding}" /> + </DataGrid.Resources> + <DataGrid.Columns> + <DataGridTemplateColumn Header="#" Width="40" CellTemplate="{StaticResource FSE_LogIcon_Cell}"/> + <DataGridTextColumn Header="DATE TIME" Binding="{Binding TimeStamp,StringFormat='HH:mm:ss.ff'}" Width="100" /> + <DataGridTextColumn Visibility="{Binding Source={StaticResource proxy},Path=Data.ResolutionService.IsHighResolution,Converter={StaticResource BooleanToVisibilityConverter}}" Header="SERVICE" Binding="{Binding ClassName}" Width="200" /> + <DataGridTemplateColumn Header="MESSAGE" Width="1*" > + <DataGridTemplateColumn.CellTemplate> + <DataTemplate> + <TextBlock TextWrapping="NoWrap" Text="{Binding Message,Converter={StaticResource StringToOneLineConverter},ConverterParameter='120'}"></TextBlock> + </DataTemplate> + </DataGridTemplateColumn.CellTemplate> + </DataGridTemplateColumn> + </DataGrid.Columns> + </DataGrid> + </DockPanel> + </Grid> + + <Grid Background="{StaticResource FSE_SemiTransparentBrush}"> + <Grid.Style> + <Style TargetType="Grid"> + <Setter Property="Visibility" Value="Visible"></Setter> + <Style.Triggers> + <DataTrigger Binding="{Binding SelectedLogFile.Status}" Value="Downloaded"> + <Setter Property="Visibility" Value="Hidden"></Setter> + </DataTrigger> + </Style.Triggers> + </Style> + </Grid.Style> + + <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center"> + <StackPanel.Style> + <Style TargetType="StackPanel"> + <Setter Property="Visibility" Value="Hidden"></Setter> + <Style.Triggers> + <DataTrigger Binding="{Binding SelectedLogFile.Status}" Value="None"> + <Setter Property="Visibility" Value="Visible"></Setter> + </DataTrigger> + </Style.Triggers> + </Style> + </StackPanel.Style> + <material:PackIcon Kind="FileDocument" Width="100" Height="100" HorizontalAlignment="Center" /> + <TextBlock Margin="0 20 0 0">Download this log file from the remote machine in order to display its content</TextBlock> + <controls:TextIconButton Style="{StaticResource FSE_TextIconButton_Dark}" Margin="0 100 0 0" Height="45" Icon="Download" HorizontalAlignment="Center" Width="180" Command="{Binding SelectedLogFile.DownloadCommand}">DOWNLOAD</controls:TextIconButton> + </StackPanel> + + <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center"> + <StackPanel.Style> + <Style TargetType="StackPanel"> + <Setter Property="Visibility" Value="Hidden"></Setter> + <Style.Triggers> + <DataTrigger Binding="{Binding SelectedLogFile.Status}" Value="Failed"> + <Setter Property="Visibility" Value="Visible"></Setter> + </DataTrigger> + </Style.Triggers> + </Style> + </StackPanel.Style> + <material:PackIcon Kind="AlertOutline" Width="100" Height="100" HorizontalAlignment="Center" Foreground="{StaticResource FSE_ErrorBrush}" /> + <TextBlock Margin="0 20 0 0">Error occurred while trying to download this log file</TextBlock> + <controls:TextIconButton Style="{StaticResource FSE_TextIconButton_Dark}" Margin="0 100 0 0" Height="45" Icon="Download" HorizontalAlignment="Center" Width="180" Command="{Binding SelectedLogFile.DownloadCommand}">TRY AGAIN</controls:TextIconButton> + </StackPanel> + + <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center"> + <StackPanel.Style> + <Style TargetType="StackPanel"> + <Setter Property="Visibility" Value="Hidden"></Setter> + <Style.Triggers> + <DataTrigger Binding="{Binding SelectedLogFile.Status}" Value="Downloading"> + <Setter Property="Visibility" Value="Visible"></Setter> + </DataTrigger> + </Style.Triggers> + </Style> + </StackPanel.Style> + <material:PackIcon Kind="FileDownload" Width="100" Height="100" HorizontalAlignment="Center" /> + <TextBlock Margin="0 20 0 0" HorizontalAlignment="Center">Downloading log file, please wait...</TextBlock> + <ProgressBar Margin="0 140 0 0" Width="600" Height="5" Minimum="0" Maximum="{Binding SelectedLogFile.Handler.Length}" Value="{Binding SelectedLogFile.Handler.Position}"></ProgressBar> + </StackPanel> + </Grid> + + <StackPanel Margin="0 0 40 20" HorizontalAlignment="Right" VerticalAlignment="Bottom"> + <material:PopupBox PopupMode="Click" Style="{StaticResource MaterialDesignMultiFloatingActionPopupBox}" PlacementMode="TopAndAlignCentres" FocusVisualStyle="{x:Null}"> + <StackPanel> + <Button ToolTip="Download all log files" Command="{Binding DownloadAllLogFilesCommand}"> + <material:PackIcon Kind="DownloadMultiple" /> + </Button> + <Button ToolTip="Export all downloaded log files" Command="{Binding ExportAllDownloadedLogFilesCommand}"> + <material:PackIcon Kind="DatabaseExport" /> + </Button> + <Button ToolTip="Export selected log file" Command="{Binding ExportLogFileCommand}"> + <material:PackIcon Kind="FileExportOutline" /> + </Button> + </StackPanel> + </material:PopupBox> + </StackPanel> + </Grid> + </Border> + </Grid> + </Grid> +</UserControl> diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/Views/LogsView.xaml.cs b/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/Views/LogsView.xaml.cs new file mode 100644 index 000000000..d08f18326 --- /dev/null +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/Views/LogsView.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.PPCConsole.Views +{ + /// <summary> + /// Interaction logic for LogsView.xaml + /// </summary> + public partial class LogsView : UserControl + { + public LogsView() + { + InitializeComponent(); + } + } +} diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/Views/MainView.xaml b/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/Views/MainView.xaml index 1efc0516f..b8932be95 100644 --- a/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/Views/MainView.xaml +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/Views/MainView.xaml @@ -29,19 +29,20 @@ </Style.Triggers> </Style> </Grid.Style> - <commonControls:FSETabControl TabsWidth="900" x:Name="tabs" SelectedObject="{Binding SelectedView,Mode=TwoWay}"> + <commonControls:FSETabControl TabsWidth="1000" x:Name="tabs" SelectedObject="{Binding SelectedView,Mode=TwoWay}"> <local:MonitoringView Tag="MONITORING" /> <local:RemoteDesktopView Tag="REMOTE DESKTOP" /> <local:ConsoleView Tag="COMMAND PROMPT" /> <local:FileSystemView Tag="FILE SYSTEM" /> <local:UpdatesView Tag="UPDATES" /> + <local:LogsView Tag="LOGS" /> </commonControls:FSETabControl> <TextBlock resolution:ResolutionHelper.MinWidth="1500" Margin="10 0 0 0" FontFamily="{StaticResource hand}" FontSize="{StaticResource FSE_ModuleHeaderFontSize}" Foreground="{StaticResource FSE_PrimaryAccentDarkBrush}" VerticalAlignment="Top" HorizontalAlignment="Left" Text="{Binding ElementName=tabs,Path=SelectedElement.Tag,Converter={StaticResource StringToTitleCaseConverter}}"></TextBlock> </Grid> </Grid> - <Grid Background="#DE202020" Visibility="{Binding MachineProvider.IsPPCAvailable,Converter={StaticResource BooleanToVisibilityInverseConverter}}"> + <Grid Background="#DE202020" Visibility="{Binding MachineProvider.IsPPCAvailable,Converter={StaticResource BooleanToVisibilityInverseConverter},FallbackValue='Visible',TargetNullValue='Visible'}"> <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center"> <material:PackIcon HorizontalAlignment="Center" Kind="AlertOutline" Width="100" Height="100" /> <TextBlock Margin="0 20 0 0" HorizontalAlignment="Center" FontSize="{StaticResource FSE_LargeFontSize}"> diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/Views/UpdatesView.xaml b/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/Views/UpdatesView.xaml index 037fa6d96..8a8e6546e 100644 --- a/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/Views/UpdatesView.xaml +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/Views/UpdatesView.xaml @@ -60,7 +60,7 @@ </DataTemplate> </DataGridTemplateColumn.CellTemplate> </DataGridTemplateColumn> - <DataGridTextColumn Header="STARTED" Binding="{Binding StartDate,Converter={StaticResource DateTimeUTCToShortDateTimeConverter}}" Width="Auto" /> + <DataGridTextColumn Header="STARTED" Binding="{Binding StartDate,Converter={StaticResource DateTimeUTCToShortDateTimeConverter}}" Width="120" /> <DataGridTextColumn Header="APPLICATION" Binding="{Binding ApplicationVersion}" Width="Auto" /> <DataGridTextColumn Header="FIRMWARE" Binding="{Binding FirmwareVersion}" Width="Auto" /> <DataGridTextColumn Header="ENDED" Binding="{Binding EndDate,TargetNullValue='Never',FallbackValue='Never',Converter={StaticResource DateTimeUTCToShortDateTimeConverter}}" Width="Auto" /> diff --git a/Software/Visual_Studio/FSE/Tango.FSE.Common/Dialogs/ApplicationLogItemView.xaml b/Software/Visual_Studio/FSE/Tango.FSE.Common/Dialogs/ApplicationLogItemView.xaml index 844bdc992..6abda9b6d 100644 --- a/Software/Visual_Studio/FSE/Tango.FSE.Common/Dialogs/ApplicationLogItemView.xaml +++ b/Software/Visual_Studio/FSE/Tango.FSE.Common/Dialogs/ApplicationLogItemView.xaml @@ -11,14 +11,14 @@ <DockPanel Margin="10"> <StackPanel DockPanel.Dock="Top" Orientation="Horizontal"> - <material:PackIcon Width="42" Height="42" VerticalAlignment="Center" Style="{StaticResource FSE_LogIcon}" /> + <material:PackIcon DataContext="{Binding LogItem}" Width="42" Height="42" VerticalAlignment="Center" Style="{StaticResource FSE_LogIcon}" /> <TextBlock VerticalAlignment="Center" Text="{Binding LogItem.Message}" TextWrapping="NoWrap" Height="22" TextTrimming="CharacterEllipsis" Width="700" Margin="10 0 0 0" FontSize="{StaticResource FSE_LargeFontSize}"></TextBlock> </StackPanel> <Grid Margin="0 10 0 0" DockPanel.Dock="Top"> <controls:TableGrid RowHeight="30"> <TextBlock Text="Time Stamp:" FontWeight="SemiBold" /> - <TextBlock Text="{Binding LogItem.TimeStamp,Converter={StaticResource DateTimeUTCToStringConverter},ConverterParameter='MM/dd/yyyy HH:mm:ss.fff'}"></TextBlock> + <TextBlock Text="{Binding LogItem.TimeStamp,StringFormat='HH:mm:ss.fff'}"></TextBlock> <TextBlock Text="Category:" FontWeight="SemiBold" /> <TextBlock Text="{Binding LogItem.Category}"></TextBlock> <TextBlock Text="File:" FontWeight="SemiBold" /> diff --git a/Software/Visual_Studio/FSE/Tango.FSE.Common/Dialogs/MachineLogItemView.xaml b/Software/Visual_Studio/FSE/Tango.FSE.Common/Dialogs/FirmwareLogItemView.xaml index 776e6d893..49d67f205 100644 --- a/Software/Visual_Studio/FSE/Tango.FSE.Common/Dialogs/MachineLogItemView.xaml +++ b/Software/Visual_Studio/FSE/Tango.FSE.Common/Dialogs/FirmwareLogItemView.xaml @@ -1,4 +1,4 @@ -<UserControl x:Class="Tango.FSE.Common.Dialogs.MachineLogItemView" +<UserControl x:Class="Tango.FSE.Common.Dialogs.FirmwareLogItemView" 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" @@ -7,18 +7,18 @@ xmlns:controls="clr-namespace:Tango.SharedUI.Controls;assembly=Tango.SharedUI" xmlns:local="clr-namespace:Tango.FSE.Common.Dialogs" mc:Ignorable="d" - Width="800" Height="500" d:DataContext="{d:DesignInstance Type=local:MachineLogItemViewVM, IsDesignTimeCreatable=False}" Background="{StaticResource FSE_PrimaryBackgroundLightBrush}" Foreground="{StaticResource FSE_PrimaryForegroundBrush}"> + Width="800" Height="500" d:DataContext="{d:DesignInstance Type=local:FirmwareLogItemViewVM, IsDesignTimeCreatable=False}" Background="{StaticResource FSE_PrimaryBackgroundLightBrush}" Foreground="{StaticResource FSE_PrimaryForegroundBrush}"> <DockPanel Margin="10"> <StackPanel DockPanel.Dock="Top" Orientation="Horizontal"> - <material:PackIcon Width="42" Height="42" VerticalAlignment="Center" Style="{StaticResource FSE_LogIcon}" /> + <material:PackIcon DataContext="{Binding LogItem}" Width="42" Height="42" VerticalAlignment="Center" Style="{StaticResource FSE_LogIcon}" /> <TextBlock VerticalAlignment="Center" Text="{Binding LogItem.Message}" TextWrapping="NoWrap" Height="22" TextTrimming="CharacterEllipsis" Width="700" Margin="10 0 0 0" FontSize="{StaticResource FSE_LargeFontSize}"></TextBlock> </StackPanel> <Grid Margin="0 10 0 0" DockPanel.Dock="Top"> <controls:TableGrid RowHeight="30"> <TextBlock Text="Time Stamp:" FontWeight="SemiBold" /> - <TextBlock Text="{Binding LogItem.TimeStamp,Converter={StaticResource DateTimeUTCToStringConverter},ConverterParameter='MM/dd/yyyy HH:mm:ss.fff'}"></TextBlock> + <TextBlock Text="{Binding LogItem.TimeStamp,StringFormat='HH:mm:ss.fff'}"></TextBlock> <TextBlock Text="Category:" FontWeight="SemiBold" /> <TextBlock Text="{Binding LogItem.Category}"></TextBlock> <TextBlock Text="File:" FontWeight="SemiBold" /> diff --git a/Software/Visual_Studio/FSE/Tango.FSE.Common/Dialogs/MachineLogItemView.xaml.cs b/Software/Visual_Studio/FSE/Tango.FSE.Common/Dialogs/FirmwareLogItemView.xaml.cs index 97103c34e..6e7556c40 100644 --- a/Software/Visual_Studio/FSE/Tango.FSE.Common/Dialogs/MachineLogItemView.xaml.cs +++ b/Software/Visual_Studio/FSE/Tango.FSE.Common/Dialogs/FirmwareLogItemView.xaml.cs @@ -18,9 +18,9 @@ namespace Tango.FSE.Common.Dialogs /// <summary> /// Interaction logic for ApplicationLogItemView.xaml /// </summary> - public partial class MachineLogItemView : UserControl + public partial class FirmwareLogItemView : UserControl { - public MachineLogItemView() + public FirmwareLogItemView() { InitializeComponent(); } diff --git a/Software/Visual_Studio/FSE/Tango.FSE.Common/Dialogs/MachineLogItemViewVM.cs b/Software/Visual_Studio/FSE/Tango.FSE.Common/Dialogs/FirmwareLogItemViewVM.cs index 2ac7a7b3a..1ee76e381 100644 --- a/Software/Visual_Studio/FSE/Tango.FSE.Common/Dialogs/MachineLogItemViewVM.cs +++ b/Software/Visual_Studio/FSE/Tango.FSE.Common/Dialogs/FirmwareLogItemViewVM.cs @@ -7,11 +7,11 @@ using Tango.Integration.Logging; namespace Tango.FSE.Common.Dialogs { - public class MachineLogItemViewVM : FSEDialogViewVM + public class FirmwareLogItemViewVM : FSEDialogViewVM { public EmbeddedLogItem LogItem { get; set; } - public MachineLogItemViewVM() + public FirmwareLogItemViewVM() { CanClose = false; OKText = "CLOSE"; diff --git a/Software/Visual_Studio/FSE/Tango.FSE.Common/Logging/ILoggingProvider.cs b/Software/Visual_Studio/FSE/Tango.FSE.Common/Logging/ILoggingProvider.cs index d49f8b21d..68c288e60 100644 --- a/Software/Visual_Studio/FSE/Tango.FSE.Common/Logging/ILoggingProvider.cs +++ b/Software/Visual_Studio/FSE/Tango.FSE.Common/Logging/ILoggingProvider.cs @@ -1,10 +1,13 @@ using System; using System.Collections.Generic; +using System.Collections.ObjectModel; using System.Linq; using System.Text; using System.Threading.Tasks; +using Tango.FSE.Common.FileSystem; using Tango.Integration.Logging; using Tango.Logging; +using Tango.PPC.Shared.Logs; namespace Tango.FSE.Common.Logging { @@ -14,13 +17,43 @@ namespace Tango.FSE.Common.Logging public interface ILoggingProvider { /// <summary> + /// Gets the last retrieved history of the application log files. + /// </summary> + ObservableCollection<RemoteLogFile> ApplicationLogFiles { get; } + + /// <summary> + /// Gets the last retrieved history of the embedded firmware log files. + /// </summary> + ObservableCollection<RemoteLogFile> FirmwareLogFiles { get; } + + /// <summary> + /// Gets the history of application log files. + /// </summary> + /// <returns></returns> + Task<List<RemoteLogFile>> GetApplicationLogFiles(); + + /// <summary> + /// Gets the history of the embedded firmware log files. + /// </summary> + /// <returns></returns> + Task<List<RemoteLogFile>> GetFirmwareLogFiles(); + + /// <summary> + /// Downloads the specified remote log file. + /// </summary> + /// <param name="logFile">The log file.</param> + /// <param name="targetFolder">Target folder.</param> + /// <returns></returns> + Task<FileSystemHandler> DownloadLogFile(RemoteLogFile logFile, String targetFolder); + + /// <summary> /// Occurs when a new PPC application log is available. /// </summary> event EventHandler<LogItemBase> ApplicationLogAvailable; /// <summary> - /// Occurs when a new embedded log is available. + /// Occurs when a new embedded firmware log is available. /// </summary> - event EventHandler<EmbeddedLogItem> EmbeddedLogAvailable; + event EventHandler<EmbeddedLogItem> FirmwareLogAvailable; } } diff --git a/Software/Visual_Studio/FSE/Tango.FSE.Common/Logging/RemoteLogFileModel.cs b/Software/Visual_Studio/FSE/Tango.FSE.Common/Logging/RemoteLogFileModel.cs new file mode 100644 index 000000000..cf71ed270 --- /dev/null +++ b/Software/Visual_Studio/FSE/Tango.FSE.Common/Logging/RemoteLogFileModel.cs @@ -0,0 +1,128 @@ +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.Core; +using Tango.Core.Commands; +using Tango.Core.DI; +using Tango.FSE.Common.FileSystem; +using Tango.Logging; +using Tango.PPC.Shared.Logs; +using Tango.SharedUI.Components; + +namespace Tango.FSE.Common.Logging +{ + /// <summary> + /// Represents a remote log file model. + /// </summary> + /// <typeparam name="T"></typeparam> + /// <seealso cref="Tango.Core.ExtendedObject" /> + public class RemoteLogFileModel<T> : ExtendedObject where T : LogItemBase + { + private ILogFileParser<T> _parser; + + /// <summary> + /// Occurs when the remote log file has downloaded successfully. + /// </summary> + public event EventHandler DownloadCompleted; + + [TangoInject] + private ILoggingProvider LoggingProvider { get; set; } + + /// <summary> + /// Gets or sets the remote log file. + /// </summary> + public RemoteLogFile RemoteLogFile { get; set; } + + /// <summary> + /// Gets the duration of the remote log file. + /// </summary> + public TimeSpan Duration + { + get { return RemoteLogFile.DateModified - RemoteLogFile.DateCreated; } + } + + private RemoteLogFileStatus _status; + /// <summary> + /// Gets or sets the remote log file status. + /// </summary> + public RemoteLogFileStatus Status + { + get { return _status; } + set { _status = value; RaisePropertyChangedAuto(); } + } + + private FileSystemHandler _handler; + /// <summary> + /// Gets or sets the remote log file download handler. + /// </summary> + public FileSystemHandler Handler + { + get { return _handler; } + set { _handler = value; RaisePropertyChangedAuto(); } + } + + /// <summary> + /// Gets or sets the temporary file (where the actual log file is stored locally). + /// </summary> + public String TemporaryFile { get; set; } + + private ObservableCollection<LogItemBase> _logItems; + /// <summary> + /// Gets or sets the log items. + /// </summary> + public ObservableCollection<LogItemBase> LogItems + { + get { return _logItems; } + set { _logItems = value; RaisePropertyChangedAuto(); } + } + + /// <summary> + /// Gets or sets the download command. + /// </summary> + public RelayCommand DownloadCommand { get; set; } + + /// <summary> + /// Initializes a new instance of the <see cref="RemoteLogFileModel{T}"/> class. + /// </summary> + /// <param name="parser">The parser.</param> + public RemoteLogFileModel(ILogFileParser<T> parser) + { + TangoIOC.Default.Inject(this); + _parser = parser; + LogItems = new ObservableCollection<LogItemBase>(); + DownloadCommand = new RelayCommand(DownloadLogFile); + } + + /// <summary> + /// Downloads the log file. + /// </summary> + public async void DownloadLogFile() + { + if (Status == RemoteLogFileStatus.None || Status == RemoteLogFileStatus.Failed) + { + Status = RemoteLogFileStatus.Downloading; + String tempLogFile = TemporaryManager.CreateImaginaryFile(".log"); + Handler = await LoggingProvider.DownloadLogFile(RemoteLogFile, tempLogFile); + Handler.StatusChanged += (x, status) => + { + if (status == FileSystemHandlerStatus.Completed) + { + TemporaryFile = tempLogFile; + LogItems = new ObservableCollection<LogItemBase>(_parser.Parse(tempLogFile, RemoteLogFile.DateCreated.ToLocalTime())); + Status = RemoteLogFileStatus.Downloaded; + DownloadCompleted?.Invoke(this, new EventArgs()); + } + else if (status == FileSystemHandlerStatus.Failed) + { + Status = RemoteLogFileStatus.Failed; + } + }; + } + } + } +} diff --git a/Software/Visual_Studio/FSE/Tango.FSE.Common/Logging/RemoteLogFileStatus.cs b/Software/Visual_Studio/FSE/Tango.FSE.Common/Logging/RemoteLogFileStatus.cs new file mode 100644 index 000000000..25f3f4eac --- /dev/null +++ b/Software/Visual_Studio/FSE/Tango.FSE.Common/Logging/RemoteLogFileStatus.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Tango.FSE.Common.Logging +{ + /// <summary> + /// Represents the remote log file statuses. + /// </summary> + public enum RemoteLogFileStatus + { + None, + Downloading, + Downloaded, + Failed, + } +} 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 7134dbcbb..1581bd25b 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 @@ -108,14 +108,14 @@ <Compile Include="Diagnostics\DiagnosticsFrameReceivedEventArgs.cs" /> <Compile Include="Diagnostics\DiagnosticsThrottlingMode.cs" /> <Compile Include="Diagnostics\IDiagnosticsProvider.cs" /> - <Compile Include="Dialogs\MachineLogItemView.xaml.cs"> - <DependentUpon>MachineLogItemView.xaml</DependentUpon> + <Compile Include="Dialogs\FirmwareLogItemView.xaml.cs"> + <DependentUpon>FirmwareLogItemView.xaml</DependentUpon> </Compile> <Compile Include="Dialogs\ApplicationLogItemView.xaml.cs"> <DependentUpon>ApplicationLogItemView.xaml</DependentUpon> </Compile> <Compile Include="Dialogs\ApplicationLogItemViewVM.cs" /> - <Compile Include="Dialogs\MachineLogItemViewVM.cs" /> + <Compile Include="Dialogs\FirmwareLogItemViewVM.cs" /> <Compile Include="EventTriggerActions\SetterAction.cs" /> <Compile Include="ExtensionMethods\IExternalBridgeClientExtensions.cs" /> <Compile Include="ExtensionMethods\ViewModelExtensionMethods.cs" /> @@ -132,6 +132,8 @@ <Compile Include="Helpers\DesignModeHelper.cs" /> <Compile Include="Helpers\EncryptionHelper.cs" /> <Compile Include="Logging\ILoggingProvider.cs" /> + <Compile Include="Logging\RemoteLogFileModel.cs" /> + <Compile Include="Logging\RemoteLogFileStatus.cs" /> <Compile Include="Modules\IFSEModuleLoader.cs" /> <Compile Include="Navigation\NavigationMenuItem.cs" /> <Compile Include="Notifications\AppBarItem.cs" /> @@ -194,7 +196,7 @@ <SubType>Designer</SubType> <Generator>MSBuild:Compile</Generator> </Page> - <Page Include="Dialogs\MachineLogItemView.xaml"> + <Page Include="Dialogs\FirmwareLogItemView.xaml"> <Generator>MSBuild:Compile</Generator> <SubType>Designer</SubType> </Page> diff --git a/Software/Visual_Studio/FSE/Tango.FSE.UI/Connection/DefaultMachineProvider.cs b/Software/Visual_Studio/FSE/Tango.FSE.UI/Connection/DefaultMachineProvider.cs index 3a43e71f0..4837fd100 100644 --- a/Software/Visual_Studio/FSE/Tango.FSE.UI/Connection/DefaultMachineProvider.cs +++ b/Software/Visual_Studio/FSE/Tango.FSE.UI/Connection/DefaultMachineProvider.cs @@ -457,11 +457,12 @@ namespace Tango.FSE.UI.Connection MachineOperator.RequestTimeout = TimeSpan.FromSeconds(5); MachineOperator.ContinuousRequestTimeout = TimeSpan.FromSeconds(5); - if (MachineOperator is ExternalBridgeTcpClient) + if (ConnectionType.IsRemote()) { - ExternalBridgeTcpClient tcpClient = MachineOperator as ExternalBridgeTcpClient; + ExternalBridgeTcpClient tcpClient = MachineOperator.As<ExternalBridgeTcpClient>(); tcpClient.EnableApplicationLogs = true; tcpClient.InjectApplicationLogsToDefaultLogManager = false; + tcpClient.LogEmbeddedDebuggingToFile = false; } } diff --git a/Software/Visual_Studio/FSE/Tango.FSE.UI/Logging/DefaultLoggingProvider.cs b/Software/Visual_Studio/FSE/Tango.FSE.UI/Logging/DefaultLoggingProvider.cs index b3a2d4051..4570589b6 100644 --- a/Software/Visual_Studio/FSE/Tango.FSE.UI/Logging/DefaultLoggingProvider.cs +++ b/Software/Visual_Studio/FSE/Tango.FSE.UI/Logging/DefaultLoggingProvider.cs @@ -1,13 +1,19 @@ using System; using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; +using Tango.Core; +using Tango.FileSystem; using Tango.FSE.Common.Connection; +using Tango.FSE.Common.FileSystem; using Tango.FSE.Common.Logging; using Tango.Integration.ExternalBridge; using Tango.Integration.Logging; using Tango.Logging; +using Tango.PPC.Shared.Logs; namespace Tango.FSE.UI.Logging { @@ -15,8 +21,11 @@ namespace Tango.FSE.UI.Logging /// Represents the <see cref="ILoggingProvider"/> default implementation. /// </summary> /// <seealso cref="Tango.FSE.Common.Logging.ILoggingProvider" /> - public class DefaultLoggingProvider : ILoggingProvider + public class DefaultLoggingProvider : ExtendedObject, ILoggingProvider { + private IFileSystemProvider _fileSystemProvider; + private IMachineProvider _machineProvider; + /// <summary> /// Occurs when a new PPC application log is available. /// </summary> @@ -25,14 +34,30 @@ namespace Tango.FSE.UI.Logging /// <summary> /// Occurs when a new embedded log is available. /// </summary> - public event EventHandler<EmbeddedLogItem> EmbeddedLogAvailable; + public event EventHandler<EmbeddedLogItem> FirmwareLogAvailable; + + /// <summary> + /// Gets the last retrieved history of the application log files. + /// </summary> + public ObservableCollection<RemoteLogFile> ApplicationLogFiles { get; private set; } + + /// <summary> + /// Gets the last retrieved history of the embedded firmware log files. + /// </summary> + public ObservableCollection<RemoteLogFile> FirmwareLogFiles { get; private set; } /// <summary> /// Initializes a new instance of the <see cref="DefaultLoggingProvider"/> class. /// </summary> /// <param name="machineProvider">The machine provider.</param> - public DefaultLoggingProvider(IMachineProvider machineProvider) + public DefaultLoggingProvider(IMachineProvider machineProvider, IFileSystemProvider fileSystemProvider) { + ApplicationLogFiles = new ObservableCollection<RemoteLogFile>(); + FirmwareLogFiles = new ObservableCollection<RemoteLogFile>(); + + _machineProvider = machineProvider; + _fileSystemProvider = fileSystemProvider; + if (machineProvider.MachineOperator is ExternalBridgeTcpClient) { (machineProvider.MachineOperator as ExternalBridgeTcpClient).ApplicationLogAvailable += DefaultLoggingProvider_ApplicationLogAvailable; @@ -41,6 +66,62 @@ namespace Tango.FSE.UI.Logging machineProvider.MachineOperator.DebugLogAvailable += MachineOperator_DebugLogAvailable; } + /// <summary> + /// Gets the history of application log files. + /// </summary> + /// <returns></returns> + public async Task<List<RemoteLogFile>> GetApplicationLogFiles() + { + var response = await _machineProvider.MachineOperator.SendGenericRequest<GetLogFilesRequest, GetLogFilesResponse>(new GetLogFilesRequest() { LogFileType = RemoteLogFileType.Application }); + + ApplicationLogFiles.Clear(); + + foreach (var item in response.LogFiles) + { + ApplicationLogFiles.Add(item); + } + + return response.LogFiles; + } + + /// <summary> + /// Gets the history of the embedded firmware log files. + /// </summary> + /// <returns></returns> + public async Task<List<RemoteLogFile>> GetFirmwareLogFiles() + { + var response = await _machineProvider.MachineOperator.SendGenericRequest<GetLogFilesRequest, GetLogFilesResponse>(new GetLogFilesRequest() { LogFileType = RemoteLogFileType.Firmware }); + + FirmwareLogFiles.Clear(); + + foreach (var item in response.LogFiles) + { + FirmwareLogFiles.Add(item); + } + + return response.LogFiles; + } + + /// <summary> + /// Downloads the specified remote log file. + /// </summary> + /// <param name="logFile">The log file.</param> + /// <param name="targetFolder">Target local folder.</param> + /// <returns></returns> + public async Task<FileSystemHandler> DownloadLogFile(RemoteLogFile logFile, String targetFolder) + { + var tempFolder = TemporaryManager.CreateFolder(); + var handler = await _fileSystemProvider.Download(new FileItem() { Path = logFile.Path }, tempFolder); + handler.StatusChanged += (x, status) => + { + if (status == FileSystemHandlerStatus.Completed) + { + File.Move(Path.Combine(tempFolder, logFile.Name), targetFolder); + } + }; + return handler; + } + private void DefaultLoggingProvider_ApplicationLogAvailable(object sender, LogItemBase log) { ApplicationLogAvailable?.Invoke(this, log); @@ -48,7 +129,7 @@ namespace Tango.FSE.UI.Logging private void MachineOperator_DebugLogAvailable(object sender, PMR.Debugging.StartDebugLogResponse log) { - EmbeddedLogAvailable?.Invoke(this, new EmbeddedLogItem(log) { TimeStamp = DateTime.Now}); + FirmwareLogAvailable?.Invoke(this, new EmbeddedLogItem(log) { TimeStamp = DateTime.Now }); } } } diff --git a/Software/Visual_Studio/FSE/Tango.FSE.UI/Panes/LogViewerPane.xaml b/Software/Visual_Studio/FSE/Tango.FSE.UI/Panes/LogViewerPane.xaml index d3ece6db1..50faea56c 100644 --- a/Software/Visual_Studio/FSE/Tango.FSE.UI/Panes/LogViewerPane.xaml +++ b/Software/Visual_Studio/FSE/Tango.FSE.UI/Panes/LogViewerPane.xaml @@ -97,14 +97,14 @@ <DockPanel Grid.Column="1" Margin="10"> <Grid DockPanel.Dock="Top" Margin="55 5 0 0" Panel.ZIndex="100"> - <TextBlock Foreground="{StaticResource FSE_PrimaryAccentBrush}" FontWeight="SemiBold">MACHINE LOGS</TextBlock> + <TextBlock Foreground="{StaticResource FSE_PrimaryAccentBrush}" FontWeight="SemiBold">FIRMWARE LOGS</TextBlock> <StackPanel Margin="0 0 0 -70" HorizontalAlignment="Right" > <StackPanel Margin="8 0 0 0" HorizontalAlignment="Left" Orientation="Horizontal"> - <controls:ToggleIconButton ToolTip="Pause/Resume machine logs" CheckedIcon="Play" UncheckedIcon="Pause" Width="24" Height="24" IsChecked="{Binding MachineLogsPaused}" /> - <controls:IconButton Icon="CircleArrows" ToolTip="Clear machine logs" Width="24" Height="24" Command="{Binding ClearMachineLogsCommand}" /> + <controls:ToggleIconButton ToolTip="Pause/Resume firmware logs" CheckedIcon="Play" UncheckedIcon="Pause" Width="24" Height="24" IsChecked="{Binding FirmwareLogsPaused}" /> + <controls:IconButton Icon="CircleArrows" ToolTip="Clear firmware logs" Width="24" Height="24" Command="{Binding ClearFirmwareLogsCommand}" /> </StackPanel> <StackPanel Margin="0 8 0 0" Orientation="Horizontal"> - <ItemsControl VerticalAlignment="Center" ItemsSource="{Binding SelectedMachineLogsCategories}" Visibility="{Binding ResolutionService.IsHighResolution,Converter={StaticResource BooleanToVisibilityConverter}}"> + <ItemsControl VerticalAlignment="Center" ItemsSource="{Binding SelectedFirmwareLogsCategories}" Visibility="{Binding ResolutionService.IsHighResolution,Converter={StaticResource BooleanToVisibilityConverter}}"> <ItemsControl.ItemsPanel> <ItemsPanelTemplate> <StackPanel Orientation="Horizontal" /> @@ -125,13 +125,13 @@ <DockPanel Margin="20 0 0 0" VerticalAlignment="Center"> <material:PackIcon Kind="Search" VerticalAlignment="Center" Width="18" Height="18" /> - <TextBox Margin="5 0 0 0" Style="{StaticResource FSE_Rounded_Corners_TextBox}" Background="{StaticResource FSE_PrimaryBackgroundBrush}" FontSize="{StaticResource FSE_SmallFontSize}" material:HintAssist.Hint="filter" material:TextFieldAssist.RippleOnFocusEnabled="True" Text="{Binding MachineLogsFilter,UpdateSourceTrigger=PropertyChanged,Delay=1000}" Width="200"/> + <TextBox Margin="5 0 0 0" Style="{StaticResource FSE_Rounded_Corners_TextBox}" Background="{StaticResource FSE_PrimaryBackgroundBrush}" FontSize="{StaticResource FSE_SmallFontSize}" material:HintAssist.Hint="filter" material:TextFieldAssist.RippleOnFocusEnabled="True" Text="{Binding FirmwareLogsFilter,UpdateSourceTrigger=PropertyChanged,Delay=1000}" Width="200"/> </DockPanel> </StackPanel> </StackPanel> </Grid> - <DataGrid Margin="0 5 0 0" Style="{StaticResource LogsGridStyle}" ItemsSource="{Binding MachineLogs}" CellStyle="{StaticResource LogsGridCellStyle}" helpers:DataGridHelper.DoubleClickCommand="{Binding OpenMachineLogItemCommand}"> + <DataGrid Margin="0 5 0 0" Style="{StaticResource LogsGridStyle}" ItemsSource="{Binding FirmwareLogs}" CellStyle="{StaticResource LogsGridCellStyle}" helpers:DataGridHelper.DoubleClickCommand="{Binding OpenFirmareLogItemCommand}"> <DataGrid.Columns> <DataGridTemplateColumn Header="#" Width="40" CellTemplate="{StaticResource FSE_LogIcon_Cell}"/> <DataGridTextColumn Header="DATE TIME" Binding="{Binding TimeStamp,StringFormat='HH:mm:ss.ff'}" Width="100" /> diff --git a/Software/Visual_Studio/FSE/Tango.FSE.UI/Panes/LogViewerPaneVM.cs b/Software/Visual_Studio/FSE/Tango.FSE.UI/Panes/LogViewerPaneVM.cs index 1a4083f28..cb0ea0229 100644 --- a/Software/Visual_Studio/FSE/Tango.FSE.UI/Panes/LogViewerPaneVM.cs +++ b/Software/Visual_Studio/FSE/Tango.FSE.UI/Panes/LogViewerPaneVM.cs @@ -23,7 +23,7 @@ namespace Tango.FSE.UI.Panes public class LogViewerPaneVM : FSEViewModel { private List<LogItemBase> _pausedApplicationLogs; - private List<EmbeddedLogItem> _pausedMachineLogs; + private List<EmbeddedLogItem> _pausedFirmwareLogs; /// <summary> /// Gets or sets the application logs. @@ -31,9 +31,9 @@ namespace Tango.FSE.UI.Panes public ObservableCollection<LogItemBase> ApplicationLogs { get; set; } /// <summary> - /// Gets or sets the machine logs. + /// Gets or sets the firmware logs. /// </summary> - public ObservableCollection<EmbeddedLogItem> MachineLogs { get; set; } + public ObservableCollection<EmbeddedLogItem> FirmwareLogs { get; set; } /// <summary> /// Gets or sets the application logs view. @@ -41,9 +41,9 @@ namespace Tango.FSE.UI.Panes public ICollectionView ApplicationLogsView { get; set; } /// <summary> - /// Gets or sets the machine logs view. + /// Gets or sets the firmware logs view. /// </summary> - public ICollectionView MachineLogsView { get; set; } + public ICollectionView FirmwareLogsView { get; set; } /// <summary> /// Gets or sets the selected application logs categories. @@ -51,9 +51,9 @@ namespace Tango.FSE.UI.Panes public SelectedObjectCollection<LogCategory> SelectedApplicationLogsCategories { get; set; } /// <summary> - /// Gets or sets the selected machine logs categories. + /// Gets or sets the selected firmware logs categories. /// </summary> - public SelectedObjectCollection<LogCategory> SelectedMachineLogsCategories { get; set; } + public SelectedObjectCollection<LogCategory> SelectedFirmwareLogsCategories { get; set; } /// <summary> /// Gets or sets a value indicating whether to pause the insertion of application logs. @@ -61,9 +61,9 @@ namespace Tango.FSE.UI.Panes public bool ApplicationLogsPaused { get; set; } /// <summary> - /// Gets or sets a value indicating whether to pause the insertion of machine logs. + /// Gets or sets a value indicating whether to pause the insertion of firmware logs. /// </summary> - public bool MachineLogsPaused { get; set; } + public bool FirmwareLogsPaused { get; set; } private String _applicationLogsFilter; /// <summary> @@ -75,14 +75,14 @@ namespace Tango.FSE.UI.Panes set { _applicationLogsFilter = value; RaisePropertyChangedAuto(); ApplicationLogsView.Refresh(); } } - private String _machineLogsFilter; + private String _firmwareLogsFilter; /// <summary> - /// Gets or sets the machine logs filter. + /// Gets or sets the firmware logs filter. /// </summary> - public String MachineLogsFilter + public String FirmwareLogsFilter { - get { return _machineLogsFilter; } - set { _machineLogsFilter = value; RaisePropertyChangedAuto(); MachineLogsView.Refresh(); } + get { return _firmwareLogsFilter; } + set { _firmwareLogsFilter = value; RaisePropertyChangedAuto(); FirmwareLogsView.Refresh(); } } /// <summary> @@ -91,9 +91,9 @@ namespace Tango.FSE.UI.Panes public RelayCommand ClearApplicationLogsCommand { get; set; } /// <summary> - /// Clears the machine logs. + /// Clears the firmware logs. /// </summary> - public RelayCommand ClearMachineLogsCommand { get; set; } + public RelayCommand ClearFirmwareLogsCommand { get; set; } /// <summary> /// Opens the detailed application log dialog. @@ -101,9 +101,9 @@ namespace Tango.FSE.UI.Panes public RelayCommand<LogItemBase> OpenApplicationLogItemCommand { get; set; } /// <summary> - /// Opens the detailed machine log dialog. + /// Opens the detailed firmware log dialog. /// </summary> - public RelayCommand<EmbeddedLogItem> OpenMachineLogItemCommand { get; set; } + public RelayCommand<EmbeddedLogItem> OpenFirmareLogItemCommand { get; set; } /// <summary> /// Initializes a new instance of the <see cref="LogViewerPaneVM"/> class. @@ -111,15 +111,15 @@ namespace Tango.FSE.UI.Panes public LogViewerPaneVM() { _pausedApplicationLogs = new List<LogItemBase>(); - _pausedMachineLogs = new List<EmbeddedLogItem>(); + _pausedFirmwareLogs = new List<EmbeddedLogItem>(); ApplicationLogs = new ObservableCollection<LogItemBase>(); - MachineLogs = new ObservableCollection<EmbeddedLogItem>(); + FirmwareLogs = new ObservableCollection<EmbeddedLogItem>(); ApplicationLogsView = CollectionViewSource.GetDefaultView(ApplicationLogs); - MachineLogsView = CollectionViewSource.GetDefaultView(MachineLogs); + FirmwareLogsView = CollectionViewSource.GetDefaultView(FirmwareLogs); ApplicationLogsView.Filter = FilterApplicationLogs; - MachineLogsView.Filter = FilterMachineLogs; + FirmwareLogsView.Filter = FilterFirmwareLogs; SelectedApplicationLogsCategories = new SelectedObjectCollection<LogCategory>(new ObservableCollection<LogCategory>() { @@ -135,7 +135,7 @@ namespace Tango.FSE.UI.Panes LogCategory.Critical, }); - SelectedMachineLogsCategories = new SelectedObjectCollection<LogCategory>(new ObservableCollection<LogCategory>() + SelectedFirmwareLogsCategories = new SelectedObjectCollection<LogCategory>(new ObservableCollection<LogCategory>() { LogCategory.Info, LogCategory.Warning, @@ -152,18 +152,18 @@ namespace Tango.FSE.UI.Panes }); SelectedApplicationLogsCategories.SynchedSource.CollectionChanged += (_, __) => ApplicationLogsView.Refresh(); - SelectedMachineLogsCategories.SynchedSource.CollectionChanged += (_, __) => MachineLogsView.Refresh(); + SelectedFirmwareLogsCategories.SynchedSource.CollectionChanged += (_, __) => FirmwareLogsView.Refresh(); ClearApplicationLogsCommand = new RelayCommand(ClearApplicationLogs); - ClearMachineLogsCommand = new RelayCommand(ClearMachineLogs); + ClearFirmwareLogsCommand = new RelayCommand(ClearFirmwareLogs); TangoIOC.Default.Inject(this); LoggingProvider.ApplicationLogAvailable += LoggingProvider_ApplicationLogAvailable; - LoggingProvider.EmbeddedLogAvailable += LoggingProvider_EmbeddedLogAvailable; + LoggingProvider.FirmwareLogAvailable += LoggingProvider_FirmwareLogAvailable; OpenApplicationLogItemCommand = new RelayCommand<LogItemBase>(OpenApplicationLogItem); - OpenMachineLogItemCommand = new RelayCommand<EmbeddedLogItem>(OpenMachineLogItem); + OpenFirmareLogItemCommand = new RelayCommand<EmbeddedLogItem>(OpenFirmwareLogItem); } private async void OpenApplicationLogItem(LogItemBase logItem) @@ -171,9 +171,9 @@ namespace Tango.FSE.UI.Panes await NotificationProvider.ShowDialog(new ApplicationLogItemViewVM() { LogItem = logItem }); } - private async void OpenMachineLogItem(EmbeddedLogItem logItem) + private async void OpenFirmwareLogItem(EmbeddedLogItem logItem) { - await NotificationProvider.ShowDialog(new MachineLogItemViewVM() { LogItem = logItem }); + await NotificationProvider.ShowDialog(new FirmwareLogItemViewVM() { LogItem = logItem }); } private bool FilterApplicationLogs(object obj) @@ -182,10 +182,10 @@ namespace Tango.FSE.UI.Panes return SelectedApplicationLogsCategories.SynchedSource.Contains(log.Category) && (String.IsNullOrWhiteSpace(ApplicationLogsFilter) || log.Message.ToLower().Contains(ApplicationLogsFilter.ToLower())); } - private bool FilterMachineLogs(object obj) + private bool FilterFirmwareLogs(object obj) { var log = obj as EmbeddedLogItem; - return SelectedMachineLogsCategories.SynchedSource.Contains(log.Category) && (String.IsNullOrWhiteSpace(MachineLogsFilter) || log.Message.ToLower().Contains(MachineLogsFilter.ToLower())); + return SelectedFirmwareLogsCategories.SynchedSource.Contains(log.Category) && (String.IsNullOrWhiteSpace(FirmwareLogsFilter) || log.Message.ToLower().Contains(FirmwareLogsFilter.ToLower())); } private void ClearApplicationLogs() @@ -194,10 +194,10 @@ namespace Tango.FSE.UI.Panes ApplicationLogs.Clear(); } - private void ClearMachineLogs() + private void ClearFirmwareLogs() { - _pausedMachineLogs.Clear(); - MachineLogs.Clear(); + _pausedFirmwareLogs.Clear(); + FirmwareLogs.Clear(); } private void LoggingProvider_ApplicationLogAvailable(object sender, LogItemBase logItem) @@ -225,27 +225,27 @@ namespace Tango.FSE.UI.Panes } } - private void LoggingProvider_EmbeddedLogAvailable(object sender, EmbeddedLogItem logItem) + private void LoggingProvider_FirmwareLogAvailable(object sender, EmbeddedLogItem logItem) { - if (MachineLogsPaused) + if (FirmwareLogsPaused) { - _pausedMachineLogs.Add(logItem); + _pausedFirmwareLogs.Add(logItem); } else { InvokeUI(() => { - if (_pausedMachineLogs.Count > 0) + if (_pausedFirmwareLogs.Count > 0) { - foreach (var pausedItem in _pausedMachineLogs) + foreach (var pausedItem in _pausedFirmwareLogs) { - MachineLogs.Insert(0, pausedItem); + FirmwareLogs.Insert(0, pausedItem); } - _pausedMachineLogs.Clear(); + _pausedFirmwareLogs.Clear(); } - MachineLogs.Insert(0, logItem); + FirmwareLogs.Insert(0, logItem); }); } } diff --git a/Software/Visual_Studio/FSE/Tango.FSE.UI/ViewModelLocator.cs b/Software/Visual_Studio/FSE/Tango.FSE.UI/ViewModelLocator.cs index 5d5463d1b..f89c2010f 100644 --- a/Software/Visual_Studio/FSE/Tango.FSE.UI/ViewModelLocator.cs +++ b/Software/Visual_Studio/FSE/Tango.FSE.UI/ViewModelLocator.cs @@ -93,8 +93,8 @@ namespace Tango.FSE.UI TangoIOC.Default.Register<IConsolProvider, DefaultConsoleProvider>(); TangoIOC.Default.Register<IPerformanceProvider, DefaultPerformanceProvider>(); TangoIOC.Default.Register<ISystemInfoProvider, DefaultSystemInfoProvider>(); - TangoIOC.Default.Register<ILoggingProvider, DefaultLoggingProvider>(); TangoIOC.Default.Register<IFileSystemProvider, DefaultFileSystemProvider>(); + TangoIOC.Default.Register<ILoggingProvider, DefaultLoggingProvider>(); TangoIOC.Default.Register<IStorageProvider, DefaultStorageProvider>(); TangoIOC.Default.Register<IMachineUpdatesProvider, DefaultMachineUpdatesProvider>(); diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/FileSystem/DefaultFileSystemService.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/FileSystem/DefaultFileSystemService.cs index 02975a2b3..86506abcf 100644 --- a/Software/Visual_Studio/PPC/Tango.PPC.Common/FileSystem/DefaultFileSystemService.cs +++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/FileSystem/DefaultFileSystemService.cs @@ -11,7 +11,10 @@ using Tango.Core.IO; using Tango.FileSystem; using Tango.FileSystem.Network; using Tango.Integration.ExternalBridge; +using Tango.Integration.Operation; +using Tango.Logging; using Tango.PPC.Common.ExternalBridge; +using Tango.PPC.Shared.Logs; using Tango.Transport; using Tango.Transport.Transporters; using Tango.WebRTC; @@ -360,6 +363,49 @@ namespace Tango.PPC.Common.FileSystem await receiver.SendGenericResponse(new PerformDiskSpaceOptimizationResponse() { DeletedBytes = deletedBytes }, token); } + [ExternalBridgeRequestHandlerMethod(typeof(GetLogFilesRequest), RequestHandlerLoggingMode.LogRequestNameAndContent)] + public async Task OnGetLogFilesRequest(GetLogFilesRequest request, String token, ExternalBridgeReceiver receiver) + { + FolderItem folder = null; + + if (request.LogFileType == RemoteLogFileType.Application) + { + var fileLogger = LogManager.RegisteredLoggers.SingleOrDefault(x => x.GetType() == typeof(FileLogger)) as FileLogger; + + if (fileLogger == null) + { + throw new InvalidOperationException("Could not locate the application file logger."); + } + + folder = await _manager.GetFolder(fileLogger.Folder, false, "*.log") as FolderItem; + } + else + { + if (MachineOperator.EmbeddedLogsFolder == null) + { + throw new InvalidOperationException("The firmware file logger folder could not be read."); + } + + folder = await _manager.GetFolder(MachineOperator.EmbeddedLogsFolder, false, "*.log") as FolderItem; + } + + GetLogFilesResponse response = new GetLogFilesResponse(); + + foreach (var file in folder.Items.OfType<FileItem>().OrderByDescending(x => x.DateCreated).DistinctBy(x => x.Name)) + { + response.LogFiles.Add(new RemoteLogFile() + { + DateModified = file.DateModified, + DateCreated = file.DateCreated, + Name = file.Name, + Path = file.Path, + Length = new FileInfo(file.Path).Length + }); + } + + await receiver.SendGenericResponse(response, token); + } + public void OnReceiverDisconnected(ExternalBridgeReceiver receiver) { if (_webRtcClients.ContainsKey(receiver)) diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Shared/Logs/GetLogFilesRequest.cs b/Software/Visual_Studio/PPC/Tango.PPC.Shared/Logs/GetLogFilesRequest.cs new file mode 100644 index 000000000..bb5d21837 --- /dev/null +++ b/Software/Visual_Studio/PPC/Tango.PPC.Shared/Logs/GetLogFilesRequest.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.Logs +{ + public class GetLogFilesRequest + { + public RemoteLogFileType LogFileType { get; set; } + } +} diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Shared/Logs/GetLogFilesResponse.cs b/Software/Visual_Studio/PPC/Tango.PPC.Shared/Logs/GetLogFilesResponse.cs new file mode 100644 index 000000000..cf5d59726 --- /dev/null +++ b/Software/Visual_Studio/PPC/Tango.PPC.Shared/Logs/GetLogFilesResponse.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Tango.PPC.Shared.Logs +{ + public class GetLogFilesResponse + { + public List<RemoteLogFile> LogFiles { get; set; } + + public GetLogFilesResponse() + { + LogFiles = new List<RemoteLogFile>(); + } + } +} diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Shared/Logs/RemoteLogFile.cs b/Software/Visual_Studio/PPC/Tango.PPC.Shared/Logs/RemoteLogFile.cs new file mode 100644 index 000000000..fc2ba88c4 --- /dev/null +++ b/Software/Visual_Studio/PPC/Tango.PPC.Shared/Logs/RemoteLogFile.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Tango.PPC.Shared.Logs +{ + public class RemoteLogFile + { + public String Name { get; set; } + public DateTime DateModified { get; set; } + public DateTime DateCreated { get; set; } + public String Path { get; set; } + public long Length { get; set; } + } +} diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Shared/Logs/RemoteLogFileType.cs b/Software/Visual_Studio/PPC/Tango.PPC.Shared/Logs/RemoteLogFileType.cs new file mode 100644 index 000000000..958b4c195 --- /dev/null +++ b/Software/Visual_Studio/PPC/Tango.PPC.Shared/Logs/RemoteLogFileType.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Tango.PPC.Shared.Logs +{ + public enum RemoteLogFileType + { + Application, + Firmware + } +} 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 2ae1c7575..96c18129a 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 @@ -60,6 +60,10 @@ <Compile Include="Information\GetMachineInformationRequest.cs" /> <Compile Include="Information\GetMachineInformationResponse.cs" /> <Compile Include="Information\InformationPackage.cs" /> + <Compile Include="Logs\GetLogFilesRequest.cs" /> + <Compile Include="Logs\GetLogFilesResponse.cs" /> + <Compile Include="Logs\RemoteLogFile.cs" /> + <Compile Include="Logs\RemoteLogFileType.cs" /> <Compile Include="Performance\PerformancePackage.cs" /> <Compile Include="Performance\StartPerformanceUpdatesRequest.cs" /> <Compile Include="Performance\StartPerformanceUpdatesResponse.cs" /> diff --git a/Software/Visual_Studio/Tango.FileSystem/FileSystemItem.cs b/Software/Visual_Studio/Tango.FileSystem/FileSystemItem.cs index c8b2fce32..558251f3c 100644 --- a/Software/Visual_Studio/Tango.FileSystem/FileSystemItem.cs +++ b/Software/Visual_Studio/Tango.FileSystem/FileSystemItem.cs @@ -25,6 +25,8 @@ namespace Tango.FileSystem public DateTime DateModified { get; set; } + public DateTime DateCreated { get; set; } + public long Size { get; set; } public String Name @@ -75,6 +77,7 @@ namespace Tango.FileSystem } item.DateModified = dto.DateModified; + item.DateCreated = dto.DateCreated; item.Path = dto.Path; item.Size = dto.Size; item.Type = dto.Type; diff --git a/Software/Visual_Studio/Tango.FileSystem/FileSystemManager.cs b/Software/Visual_Studio/Tango.FileSystem/FileSystemManager.cs index c08304ca8..dc8efa7dd 100644 --- a/Software/Visual_Studio/Tango.FileSystem/FileSystemManager.cs +++ b/Software/Visual_Studio/Tango.FileSystem/FileSystemManager.cs @@ -71,6 +71,7 @@ namespace Tango.FileSystem Path = directory, Type = FileSystemItemType.Folder, DateModified = Directory.GetLastWriteTimeUtc(directory), + DateCreated = Directory.GetCreationTimeUtc(directory), }); } @@ -83,6 +84,7 @@ namespace Tango.FileSystem Path = file, Type = FileSystemItemType.File, DateModified = File.GetLastWriteTimeUtc(file), + DateCreated = File.GetCreationTimeUtc(file), Size = new FileInfo(file).Length }); } diff --git a/Software/Visual_Studio/Tango.FileSystem/Network/FileSystemItemDTO.cs b/Software/Visual_Studio/Tango.FileSystem/Network/FileSystemItemDTO.cs index 900ba0628..43467f227 100644 --- a/Software/Visual_Studio/Tango.FileSystem/Network/FileSystemItemDTO.cs +++ b/Software/Visual_Studio/Tango.FileSystem/Network/FileSystemItemDTO.cs @@ -19,6 +19,8 @@ namespace Tango.FileSystem.Network public DateTime DateModified { get; set; } + public DateTime DateCreated { get; set; } + public long Size { get; set; } public bool IsRoot { get; set; } diff --git a/Software/Visual_Studio/Tango.Integration/Logging/EmbeddedLogFileParser.cs b/Software/Visual_Studio/Tango.Integration/Logging/EmbeddedLogFileParser.cs index 72e55bbfd..bfe1cebcc 100644 --- a/Software/Visual_Studio/Tango.Integration/Logging/EmbeddedLogFileParser.cs +++ b/Software/Visual_Studio/Tango.Integration/Logging/EmbeddedLogFileParser.cs @@ -115,5 +115,12 @@ namespace Tango.Integration.Logging } } } + + public List<EmbeddedLogItem> Parse(string file, DateTime fileDate) + { + List<EmbeddedLogItem> logs = new List<EmbeddedLogItem>(); + Parse(file, fileDate, ref logs); + return logs; + } } } diff --git a/Software/Visual_Studio/Tango.Logging/ApplicationLogFileParser.cs b/Software/Visual_Studio/Tango.Logging/ApplicationLogFileParser.cs index 85f82d04e..1530c4d27 100644 --- a/Software/Visual_Studio/Tango.Logging/ApplicationLogFileParser.cs +++ b/Software/Visual_Studio/Tango.Logging/ApplicationLogFileParser.cs @@ -75,6 +75,13 @@ namespace Tango.Logging return logItems; } + public List<LogItemBase> Parse(String file, DateTime fileDate) + { + List<LogItemBase> logs = new List<LogItemBase>(); + Parse(file, fileDate, ref logs); + return logs; + } + private void Parse(string file, DateTime datetime, ref List<LogItemBase> logItems) { String text = File.ReadAllText(file); diff --git a/Software/Visual_Studio/Tango.Logging/ILogFileParser.cs b/Software/Visual_Studio/Tango.Logging/ILogFileParser.cs index bc43c7cd0..64b4c7206 100644 --- a/Software/Visual_Studio/Tango.Logging/ILogFileParser.cs +++ b/Software/Visual_Studio/Tango.Logging/ILogFileParser.cs @@ -10,6 +10,8 @@ namespace Tango.Logging { List<T> Parse(LogFile logFile); + List<T> Parse(String file, DateTime fileDate); + List<LogFile> GetLogFiles(); } } diff --git a/Software/Visual_Studio/Tango.Logging/LogFile.cs b/Software/Visual_Studio/Tango.Logging/LogFile.cs index c86ec1792..f727b96f4 100644 --- a/Software/Visual_Studio/Tango.Logging/LogFile.cs +++ b/Software/Visual_Studio/Tango.Logging/LogFile.cs @@ -28,7 +28,6 @@ namespace Tango.Logging /// </summary> public bool PartOfSet { get; set; } - /// <summary> /// Gets or sets the start index of the set. /// </summary> diff --git a/Software/Visual_Studio/Tango.Logging/LogItemBase.cs b/Software/Visual_Studio/Tango.Logging/LogItemBase.cs index f89b73b40..01d520f6a 100644 --- a/Software/Visual_Studio/Tango.Logging/LogItemBase.cs +++ b/Software/Visual_Studio/Tango.Logging/LogItemBase.cs @@ -47,6 +47,14 @@ namespace Tango.Logging } /// <summary> + /// Gets the name of the caller file class. + /// </summary> + public String ClassName + { + get { return RelativeCallerFile.Split('\\').LastOrDefault()?.Split('.').FirstOrDefault(); } + } + + /// <summary> /// Gets or sets the caller line number. /// </summary> public int CallerLineNumber { get; set; } diff --git a/Software/Visual_Studio/Tango.SharedUI/Components/BindingProxy.cs b/Software/Visual_Studio/Tango.SharedUI/Components/BindingProxy.cs new file mode 100644 index 000000000..e13436900 --- /dev/null +++ b/Software/Visual_Studio/Tango.SharedUI/Components/BindingProxy.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; + +namespace Tango.SharedUI.Components +{ + public class BindingProxy : Freezable + { + #region Overrides of Freezable + + protected override Freezable CreateInstanceCore() + { + return new BindingProxy(); + } + + #endregion + + public object Data + { + get { return (object)GetValue(DataProperty); } + set { SetValue(DataProperty, value); } + } + + public static readonly DependencyProperty DataProperty = + DependencyProperty.Register("Data", typeof(object), + typeof(BindingProxy)); + } +} diff --git a/Software/Visual_Studio/Tango.SharedUI/Converters/StringToOneLineConverter.cs b/Software/Visual_Studio/Tango.SharedUI/Converters/StringToOneLineConverter.cs index d130ebaaf..e1255ef13 100644 --- a/Software/Visual_Studio/Tango.SharedUI/Converters/StringToOneLineConverter.cs +++ b/Software/Visual_Studio/Tango.SharedUI/Converters/StringToOneLineConverter.cs @@ -15,11 +15,11 @@ namespace Tango.SharedUI.Converters if (value != null) { string str = value.ToString(); - int newLineIndex = str.IndexOf(Environment.NewLine); + int newLineIndex = str.IndexOf("\n"); if (newLineIndex == -1) { - newLineIndex = str.IndexOf("\n"); + newLineIndex = str.IndexOf(Environment.NewLine); } string firstline = str; diff --git a/Software/Visual_Studio/Tango.SharedUI/Tango.SharedUI.csproj b/Software/Visual_Studio/Tango.SharedUI/Tango.SharedUI.csproj index 000a4e5d7..a649d0c1a 100644 --- a/Software/Visual_Studio/Tango.SharedUI/Tango.SharedUI.csproj +++ b/Software/Visual_Studio/Tango.SharedUI/Tango.SharedUI.csproj @@ -67,6 +67,7 @@ <Compile Include="Binding\BindingEventArgs.cs" /> <Compile Include="Binding\BindingEventContainer.cs" /> <Compile Include="Binding\BindingProperty.cs" /> + <Compile Include="Components\BindingProxy.cs" /> <Compile Include="Components\SelectedObject.cs" /> <Compile Include="Components\SelectedObjectCollection.cs" /> <Compile Include="Components\TextController.cs" /> @@ -255,7 +256,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> </Project>
\ No newline at end of file |
