diff options
| author | Roy Ben Shabat <Roy.mail.net@gmail.com> | 2020-03-28 10:02:12 +0300 |
|---|---|---|
| committer | Roy Ben Shabat <Roy.mail.net@gmail.com> | 2020-03-28 10:02:12 +0300 |
| commit | 3d6a882cf14f36297d8b379e0fdf65376064edf7 (patch) | |
| tree | 91728997624076d0eb0d6b38df011851e559a838 /Software | |
| parent | 888d3037722f80b00c12e140ba101f58661ec4b6 (diff) | |
| download | Tango-3d6a882cf14f36297d8b379e0fdf65376064edf7.tar.gz Tango-3d6a882cf14f36297d8b379e0fdf65376064edf7.zip | |
Many changes and improvements.
Diffstat (limited to 'Software')
40 files changed, 1412 insertions, 64 deletions
diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/ViewModels/FileSystemViewVM.cs b/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/ViewModels/FileSystemViewVM.cs index e10cc0ad1..3a4a87884 100644 --- a/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/ViewModels/FileSystemViewVM.cs +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/ViewModels/FileSystemViewVM.cs @@ -9,6 +9,7 @@ using System.Text; using System.Threading.Tasks; using System.Windows.Input; using Tango.Core.Commands; +using Tango.Core.Helpers; using Tango.FileSystem; using Tango.FSE.Common; using Tango.FSE.Common.Connection; @@ -57,6 +58,7 @@ namespace Tango.FSE.PPCConsole.ViewModels public RelayCommand<List<FileSystemItem>> CopyPasteCommand { get; set; } public RelayCommand<List<FileSystemItem>> CutPasteCommand { get; set; } public RelayCommand<List<FileSystemItem>> DownloadCommand { get; set; } + public RelayCommand UploadCommand { get; set; } public RelayCommand<FileSystemItem> RenameCommand { get; set; } public RelayCommand NewFolderCommand { get; set; } @@ -72,6 +74,7 @@ namespace Tango.FSE.PPCConsole.ViewModels NavigateToFolderCommand = new RelayCommand<string>(async (x) => await Navigate(x)); DeleteCommand = new RelayCommand<IList<FileSystemItem>>(DeleteSelectedItems); DragCommand = new RelayCommand<List<DragItem>>(OnItemsDraggedOut); + DropCommand = new RelayCommand<List<FileSystemItem>>(OnItemsDroppedIn); DeleteFileSystemHandlerCommand = new RelayCommand<FileSystemHandler>(DeleteFileSystemHandler); OpenFileSystemHandlerDestinationCommand = new RelayCommand<FileSystemHandler>(OpenFileSystemHandlerDestination); RetryFailedFileSystemHandlerCommand = new RelayCommand<FileSystemHandler>(RetryFailedFileSystemHandler); @@ -80,6 +83,7 @@ namespace Tango.FSE.PPCConsole.ViewModels DownloadCommand = new RelayCommand<List<FileSystemItem>>(DownloadSelectedItems); RenameCommand = new RelayCommand<FileSystemItem>(RenameFileSystemItem); NewFolderCommand = new RelayCommand(CreateNewFolder); + UploadCommand = new RelayCommand(UploadFilesAndFolder); } private async void NavigateBack() @@ -114,9 +118,12 @@ namespace Tango.FSE.PPCConsole.ViewModels } } - private async void NavigateToCurrentPath() + private void NavigateToCurrentPath() { - await Navigate(CurrentPath); + InvokeUI(async () => + { + await Navigate(CurrentPath); + }); } private async void OpenFileSystemItem(FileSystemItem item) @@ -195,6 +202,36 @@ namespace Tango.FSE.PPCConsole.ViewModels } } + private async void OnItemsDroppedIn(List<FileSystemItem> items) + { + String currentPathBefore = CurrentPath; + + foreach (var item in items.Where(x => x.Type != FileSystemItemType.Drive)) + { + Debug.WriteLine($"Dropped in: {item.Name} => {CurrentItem.Path}"); + + if ((CurrentItem as IFileSystemContainer).Items.ToList().Exists(x => x.Name.ToLower() == item.Name.ToLower())) + { + if (!await NotificationProvider.ShowWarningQuestion($"'{item.Name}' already exists on '{CurrentItem.Name}'. Do you want to overwrite?")) + { + continue; + } + } + + var handler = await FileSystemProvider.Upload(item.Path, CurrentItem); + + handler.StatusChanged += (x, status) => + { + if (status == FileSystemHandlerStatus.Completed && currentPathBefore == CurrentPath) + { + NavigateToCurrentPath(); + } + }; + + FileSystemHandlers.Insert(0, handler); + } + } + private async void DeleteFileSystemHandler(FileSystemHandler handler) { if (handler.Status != FileSystemHandlerStatus.Completed && handler.Status != FileSystemHandlerStatus.Failed) @@ -216,19 +253,26 @@ namespace Tango.FSE.PPCConsole.ViewModels } } - private void OpenFileSystemHandlerDestination(FileSystemHandler handler) + private async void OpenFileSystemHandlerDestination(FileSystemHandler handler) { String destination = String.Empty; - if (File.Exists(handler.Destination) || Directory.Exists(handler.Destination)) + if (handler.Type == FileSystemHandlerType.FileDownload || handler.Type == FileSystemHandlerType.FolderDownload) { - destination = handler.Destination; - Process.Start("explorer.exe", string.Format("/select,\"{0}\"", destination)); + if (File.Exists(handler.Destination) || Directory.Exists(handler.Destination)) + { + destination = handler.Destination; + Process.Start("explorer.exe", string.Format("/select,\"{0}\"", destination)); + } + else + { + destination = Path.GetDirectoryName(handler.Destination); + Process.Start("explorer.exe", destination); + } } else { - destination = Path.GetDirectoryName(handler.Destination); - Process.Start("explorer.exe", destination); + await Navigate(Path.GetDirectoryName(handler.Destination)); } } @@ -364,6 +408,49 @@ namespace Tango.FSE.PPCConsole.ViewModels } } + private async void UploadFilesAndFolder() + { + var result = await StorageProvider.SelectFilesAndFolders("Select files and folders to upload"); + if (result) + { + String currentPathBefore = CurrentPath; + + foreach (var item in result.SelectedItems) + { + if (!File.Exists(item) && !Directory.Exists(item)) + { + await NotificationProvider.ShowError($"File or folder '{item}' cannot be uploaded."); + return; + } + } + + foreach (var item in result.SelectedItems) + { + String itemName = Path.GetFileName(item); + + if ((CurrentItem as IFileSystemContainer).Items.ToList().Exists(x => x.Name.ToLower() == itemName.ToLower())) + { + if (!await NotificationProvider.ShowWarningQuestion($"'{itemName}' already exists on '{CurrentItem.Name}'. Do you want to overwrite?")) + { + continue; + } + } + + var handler = await FileSystemProvider.Upload(item, CurrentItem); + + handler.StatusChanged += (x, status) => + { + if (status == FileSystemHandlerStatus.Completed && currentPathBefore == CurrentPath) + { + NavigateToCurrentPath(); + } + }; + + FileSystemHandlers.Insert(0, handler); + } + } + } + private async void RenameFileSystemItem(FileSystemItem item) { if (item.Type != FileSystemItemType.Drive) @@ -423,7 +510,7 @@ namespace Tango.FSE.PPCConsole.ViewModels } catch (Exception ex) { - LogManager.Log(ex, $"Error creating new folder '{Path.Combine(CurrentItem.Path,result.Input)}."); + LogManager.Log(ex, $"Error creating new folder '{Path.Combine(CurrentItem.Path, result.Input)}."); await NotificationProvider.ShowError($"Error creating folder '{result.Input}'.\n{ex.FlattenMessage()}"); } } diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/ViewModels/MonitoringViewVM.cs b/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/ViewModels/MonitoringViewVM.cs index eb59b2c9a..890adcf54 100644 --- a/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/ViewModels/MonitoringViewVM.cs +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/ViewModels/MonitoringViewVM.cs @@ -8,6 +8,8 @@ using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Media; +using Tango.Core.Commands; +using Tango.Core.Helpers; using Tango.FSE.Common; using Tango.FSE.Common.Graphs; using Tango.FSE.Common.Performance; @@ -74,6 +76,8 @@ namespace Tango.FSE.PPCConsole.ViewModels } } + public RelayCommand PerformDiskSpaceOptimizationCommand { get; set; } + public MonitoringViewVM() { CPUController = CreateController(CreateSeries("Total", GraphHelper.GraphColor.White), CreateSeries("Application", GraphHelper.GraphColor.Red)); @@ -84,6 +88,8 @@ namespace Tango.FSE.PPCConsole.ViewModels { return $"{(point.Y / 1000d).ToString("0.0")} GB"; }; + + PerformDiskSpaceOptimizationCommand = new RelayCommand(PerformDiskSpaceOptimization); } private WpfGraphController<DateTimeDataPoint, DoubleDataPoint> CreateController(params WpfGraphDataSeries[] seriesCollection) @@ -207,5 +213,17 @@ namespace Tango.FSE.PPCConsole.ViewModels } } } + + private async void PerformDiskSpaceOptimization() + { + if (await NotificationProvider.ShowQuestion("The following stage will try to optimize the disk space on the remote machine panel PC. Do you wish to continue?", "RUN OPTIMIZATION", "NO")) + { + using (NotificationProvider.PushTaskItem("Performing disk space optimization, please wait...")) + { + var response = await FileSystemProvider.PerformDiskSpaceOptimization(); + await NotificationProvider.ShowSuccess($"Disk space optimization completed successfully.\n{FileHelper.GetFriendlyFileSize(response.DeletedBytes)} cleared!"); + } + } + } } } diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/Views/FileSystemView.xaml b/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/Views/FileSystemView.xaml index 67f1dc1c5..d8b82f6c3 100644 --- a/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/Views/FileSystemView.xaml +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/Views/FileSystemView.xaml @@ -118,7 +118,7 @@ <Grid.RowDefinitions> <RowDefinition Height="1*" MinHeight="100" /> <RowDefinition Height="5" /> - <RowDefinition Height="100" MinHeight="0" /> + <RowDefinition Height="105" MinHeight="0" /> </Grid.RowDefinitions> <controls:FileSystemControl x:Name="fileSystemControl" @@ -135,6 +135,7 @@ CutPasteCommand="{Binding CutPasteCommand}" CopyPasteCommand="{Binding CopyPasteCommand}" DownloadCommand="{Binding DownloadCommand}" + UploadCommand="{Binding UploadCommand}" BackCommand="{Binding BackCommand}" RenameCommand="{Binding RenameCommand}" NewFolderCommand="{Binding NewFolderCommand}"/> @@ -217,7 +218,7 @@ <Grid DockPanel.Dock="Right" Width="140" Margin="20 0 0 0"> <UniformGrid Columns="3"> <Grid> - <controls:ToggleIconButton Width="24" Height="24" UncheckedForeground="{StaticResource FSE_OrangeBrush}" CheckedForeground="{StaticResource FSE_GreenBrush}" UncheckedIcon="Pause" CheckedIcon="Play" Cursor="Hand" IsChecked="{Binding IsPaused,Mode=TwoWay}"> + <controls:ToggleIconButton ToolTip="Pause/Resume" Width="24" Height="24" UncheckedForeground="{StaticResource FSE_OrangeBrush}" CheckedForeground="{StaticResource FSE_GreenBrush}" UncheckedIcon="Pause" CheckedIcon="Play" Cursor="Hand" IsChecked="{Binding IsPaused,Mode=TwoWay}"> <controls:ToggleIconButton.Style> <Style TargetType="controls:ToggleIconButton" BasedOn="{StaticResource {x:Type controls:ToggleIconButton}}"> <Setter Property="IsEnabled" Value="False"></Setter> @@ -243,7 +244,7 @@ </Style> </controls:ToggleIconButton.Style> </controls:ToggleIconButton> - <controls:IconButton Icon="Restart" Cursor="Hand" Command="{Binding RelativeSource={RelativeSource AncestorType=UserControl},Path=DataContext.RetryFailedFileSystemHandlerCommand}" CommandParameter="{Binding}"> + <controls:IconButton Icon="Restart" ToolTip="Try again" Cursor="Hand" Command="{Binding RelativeSource={RelativeSource AncestorType=UserControl},Path=DataContext.RetryFailedFileSystemHandlerCommand}" CommandParameter="{Binding}"> <controls:IconButton.Style> <Style TargetType="controls:IconButton" BasedOn="{StaticResource {x:Type controls:IconButton}}"> <Setter Property="Visibility" Value="Hidden"></Setter> @@ -256,8 +257,8 @@ </controls:IconButton.Style> </controls:IconButton> </Grid> - <controls:IconButton Icon="FolderOpen" Cursor="Hand" Command="{Binding RelativeSource={RelativeSource AncestorType=UserControl},Path=DataContext.OpenFileSystemHandlerDestinationCommand}" CommandParameter="{Binding}" /> - <controls:IconButton Icon="DeleteForever" Foreground="{StaticResource FSE_RedBrush}" Cursor="Hand" Command="{Binding RelativeSource={RelativeSource AncestorType=UserControl},Path=DataContext.DeleteFileSystemHandlerCommand}" CommandParameter="{Binding}" /> + <controls:IconButton Icon="FolderOpen" ToolTip="Open containing folder" Cursor="Hand" Command="{Binding RelativeSource={RelativeSource AncestorType=UserControl},Path=DataContext.OpenFileSystemHandlerDestinationCommand}" CommandParameter="{Binding}" /> + <controls:IconButton Icon="DeleteForever" ToolTip="Remove from queue" Foreground="{StaticResource FSE_RedBrush}" Cursor="Hand" Command="{Binding RelativeSource={RelativeSource AncestorType=UserControl},Path=DataContext.DeleteFileSystemHandlerCommand}" CommandParameter="{Binding}" /> </UniformGrid> </Grid> diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/Views/MonitoringView.xaml b/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/Views/MonitoringView.xaml index 22e2566b5..397a07377 100644 --- a/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/Views/MonitoringView.xaml +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/Views/MonitoringView.xaml @@ -1,6 +1,7 @@ <UserControl x:Class="Tango.FSE.PPCConsole.Views.MonitoringView" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:material="http://materialdesigninxaml.net/winfx/xaml/themes" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:graphs="clr-namespace:Tango.FSE.Common.Graphs;assembly=Tango.FSE.Common" @@ -103,7 +104,7 @@ <Grid Grid.Row="1" Grid.ColumnSpan="2" Margin="0 40 0 0"> <Grid.ColumnDefinitions> - <ColumnDefinition Width="2*"/> + <ColumnDefinition Width="2.5*"/> <ColumnDefinition Width="8*"/> </Grid.ColumnDefinitions> @@ -112,7 +113,14 @@ <TextBlock DockPanel.Dock="Top" Foreground="{StaticResource FSE_GrayBrush}" HorizontalAlignment="Left" VerticalAlignment="Top" FontSize="{StaticResource FSE_LargeFontSize}" FontWeight="SemiBold">AVAILABLE DISK SPACE</TextBlock> - <lvc:PieChart Opacity="0.7" DisableAnimations="True" LegendLocation="Bottom" Hoverable="False" DataTooltip="{x:Null}" HorizontalAlignment="Left" Width="250"> + <Button ToolTip="Perform automatic disk space optimization" Command="{Binding PerformDiskSpaceOptimizationCommand}" IsEnabled="{Binding MachineProvider.IsConnected}" DockPanel.Dock="Right" FontSize="{StaticResource FSE_SmallFontSize}" Background="Transparent" VerticalAlignment="Top" Margin="0 12 10 0" Style="{StaticResource FSE_Button_Orange}"> + <StackPanel Orientation="Horizontal"> + <material:PackIcon Kind="Recycle" Margin="0 -2 0 0" /> + <TextBlock Margin="10 0 0 0" VerticalAlignment="Center" Visibility="{Binding ResolutionService.IsHighResolution,Converter={StaticResource BooleanToVisibilityConverter}}">FREE SPACE</TextBlock> + </StackPanel> + </Button> + + <lvc:PieChart Opacity="0.7" Margin="-20 0 0 0" DisableAnimations="True" LegendLocation="Bottom" Hoverable="False" DataTooltip="{x:Null}" HorizontalAlignment="Left" Width="250"> <lvc:PieChart.Series> <lvc:PieSeries Foreground="{StaticResource FSE_PrimaryForegroundBrush}" Stroke="{StaticResource FSE_PrimaryBackgroundBrush}" Fill="#F47FD121" FontSize="16" Title="Free" Values="{Binding AvailableDiskSpace,Converter={StaticResource DoubleToChartValuesConverter}}" DataLabels="True" LabelPoint="{Binding DiskSpacePointLabel}"/> diff --git a/Software/Visual_Studio/FSE/Tango.FSE.Common/Controls/FileSystemControl.xaml b/Software/Visual_Studio/FSE/Tango.FSE.Common/Controls/FileSystemControl.xaml index 5bc75ca54..63d90c171 100644 --- a/Software/Visual_Studio/FSE/Tango.FSE.Common/Controls/FileSystemControl.xaml +++ b/Software/Visual_Studio/FSE/Tango.FSE.Common/Controls/FileSystemControl.xaml @@ -43,7 +43,7 @@ BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}"> <Border.ContextMenu> - <ContextMenu Width="250" IsOpen="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},Path=IsContextMenuOpened,Mode=TwoWay}"> + <ContextMenu FontSize="13" Width="250" IsOpen="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},Path=IsContextMenuOpened,Mode=TwoWay}"> <MenuItem Header="Open" InputGestureText="Enter" Command="{TemplateBinding OpenCommand}"> <MenuItem.Icon> <material:PackIcon Kind="SubdirectoryArrowRight" /> @@ -66,7 +66,6 @@ <material:PackIcon Kind="ContentCut" /> </MenuItem.Icon> </MenuItem> - <Separator/> <MenuItem Header="Paste" InputGestureText="Ctrl+V" Command="{TemplateBinding PasteCommandInternal}"> <MenuItem.Icon> <material:PackIcon Kind="ContentPaste" /> @@ -78,7 +77,6 @@ <material:PackIcon Kind="Rename" /> </MenuItem.Icon> </MenuItem> - <Separator/> <MenuItem Header="Delete" InputGestureText="DEL" Command="{TemplateBinding DeleteCommandInternal}"> <MenuItem.Icon> <material:PackIcon Kind="DeleteForever" /> @@ -90,6 +88,11 @@ <material:PackIcon Kind="Download" /> </MenuItem.Icon> </MenuItem> + <MenuItem Header="Upload" InputGestureText="Ctrl+U" Command="{TemplateBinding UploadCommandInternal}"> + <MenuItem.Icon> + <material:PackIcon Kind="Upload" /> + </MenuItem.Icon> + </MenuItem> <Separator/> <MenuItem Header="Select All" InputGestureText="Ctrl+A" Command="{TemplateBinding SelectAllCommand}"> <MenuItem.Icon> @@ -100,7 +103,7 @@ </Border.ContextMenu> <Grid Background="Transparent"> - <ListBox x:Name="PART_listbox" IsTextSearchEnabled="True" TextSearch.TextPath="Name" Background="Transparent" SelectionMode="{TemplateBinding SelectionMode}" ScrollViewer.HorizontalScrollBarVisibility="Disabled" ScrollViewer.VerticalScrollBarVisibility="Auto" ItemsSource="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},Path=CurrentItem.Items}"> + <ListBox x:Name="PART_listbox" ScrollViewer.CanContentScroll="False" IsTextSearchEnabled="True" TextSearch.TextPath="Name" Background="Transparent" SelectionMode="{TemplateBinding SelectionMode}" ScrollViewer.HorizontalScrollBarVisibility="Disabled" ScrollViewer.VerticalScrollBarVisibility="Auto" ItemsSource="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},Path=CurrentItem.Items}"> <ListBox.Style> <Style TargetType="ListBox" BasedOn="{StaticResource {x:Type ListBox}}"> <Setter Property="Visibility" Value="Collapsed"></Setter> @@ -171,7 +174,7 @@ </Style.Triggers> </Style> </Grid.Style> - <local:FileSystemDataGrid x:Name="PART_datagrid" IsTextSearchEnabled="True" TextSearch.TextPath="Name" ItemsSource="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},Path=CurrentItem.Items}" CellStyle="{StaticResource FileSystemCellStyle}"> + <local:FileSystemDataGrid x:Name="PART_datagrid" ScrollViewer.CanContentScroll="False" IsTextSearchEnabled="True" TextSearch.TextPath="Name" ItemsSource="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},Path=CurrentItem.Items}" CellStyle="{StaticResource FileSystemCellStyle}"> <DataGrid.Style> <Style TargetType="DataGrid" BasedOn="{StaticResource {x:Type DataGrid}}"> <Setter Property="Background" Value="Transparent"></Setter> diff --git a/Software/Visual_Studio/FSE/Tango.FSE.Common/FileSystem/FileSystemHandler.cs b/Software/Visual_Studio/FSE/Tango.FSE.Common/FileSystem/FileSystemHandler.cs index a748a63cc..e74395ade 100644 --- a/Software/Visual_Studio/FSE/Tango.FSE.Common/FileSystem/FileSystemHandler.cs +++ b/Software/Visual_Studio/FSE/Tango.FSE.Common/FileSystem/FileSystemHandler.cs @@ -15,6 +15,8 @@ namespace Tango.FSE.Common.FileSystem private System.Timers.Timer _transferRateTimer; private double _lastPosition; + public event EventHandler<FileSystemHandlerStatus> StatusChanged; + public FileSystemHandlerType Type { get; set; } private FileSystemHandlerStatus _status; @@ -23,8 +25,12 @@ namespace Tango.FSE.Common.FileSystem get { return _status; } set { - _status = value; - RaisePropertyChangedAuto(); + if (_status != value) + { + _status = value; + RaisePropertyChangedAuto(); + StatusChanged?.Invoke(this, _status); + } } } @@ -85,8 +91,9 @@ namespace Tango.FSE.Common.FileSystem public String Destination { get; set; } public String OperationId { get; set; } - public FileSystemHandler(FileSystemItem fileSystemItem, String destination, Action abortAction) + public FileSystemHandler(FileSystemHandlerType type, FileSystemItem fileSystemItem, String destination, Action abortAction) { + Type = type; FileSystemItem = fileSystemItem; Destination = destination; _abortAction = abortAction; diff --git a/Software/Visual_Studio/FSE/Tango.FSE.Common/FileSystem/IFileSystemProvider.cs b/Software/Visual_Studio/FSE/Tango.FSE.Common/FileSystem/IFileSystemProvider.cs index 00546094e..7580fd9c8 100644 --- a/Software/Visual_Studio/FSE/Tango.FSE.Common/FileSystem/IFileSystemProvider.cs +++ b/Software/Visual_Studio/FSE/Tango.FSE.Common/FileSystem/IFileSystemProvider.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Text; using System.Threading.Tasks; using Tango.FileSystem; +using Tango.FileSystem.Network; using static System.Environment; namespace Tango.FSE.Common.FileSystem @@ -16,11 +17,12 @@ namespace Tango.FSE.Common.FileSystem Task<IFileSystemContainer> GetSpecialFolder(SpecialFolder specialFolder); Task<IFileSystemContainer> GetThisPC(); Task<FileSystemHandler> Download(FileSystemItem item, String localTargetFolder); - Task<FileSystemHandler> Upload(String sourcePath, String remoteTargetFolder); + Task<FileSystemHandler> Upload(String localSourcePath, FileSystemItem remoteFolder); Task Copy(FileSystemItem source, FileSystemItem target); Task Move(FileSystemItem source, FileSystemItem target); Task Rename(FileSystemItem source, String newName); Task Delete(FileSystemItem item); Task<FolderItem> CreateFolder(FileSystemItem parent, String folderName); + Task<PerformDiskSpaceOptimizationResponse> PerformDiskSpaceOptimization(); } } diff --git a/Software/Visual_Studio/FSE/Tango.FSE.Common/Notifications/MessageType.cs b/Software/Visual_Studio/FSE/Tango.FSE.Common/Notifications/MessageType.cs index 2d9edcfd3..c4090865e 100644 --- a/Software/Visual_Studio/FSE/Tango.FSE.Common/Notifications/MessageType.cs +++ b/Software/Visual_Studio/FSE/Tango.FSE.Common/Notifications/MessageType.cs @@ -9,6 +9,7 @@ namespace Tango.FSE.Common.Notifications public enum MessageType { Info, + Question, Warning, Error, Success diff --git a/Software/Visual_Studio/FSE/Tango.FSE.Common/Resources/Colors.xaml b/Software/Visual_Studio/FSE/Tango.FSE.Common/Resources/Colors.xaml index 1a355ebf0..0c234fda4 100644 --- a/Software/Visual_Studio/FSE/Tango.FSE.Common/Resources/Colors.xaml +++ b/Software/Visual_Studio/FSE/Tango.FSE.Common/Resources/Colors.xaml @@ -26,7 +26,7 @@ <Color x:Key="FSE_RedColor">#FF6F6F</Color> <Color x:Key="FSE_GreenColor">#8EFF6F</Color> - <Color x:Key="FSE_OrangeColor">#FA9252</Color> + <Color x:Key="FSE_OrangeColor">#FF7C2B</Color> <Color x:Key="FSE_YellowColor">#FFB84B</Color> <Color x:Key="FSE_RealTimeGraph_White">#18FFFFFF</Color> diff --git a/Software/Visual_Studio/FSE/Tango.FSE.Common/Storage/IStorageProvider.cs b/Software/Visual_Studio/FSE/Tango.FSE.Common/Storage/IStorageProvider.cs index 602e6057c..8a511a56c 100644 --- a/Software/Visual_Studio/FSE/Tango.FSE.Common/Storage/IStorageProvider.cs +++ b/Software/Visual_Studio/FSE/Tango.FSE.Common/Storage/IStorageProvider.cs @@ -35,6 +35,7 @@ namespace Tango.FSE.Common.Storage Task<SingleStorageResult> OpenFile(String title, String filter = null, String initialFolder = null); Task<MultiStorageResult> OpenFiles(String title, String filter = null, String initialFolder = null); + Task<MultiStorageResult> SelectFilesAndFolders(String title, String filter = null, String initialFolder = null); Task<SingleStorageResult> SaveFile(String title, String filter = null, String defaultFileName = null, String defaultExtension = null, String initialFolder = null); Task<SingleStorageResult> SelectFolder(String title, String initialFolder = null); } diff --git a/Software/Visual_Studio/FSE/Tango.FSE.UI/FileSystem/DefaultFileSystemProvider.cs b/Software/Visual_Studio/FSE/Tango.FSE.UI/FileSystem/DefaultFileSystemProvider.cs index 6f25e1774..86ee2a73e 100644 --- a/Software/Visual_Studio/FSE/Tango.FSE.UI/FileSystem/DefaultFileSystemProvider.cs +++ b/Software/Visual_Studio/FSE/Tango.FSE.UI/FileSystem/DefaultFileSystemProvider.cs @@ -8,6 +8,7 @@ using System.Text; using System.Threading; using System.Threading.Tasks; using Tango.Core; +using Tango.Core.IO; using Tango.Core.Threading; using Tango.FileSystem; using Tango.FileSystem.Network; @@ -24,7 +25,8 @@ namespace Tango.FSE.UI.FileSystem private IMachineProvider _machineProvider; private BasicTransporter _webRtcTransporter; private const string WEB_RTC_CHANNEL_NAME = "FileSystemChannel"; - private const long MAX_CHUNK_SIZE = 1024 * 10; + private const long MAX_CHUNK_SIZE = 1024 * 100; + private const long MIN_CHUNK_SIZE = 1024; private const long MAX_CHUNK_SIZE_WEB_RTC = 1024 * 50; private const int WEB_RTC_MAX_RETRIES = 8; private List<FileSystemHandler> _activeHandlers; @@ -170,7 +172,7 @@ namespace Tango.FSE.UI.FileSystem destination = Path.Combine(localTargetFolder, item.Name); - handler = new FileSystemHandler(item, destination, async () => + handler = new FileSystemHandler(item.Type == FileSystemItemType.Folder ? FileSystemHandlerType.FolderDownload : FileSystemHandlerType.FileDownload, item, destination, async () => { if (!aborted) { @@ -239,6 +241,7 @@ namespace Tango.FSE.UI.FileSystem long position = 0; bool webRtcFailed = false; int webRtcRetries = WEB_RTC_MAX_RETRIES; + long dynamixMaxChunkSizeSignalR = MAX_CHUNK_SIZE; var tempFile = TemporaryManager.CreateFile(); @@ -288,11 +291,29 @@ namespace Tango.FSE.UI.FileSystem } else { + request.MaxChunkSize = dynamixMaxChunkSizeSignalR; + + Stopwatch watch = new Stopwatch(); + watch.Start(); + response = await _machineProvider.MachineOperator.SendGenericRequest<ChunkDownloadRequest, ChunkDownloadResponse>(request, new TransportRequestConfig() { Timeout = TimeSpan.FromSeconds(30), Priority = QueuePriority.Low }); + + watch.Stop(); + + if (watch.Elapsed.TotalSeconds < 1) + { + dynamixMaxChunkSizeSignalR += 1024 * 10; + } + else if (watch.Elapsed.TotalSeconds > 1) + { + dynamixMaxChunkSizeSignalR -= 1024 * 10; + } + + dynamixMaxChunkSizeSignalR = Math.Max(dynamixMaxChunkSizeSignalR, MIN_CHUNK_SIZE); } using (FileStream fs = new FileStream(tempFile, FileMode.Append)) @@ -323,7 +344,11 @@ namespace Tango.FSE.UI.FileSystem } else if (item.Type == FileSystemItemType.Folder) { - ZipFile.ExtractToDirectory(tempFile, destination); + using (Ionic.Zip.ZipFile zip = new Ionic.Zip.ZipFile(tempFile)) + { + zip.ExtractAll(destination, Ionic.Zip.ExtractExistingFileAction.OverwriteSilently); + } + tempFile.Delete(); } @@ -345,9 +370,225 @@ namespace Tango.FSE.UI.FileSystem return Task.FromResult(handler); } - public Task<FileSystemHandler> Upload(string sourcePath, string remoteTargetFolder) + public Task<FileSystemHandler> Upload(String localSourcePath, FileSystemItem remoteFolder) { - throw new NotImplementedException(); + String operationId = String.Empty; + String destination = Path.Combine(remoteFolder.Path, Path.GetFileName(localSourcePath)); + bool isFolder = false; + bool aborted = false; + + FileSystemItem sourceItem = null; + + if (Directory.Exists(localSourcePath)) + { + sourceItem = new FolderItem() { Path = localSourcePath }; + isFolder = true; + } + else if (File.Exists(localSourcePath)) + { + sourceItem = new FileItem() { Path = localSourcePath }; + isFolder = false; + } + else + { + throw new FileNotFoundException("Could not locate the local file or directory to upload."); + } + + FileSystemHandler handler = null; + + handler = new FileSystemHandler(isFolder ? FileSystemHandlerType.FolderUpload : FileSystemHandlerType.FileUpload, sourceItem, destination, async () => + { + if (!aborted) + { + aborted = true; + try + { + var response = await _machineProvider.MachineOperator.SendGenericRequest<AbortOperationRequest, AbortOperationResponse>( + new AbortOperationRequest() + { + OperationId = operationId + }, new TransportRequestConfig() { Timeout = TimeSpan.FromSeconds(30) }); + } + catch (Exception ex) + { + LogManager.Log(ex, "Error aborting the upload operation."); + } + finally + { + handler.RaiseAborted(); + } + } + }); + + _activeHandlers.Add(handler); + + ThreadFactory.StartNew(async () => + { + try + { + if (!isFolder) + { + var response = await _machineProvider.MachineOperator.SendGenericRequest<FileUploadRequest, FileUploadResponse>( + new FileUploadRequest() + { + Path = destination + }, new TransportRequestConfig() { Timeout = TimeSpan.FromSeconds(20) }); + + operationId = response.OperationId; + handler.OperationId = operationId; + } + else + { + var response = await _machineProvider.MachineOperator.SendGenericRequest<FolderUploadRequest, FolderUploadResponse>( + new FolderUploadRequest() + { + Path = destination + }, new TransportRequestConfig() { Timeout = TimeSpan.FromSeconds(20) }); + + operationId = response.OperationId; + handler.OperationId = operationId; + } + + if (isFolder) + { + var originalPath = localSourcePath; + localSourcePath = TemporaryManager.CreateImaginaryFile().Path; + ZipFile.CreateFromDirectory(originalPath, localSourcePath); + } + } + catch (Exception ex) + { + _activeHandlers.Remove(handler); + handler.RaiseFailed(ex); + return; + } + + long position = 0; + bool webRtcFailed = false; + int webRtcRetries = WEB_RTC_MAX_RETRIES; + long dynamixMaxChunkSizeSignalR = MAX_CHUNK_SIZE; + + using (FileStream fs = new FileStream(localSourcePath, FileMode.Open)) + { + while (position < fs.Length && !aborted) + { + fs.Position = position; + + if (handler.IsPaused) + { + Thread.Sleep(1000); + continue; + } + + try + { + ChunkUploadResponse response = null; + ChunkUploadRequest request = new ChunkUploadRequest() + { + OperationId = operationId, + }; + + if (_webRtcTransporter != null && _webRtcTransporter.State == TransportComponentState.Connected && EnableWebRTC && !webRtcFailed) + { + try + { + byte[] data = new byte[Math.Min(MAX_CHUNK_SIZE_WEB_RTC, fs.Length - fs.Position)]; + fs.Read(data, 0, data.Length); + request.Data = data; + request.IsCompleted = fs.Position == fs.Length; + + response = await _webRtcTransporter.SendGenericRequest<ChunkUploadRequest, ChunkUploadResponse>(request, new TransportRequestConfig() + { + Timeout = request.IsCompleted ? TimeSpan.FromSeconds(120) : TimeSpan.FromSeconds(2), + Priority = QueuePriority.Low + }); + + webRtcRetries = WEB_RTC_MAX_RETRIES; + } + catch (Exception ex) + { + webRtcRetries--; + + if (webRtcRetries == 0) + { + webRtcFailed = true; + LogManager.Log(ex, "WebRTC chunk upload failed. Falling back to standard upload..."); + } + + continue; + } + } + else + { + byte[] data = new byte[Math.Min(dynamixMaxChunkSizeSignalR, fs.Length - fs.Position)]; + fs.Read(data, 0, data.Length); + request.Data = data; + request.IsCompleted = fs.Position == fs.Length; + + Stopwatch watch = new Stopwatch(); + watch.Start(); + + response = await _machineProvider.MachineOperator.SendGenericRequest<ChunkUploadRequest, ChunkUploadResponse>(request, new TransportRequestConfig() + { + Timeout = request.IsCompleted ? TimeSpan.FromSeconds(120) : TimeSpan.FromSeconds(30), + Priority = QueuePriority.Low + }); + + watch.Stop(); + + if (watch.Elapsed.TotalSeconds < 1) + { + dynamixMaxChunkSizeSignalR += 1024 * 10; + } + else if (watch.Elapsed.TotalSeconds > 1) + { + dynamixMaxChunkSizeSignalR -= 1024 * 10; + } + + dynamixMaxChunkSizeSignalR = Math.Max(dynamixMaxChunkSizeSignalR, MIN_CHUNK_SIZE); + } + + position = fs.Position; + handler.InvalidateProgress(position, fs.Length); + } + catch (Exception ex) + { + _activeHandlers.Remove(handler); + handler.RaiseFailed(ex); + + if (isFolder) + { + try + { + fs.Dispose(); + File.Delete(localSourcePath); + } + catch { } + } + + return; + } + } + + if (!aborted) + { + handler.RaiseCompleted(); + } + + if (isFolder) + { + try + { + File.Delete(localSourcePath); + } + catch { } + } + } + + _activeHandlers.Remove(handler); + }); + + return Task.FromResult(handler); } public async Task Copy(FileSystemItem source, FileSystemItem target) @@ -439,5 +680,18 @@ namespace Tango.FSE.UI.FileSystem return FileSystemItem.FromDTO(response.FolderItem) as FolderItem; } + + public async Task<PerformDiskSpaceOptimizationResponse> PerformDiskSpaceOptimization() + { + var response = await _machineProvider.MachineOperator.SendGenericRequest<PerformDiskSpaceOptimizationRequest, PerformDiskSpaceOptimizationResponse>(new PerformDiskSpaceOptimizationRequest() + { + + }, new TransportRequestConfig() + { + Timeout = TimeSpan.FromMinutes(5) + }); + + return response; + } } } diff --git a/Software/Visual_Studio/FSE/Tango.FSE.UI/Notifications/DefaultNotificationProvider.cs b/Software/Visual_Studio/FSE/Tango.FSE.UI/Notifications/DefaultNotificationProvider.cs index 666619b59..861a8c4d4 100644 --- a/Software/Visual_Studio/FSE/Tango.FSE.UI/Notifications/DefaultNotificationProvider.cs +++ b/Software/Visual_Studio/FSE/Tango.FSE.UI/Notifications/DefaultNotificationProvider.cs @@ -245,7 +245,7 @@ namespace Tango.FSE.UI.Notifications Message = message, Title = "Confirm", HasCancel = true, - Type = MessageType.Info, + Type = MessageType.Question, OKText = okText != null ? okText : "YES", CancelText = cancelText != null ? cancelText : "NO" }); diff --git a/Software/Visual_Studio/FSE/Tango.FSE.UI/Storage/DefaultStorageProvider.cs b/Software/Visual_Studio/FSE/Tango.FSE.UI/Storage/DefaultStorageProvider.cs index 627b380da..80c08fb2e 100644 --- a/Software/Visual_Studio/FSE/Tango.FSE.UI/Storage/DefaultStorageProvider.cs +++ b/Software/Visual_Studio/FSE/Tango.FSE.UI/Storage/DefaultStorageProvider.cs @@ -135,7 +135,7 @@ namespace Tango.FSE.UI.Storage } else { - return null; + throw new NotImplementedException(); } } @@ -147,7 +147,19 @@ namespace Tango.FSE.UI.Storage } else { - return null; + throw new NotImplementedException(); + } + } + + public Task<MultiStorageResult> SelectFilesAndFolders(string title, string filter = null, string initialFolder = null) + { + if (UseNativeDialogs) + { + return OpenFilesAndFoldersNative(title, filter, initialFolder); + } + else + { + throw new NotImplementedException(); } } @@ -159,7 +171,7 @@ namespace Tango.FSE.UI.Storage } else { - return null; + throw new NotImplementedException(); } } @@ -364,6 +376,28 @@ namespace Tango.FSE.UI.Storage return Task.FromResult(result); } + public Task<MultiStorageResult> OpenFilesAndFoldersNative(string title, string filter = null, string initialFolder = null) + { + MultiStorageResult result = new MultiStorageResult(); + + ExplorerControlDialog dlg = new ExplorerControlDialog(); + dlg.Owner = Application.Current.MainWindow; + dlg.Title = title; + + if (initialFolder != null) + { + dlg.InitialDirectory = initialFolder; + } + + if (dlg.ShowDialog().Value) + { + result.Confirmed = true; + result.SelectedItems = dlg.SelectedItems.ToList(); + } + + return Task.FromResult(result); + } + public Task<SingleStorageResult> SaveFileNative(string title, string filter = null, string defaultFileName = null, string defaultExtension = null, string initialFolder = null) { SingleStorageResult result = new SingleStorageResult(); diff --git a/Software/Visual_Studio/FSE/Tango.FSE.UI/Storage/ExplorerControlDialog.xaml b/Software/Visual_Studio/FSE/Tango.FSE.UI/Storage/ExplorerControlDialog.xaml new file mode 100644 index 000000000..2a3831742 --- /dev/null +++ b/Software/Visual_Studio/FSE/Tango.FSE.UI/Storage/ExplorerControlDialog.xaml @@ -0,0 +1,164 @@ +<Window x:Class="Tango.FSE.UI.Storage.ExplorerControlDialog" + xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:material="http://materialdesigninxaml.net/winfx/xaml/themes" + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:WindowsAPICodePackPresentation="clr-namespace:Microsoft.WindowsAPICodePack.Controls.WindowsPresentationFoundation;assembly=Microsoft.WindowsAPICodePack.Shell" + xmlns:WindowsAPICodePackShell="clr-namespace:Microsoft.WindowsAPICodePack.Shell;assembly=Microsoft.WindowsAPICodePack.Shell" + xmlns:wf="clr-namespace:System.Windows.Forms;assembly=System.Windows.Forms" + xmlns:local="clr-namespace:Tango.FSE.Storage.UI" + mc:Ignorable="d" + Title="Select Files And Folder" Height="530" Width="943" DataContext="{Binding RelativeSource={RelativeSource Mode=Self}}"> + + <Window.Resources> + <Style TargetType="Button" x:Key="FlatButton"> + <Setter Property="Background" Value="Transparent"/> + <Setter Property="BorderThickness" Value="0"></Setter> + <Setter Property="BorderBrush" Value="Gainsboro"></Setter> + <Setter Property="VerticalAlignment" Value="Center"></Setter> + <Setter Property="Foreground" Value="Gray"></Setter> + <Setter Property="Template"> + <Setter.Value> + <ControlTemplate TargetType="{x:Type Button}"> + <Border Background="{TemplateBinding Background}" Padding="{TemplateBinding Padding}" BorderThickness="{TemplateBinding BorderThickness}" BorderBrush="{TemplateBinding BorderBrush}"> + <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/> + </Border> + </ControlTemplate> + </Setter.Value> + </Setter> + <Style.Triggers> + <Trigger Property="IsMouseOver" Value="True"> + <Setter Property="Background" Value="#E5F3FF"/> + </Trigger> + <Trigger Property="IsEnabled" Value="False"> + <Setter Property="Foreground" Value="Gainsboro"/> + </Trigger> + </Style.Triggers> + </Style> + + <Style TargetType="Button" x:Key="FlatButtonIcon"> + <Setter Property="Background" Value="White"></Setter> + <Setter Property="Padding" Value="4"></Setter> + <Setter Property="VerticalAlignment" Value="Center"></Setter> + <Setter Property="Template"> + <Setter.Value> + <ControlTemplate TargetType="Button"> + <Border Padding="{TemplateBinding Padding}" Background="{TemplateBinding Background}"> + <Border.Style> + <Style TargetType="Border"> + <Setter Property="TextElement.Foreground" Value="Gray"></Setter> + <Style.Triggers> + <Trigger Property="IsEnabled" Value="False"> + <Setter Property="TextElement.Foreground" Value="Gainsboro"/> + </Trigger> + <DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},Path=IsMouseOver}" Value="True"> + <Setter Property="TextElement.Foreground" Value="DodgerBlue"></Setter> + </DataTrigger> + </Style.Triggers> + </Style> + </Border.Style> + <ContentPresenter/> + </Border> + </ControlTemplate> + </Setter.Value> + </Setter> + </Style> + </Window.Resources> + + <Grid> + <DockPanel> + <Grid DockPanel.Dock="Top" Height="40"> + <DockPanel Margin="10 0 0 0"> + <Button Style="{StaticResource FlatButtonIcon}" Width="24" Height="24" x:Name="btnBack" Click="BtnBack_Click"> + <material:PackIcon Kind="ArrowLeft" Width="Auto" Height="Auto" Foreground="{Binding Path=(TextElement.Foreground), RelativeSource={RelativeSource AncestorType=ContentPresenter}}" /> + </Button> + <Button Style="{StaticResource FlatButtonIcon}" Margin="10 0 0 0" Width="24" Height="24" x:Name="btnForward" Click="BtnForward_Click"> + <material:PackIcon Kind="ArrowRight" Width="Auto" Height="Auto" Foreground="{Binding Path=(TextElement.Foreground), RelativeSource={RelativeSource AncestorType=ContentPresenter}}" /> + </Button> + + <material:PackIcon Margin="10 0 0 0" VerticalAlignment="Center" Foreground="Gainsboro" Kind="ChevronDown" Width="16" Height="16" /> + + <Button Style="{StaticResource FlatButton}" Margin="10 0 0 0" Width="24" Height="24" Padding="2" x:Name="btnUp" Click="BtnUp_Click"> + <material:PackIcon Kind="ArrowUp" Width="Auto" Height="Auto" /> + </Button> + + <Grid> + <DockPanel Margin="10 0 10 0"> + <Button Style="{StaticResource FlatButton}" DockPanel.Dock="Right" Width="24" Height="20" BorderThickness="0 1 1 1" x:Name="btnRefresh"> + <material:PackIcon Kind="Refresh" Width="Auto" Height="Auto" /> + </Button> + + <Grid> + <TextBox Style="{x:Null}" VerticalAlignment="Center" GotKeyboardFocus="TxtLocation_GotKeyboardFocus" LostKeyboardFocus="TxtLocation_LostKeyboardFocus" x:Name="txtLocation" Background="White" BorderBrush="Gainsboro" VerticalContentAlignment="Center" Padding="2 0 0 0" Height="20"></TextBox> + <ItemsControl x:Name="listHistory" HorizontalAlignment="Left" ItemsSource="{Binding ShellHistory}" Background="White" Height="18" Margin="5 0 0 0"> + <ItemsControl.ItemsPanel> + <ItemsPanelTemplate> + <StackPanel Margin="5 0 0 0" IsItemsHost="True" Orientation="Horizontal"></StackPanel> + </ItemsPanelTemplate> + </ItemsControl.ItemsPanel> + <ItemsControl.ItemTemplate> + <DataTemplate> + <StackPanel Orientation="Horizontal"> + <material:PackIcon Foreground="Gray" VerticalAlignment="Center" Width="13" Height="13" Kind="ChevronRight" /> + <Button Click="OnHistoryItemClicked" Padding="5 0" Style="{StaticResource FlatButton}"> + <TextBlock Foreground="Black" Text="{Binding}"></TextBlock> + </Button> + </StackPanel> + </DataTemplate> + </ItemsControl.ItemTemplate> + </ItemsControl> + </Grid> + </DockPanel> + </Grid> + </DockPanel> + </Grid> + <Grid DockPanel.Dock="Bottom" Height="70" Background="#F5F6F7"> + <DockPanel> + <Button Style="{x:Null}" BorderBrush="#A8A8A8" Margin="0 0 20 0" DockPanel.Dock="Right" VerticalAlignment="Center" Padding="40 5" x:Name="btnCancel" Click="BtnCancel_Click">Cancel</Button> + <Button Style="{x:Null}" Margin="0 0 10 0" IsDefault="True" BorderThickness="2" BorderBrush="#1EB7FF" DockPanel.Dock="Right" VerticalAlignment="Center" Padding="40 5" x:Name="btnSelect" Click="BtnSelect_Click">Select</Button> + <Grid> + + </Grid> + </DockPanel> + </Grid> + + <Grid> + <!--<WindowsAPICodePackPresentation:ExplorerBrowser x:Name="explorerBrowser" + Margin="-5" + BorderThickness="0" + AutoArrange="False" + CheckSelect="False" + ExtendedTiles="False" + FullRowSelect="False" + HideFileNames="False" + NoBrowserViewState="False" + NoColumnHeader="False" + NoHeaderInAllViews="False" + NoIcons="False" + NoSubfolders="False" + SingleClickActivate="False" + SingleSelection="False" + AlwaysNavigate="False" + NavigateOnce="False" + ViewMode="Auto" + CommandsPane="Show" + CommandsOrganizePane="Show" + CommandsViewPane="Show" + DetailsPane="Hide" + NavigationPane="Show" + PreviewPane="Hide" + AdvancedQueryPane="Show" + QueryPane="Show"/>--> + + <WindowsFormsHost> + <WindowsFormsHost.Child> + <local:ExplorerControlForms x:Name="explorer"> + + </local:ExplorerControlForms> + </WindowsFormsHost.Child> + </WindowsFormsHost> + </Grid> + </DockPanel> + </Grid> +</Window> diff --git a/Software/Visual_Studio/FSE/Tango.FSE.UI/Storage/ExplorerControlDialog.xaml.cs b/Software/Visual_Studio/FSE/Tango.FSE.UI/Storage/ExplorerControlDialog.xaml.cs new file mode 100644 index 000000000..db12a70f1 --- /dev/null +++ b/Software/Visual_Studio/FSE/Tango.FSE.UI/Storage/ExplorerControlDialog.xaml.cs @@ -0,0 +1,218 @@ +using Microsoft.WindowsAPICodePack.Controls; +using Microsoft.WindowsAPICodePack.Shell; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +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.Shapes; + +namespace Tango.FSE.UI.Storage +{ + /// <summary> + /// Interaction logic for ExplorerControlWindow.xaml + /// </summary> + public partial class ExplorerControlDialog : Window + { + private static string _lastDirectory; + private static Point? _lastLocation; + + private string _currentLocation; + + public List<String> SelectedItems { get; set; } + public String InitialDirectory { get; set; } + + public ObservableCollection<ShellObject> ShellHistory + { + get { return (ObservableCollection<ShellObject>)GetValue(ShellHistoryProperty); } + set { SetValue(ShellHistoryProperty, value); } + } + public static readonly DependencyProperty ShellHistoryProperty = + DependencyProperty.Register("ShellHistory", typeof(ObservableCollection<ShellObject>), typeof(ExplorerControlDialog), new PropertyMetadata(null)); + + public ExplorerControlDialog() + { + SelectedItems = new List<string>(); + ShellHistory = new ObservableCollection<ShellObject>(); + + InitializeComponent(); + Loaded += ExplorerControlWindow_Loaded; + SourceInitialized += ExplorerControlWindow_SourceInitialized; + explorer.browser.SelectionChanged += Browser_SelectionChanged; + explorer.browser.NavigationComplete += Browser_NavigationComplete; + btnSelect.IsEnabled = false; + btnBack.IsEnabled = false; + btnForward.IsEnabled = false; + + if (_lastLocation != null) + { + WindowStartupLocation = WindowStartupLocation.Manual; + Left = _lastLocation.Value.X; + Top = _lastLocation.Value.Y; + } + else + { + WindowStartupLocation = WindowStartupLocation.CenterOwner; + } + } + + private void FillShellHistory(ShellObject current) + { + ShellHistory.Clear(); + + ShellObject parent = current; + + while (parent != null) + { + ShellHistory.Insert(0, parent); + + if (parent.ToString() == "This PC") break; + + parent = parent.Parent; + } + } + + private void Browser_NavigationComplete(object sender, Microsoft.WindowsAPICodePack.Controls.NavigationCompleteEventArgs e) + { + _currentLocation = e.NewLocation.ParsingName; + ShellObject shell = ShellObject.FromParsingName(_currentLocation); + + if (shell.ToString() != "This PC") + { + btnUp.IsEnabled = true; + txtLocation.Text = shell.GetDisplayName(DisplayNameType.FileSystemPath); + } + else + { + btnUp.IsEnabled = false; + txtLocation.Text = ""; + } + + FillShellHistory(shell); + + btnBack.IsEnabled = explorer.browser.NavigationLog.CanNavigateBackward; + btnForward.IsEnabled = explorer.browser.NavigationLog.CanNavigateForward; + } + + private void Browser_SelectionChanged(object sender, EventArgs e) + { + if (explorer.browser.SelectedItems.OfType<ShellObject>().ToList().Count > 0) + { + btnSelect.IsEnabled = true; + } + else + { + btnSelect.IsEnabled = false; + } + } + + private void ExplorerControlWindow_SourceInitialized(object sender, EventArgs e) + { + this.HideMinimizeAndMaximizeButtons(); + } + + private void ExplorerControlWindow_Loaded(object sender, RoutedEventArgs e) + { + ShellObject initialShellObject = (ShellObject)KnownFolders.Desktop; + + if (InitialDirectory != null && Directory.Exists(InitialDirectory)) + { + initialShellObject = ShellObject.FromParsingName(InitialDirectory); + } + else if (_lastDirectory != null && Directory.Exists(_lastDirectory)) + { + initialShellObject = ShellObject.FromParsingName(_lastDirectory); + } + + explorer.browser.Navigate(initialShellObject); + } + + private void BtnSelect_Click(object sender, RoutedEventArgs e) + { + SelectedItems.Clear(); + + foreach (var item in explorer.browser.SelectedItems.OfType<ShellObject>()) + { + SelectedItems.Add(item.ParsingName); + } + + _lastDirectory = _currentLocation; + _lastLocation = new Point(Left, Top); + + + DialogResult = true; + Close(); + } + + private void BtnCancel_Click(object sender, RoutedEventArgs e) + { + _lastLocation = new Point(Left, Top); + Close(); + } + + private void BtnBack_Click(object sender, RoutedEventArgs e) + { + explorer.browser.NavigateLogLocation(NavigationLogDirection.Backward); + } + + private void BtnForward_Click(object sender, RoutedEventArgs e) + { + explorer.browser.NavigateLogLocation(NavigationLogDirection.Forward); + } + + private void BtnUp_Click(object sender, RoutedEventArgs e) + { + ShellObject currentLocation = ShellObject.FromParsingName(_currentLocation); + explorer.browser.Navigate(currentLocation.Parent); + } + + private void TxtLocation_GotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e) + { + listHistory.Visibility = Visibility.Collapsed; + } + + private void TxtLocation_LostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e) + { + listHistory.Visibility = Visibility.Visible; + } + + private void OnHistoryItemClicked(object sender, RoutedEventArgs e) + { + ShellObject shell = (sender as Button).DataContext as ShellObject; + explorer.browser.Navigate(shell); + } + } + + internal static class WindowExtensions + { + // from winuser.h + private const int GWL_STYLE = -16, + WS_MAXIMIZEBOX = 0x10000, + WS_MINIMIZEBOX = 0x20000; + + [DllImport("user32.dll")] + extern private static int GetWindowLong(IntPtr hwnd, int index); + + [DllImport("user32.dll")] + extern private static int SetWindowLong(IntPtr hwnd, int index, int value); + + internal static void HideMinimizeAndMaximizeButtons(this Window window) + { + IntPtr hwnd = new System.Windows.Interop.WindowInteropHelper(window).Handle; + var currentStyle = GetWindowLong(hwnd, GWL_STYLE); + + SetWindowLong(hwnd, GWL_STYLE, (currentStyle & ~WS_MAXIMIZEBOX & ~WS_MINIMIZEBOX)); + } + } +} diff --git a/Software/Visual_Studio/FSE/Tango.FSE.UI/Storage/ExplorerControlForms.cs b/Software/Visual_Studio/FSE/Tango.FSE.UI/Storage/ExplorerControlForms.cs new file mode 100644 index 000000000..928baa201 --- /dev/null +++ b/Software/Visual_Studio/FSE/Tango.FSE.UI/Storage/ExplorerControlForms.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Data; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace Tango.FSE.Storage.UI +{ + public partial class ExplorerControlForms : UserControl + { + public ExplorerControlForms() + { + InitializeComponent(); + } + } +} diff --git a/Software/Visual_Studio/FSE/Tango.FSE.UI/Storage/ExplorerControlForms.designer.cs b/Software/Visual_Studio/FSE/Tango.FSE.UI/Storage/ExplorerControlForms.designer.cs new file mode 100644 index 000000000..f77fdd5cc --- /dev/null +++ b/Software/Visual_Studio/FSE/Tango.FSE.UI/Storage/ExplorerControlForms.designer.cs @@ -0,0 +1,109 @@ +namespace Tango.FSE.Storage.UI +{ + partial class ExplorerControlForms + { + /// <summary> + /// Required designer variable. + /// </summary> + private System.ComponentModel.IContainer components = null; + + /// <summary> + /// Clean up any resources being used. + /// </summary> + /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param> + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// <summary> + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// </summary> + private void InitializeComponent() + { + this.browser = new Microsoft.WindowsAPICodePack.Controls.WindowsForms.ExplorerBrowser(); + this.panel1 = new System.Windows.Forms.Panel(); + this.panel2 = new System.Windows.Forms.Panel(); + this.panel3 = new System.Windows.Forms.Panel(); + this.panel4 = new System.Windows.Forms.Panel(); + this.SuspendLayout(); + // + // browser + // + this.browser.Dock = System.Windows.Forms.DockStyle.Fill; + this.browser.Location = new System.Drawing.Point(0, 0); + this.browser.Name = "browser"; + this.browser.PropertyBagName = "Microsoft.WindowsAPICodePack.Controls.WindowsForms.ExplorerBrowser"; + this.browser.Size = new System.Drawing.Size(398, 311); + this.browser.TabIndex = 1; + // + // panel1 + // + this.panel1.BackColor = System.Drawing.Color.White; + this.panel1.Dock = System.Windows.Forms.DockStyle.Top; + this.panel1.Location = new System.Drawing.Point(0, 0); + this.panel1.Name = "panel1"; + this.panel1.Size = new System.Drawing.Size(398, 1); + this.panel1.TabIndex = 2; + // + // panel2 + // + this.panel2.BackColor = System.Drawing.Color.White; + this.panel2.Dock = System.Windows.Forms.DockStyle.Bottom; + this.panel2.Location = new System.Drawing.Point(0, 310); + this.panel2.Margin = new System.Windows.Forms.Padding(0); + this.panel2.Name = "panel2"; + this.panel2.Size = new System.Drawing.Size(398, 1); + this.panel2.TabIndex = 3; + // + // panel3 + // + this.panel3.BackColor = System.Drawing.Color.White; + this.panel3.Dock = System.Windows.Forms.DockStyle.Left; + this.panel3.Location = new System.Drawing.Point(0, 1); + this.panel3.Margin = new System.Windows.Forms.Padding(0); + this.panel3.Name = "panel3"; + this.panel3.Size = new System.Drawing.Size(1, 309); + this.panel3.TabIndex = 4; + // + // panel4 + // + this.panel4.BackColor = System.Drawing.Color.White; + this.panel4.Dock = System.Windows.Forms.DockStyle.Right; + this.panel4.Location = new System.Drawing.Point(397, 1); + this.panel4.Margin = new System.Windows.Forms.Padding(0); + this.panel4.Name = "panel4"; + this.panel4.Size = new System.Drawing.Size(1, 309); + this.panel4.TabIndex = 5; + // + // ExplorerControlForms + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.panel4); + this.Controls.Add(this.panel3); + this.Controls.Add(this.panel2); + this.Controls.Add(this.panel1); + this.Controls.Add(this.browser); + this.Name = "ExplorerControlForms"; + this.Size = new System.Drawing.Size(398, 311); + this.ResumeLayout(false); + + } + + #endregion + + public Microsoft.WindowsAPICodePack.Controls.WindowsForms.ExplorerBrowser browser; + private System.Windows.Forms.Panel panel1; + private System.Windows.Forms.Panel panel2; + private System.Windows.Forms.Panel panel3; + private System.Windows.Forms.Panel panel4; + } +} diff --git a/Software/Visual_Studio/FSE/Tango.FSE.UI/Storage/ExplorerControlForms.resx b/Software/Visual_Studio/FSE/Tango.FSE.UI/Storage/ExplorerControlForms.resx new file mode 100644 index 000000000..1af7de150 --- /dev/null +++ b/Software/Visual_Studio/FSE/Tango.FSE.UI/Storage/ExplorerControlForms.resx @@ -0,0 +1,120 @@ +<?xml version="1.0" encoding="utf-8"?> +<root> + <!-- + Microsoft ResX Schema + + Version 2.0 + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes + associated with the data types. + + Example: + + ... ado.net/XML headers & schema ... + <resheader name="resmimetype">text/microsoft-resx</resheader> + <resheader name="version">2.0</resheader> + <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> + <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> + <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> + <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> + <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> + <value>[base64 mime encoded serialized .NET Framework object]</value> + </data> + <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> + <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> + <comment>This is a comment</comment> + </data> + + There are any number of "resheader" rows that contain simple + name/value pairs. + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the + mimetype set. + + The mimetype is used for serialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not + extensible. For a given mimetype the value must be set accordingly: + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can + read any of the formats listed below. + + mimetype: application/x-microsoft.net.object.binary.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.soap.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Soap.SoapFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="metadata"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" /> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string" /> + <xsd:attribute name="type" type="xsd:string" /> + <xsd:attribute name="mimetype" type="xsd:string" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="assembly"> + <xsd:complexType> + <xsd:attribute name="alias" type="xsd:string" /> + <xsd:attribute name="name" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>2.0</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> +</root>
\ No newline at end of file diff --git a/Software/Visual_Studio/FSE/Tango.FSE.UI/Tango.FSE.UI.csproj b/Software/Visual_Studio/FSE/Tango.FSE.UI/Tango.FSE.UI.csproj index 12b4c9d45..b4854db5a 100644 --- a/Software/Visual_Studio/FSE/Tango.FSE.UI/Tango.FSE.UI.csproj +++ b/Software/Visual_Studio/FSE/Tango.FSE.UI/Tango.FSE.UI.csproj @@ -53,6 +53,9 @@ <Reference Include="Google.Protobuf, Version=3.4.1.0, Culture=neutral, PublicKeyToken=a7d26565bac4d604, processorArchitecture=MSIL"> <HintPath>..\..\packages\Google.Protobuf.3.4.1\lib\net45\Google.Protobuf.dll</HintPath> </Reference> + <Reference Include="Ionic.Zip, Version=1.9.1.8, Culture=neutral, PublicKeyToken=edbe51ad942a3f5c, processorArchitecture=MSIL"> + <HintPath>..\..\packages\Ionic.Zip.1.9.1.8\lib\Ionic.Zip.dll</HintPath> + </Reference> <Reference Include="MahApps.Metro, Version=1.6.5.1, Culture=neutral, processorArchitecture=MSIL"> <HintPath>..\..\packages\MahApps.Metro.1.6.5\lib\net46\MahApps.Metro.dll</HintPath> </Reference> @@ -110,6 +113,7 @@ <Reference Include="WindowsBase" /> <Reference Include="PresentationCore" /> <Reference Include="PresentationFramework" /> + <Reference Include="WindowsFormsIntegration" /> </ItemGroup> <ItemGroup> <ApplicationDefinition Include="App.xaml"> @@ -169,6 +173,15 @@ <Compile Include="RemoteDesktop\DefaultRemoteDesktopProvider.cs" /> <Compile Include="Resolution\DefaultResolutionService.cs" /> <Compile Include="Storage\DefaultStorageProvider.cs" /> + <Compile Include="Storage\ExplorerControlDialog.xaml.cs"> + <DependentUpon>ExplorerControlDialog.xaml</DependentUpon> + </Compile> + <Compile Include="Storage\ExplorerControlForms.cs"> + <SubType>UserControl</SubType> + </Compile> + <Compile Include="Storage\ExplorerControlForms.designer.cs"> + <DependentUpon>ExplorerControlForms.cs</DependentUpon> + </Compile> <Compile Include="SystemInfo\DefaultSystemInfoProvider.cs" /> <Compile Include="Threading\DefaultDispatcherProvider.cs" /> <Compile Include="ViewModelLocator.cs" /> @@ -248,6 +261,10 @@ <SubType>Designer</SubType> <Generator>MSBuild:Compile</Generator> </Page> + <Page Include="Storage\ExplorerControlDialog.xaml"> + <Generator>MSBuild:Compile</Generator> + <SubType>Designer</SubType> + </Page> <Page Include="Views\SettingsView.xaml"> <Generator>MSBuild:Compile</Generator> <SubType>Designer</SubType> @@ -299,6 +316,9 @@ <Generator>ResXFileCodeGenerator</Generator> <LastGenOutput>Resources.Designer.cs</LastGenOutput> </EmbeddedResource> + <EmbeddedResource Include="Storage\ExplorerControlForms.resx"> + <DependentUpon>ExplorerControlForms.cs</DependentUpon> + </EmbeddedResource> <None Include="packages.config" /> <None Include="Properties\Settings.settings"> <Generator>SettingsSingleFileGenerator</Generator> diff --git a/Software/Visual_Studio/FSE/Tango.FSE.UI/ViewModels/LayoutViewVM.cs b/Software/Visual_Studio/FSE/Tango.FSE.UI/ViewModels/LayoutViewVM.cs index 9ed73afb2..130839534 100644 --- a/Software/Visual_Studio/FSE/Tango.FSE.UI/ViewModels/LayoutViewVM.cs +++ b/Software/Visual_Studio/FSE/Tango.FSE.UI/ViewModels/LayoutViewVM.cs @@ -178,7 +178,7 @@ namespace Tango.FSE.UI.ViewModels private void DiagnosticsProvider_FrameReceived(object sender, Common.Diagnostics.DiagnosticsFrameReceivedEventArgs e) { - Debug.WriteLine("Diagnostics Received..."); + //Debug.WriteLine("Diagnostics Received..."); } private void Logout() diff --git a/Software/Visual_Studio/FSE/Tango.FSE.UI/Views/MainView.xaml b/Software/Visual_Studio/FSE/Tango.FSE.UI/Views/MainView.xaml index 6941b830a..0f16697ad 100644 --- a/Software/Visual_Studio/FSE/Tango.FSE.UI/Views/MainView.xaml +++ b/Software/Visual_Studio/FSE/Tango.FSE.UI/Views/MainView.xaml @@ -579,6 +579,10 @@ <Setter Property="Foreground" Value="{StaticResource FSE_InfoBrush}"></Setter> <Setter Property="Kind" Value="InfoOutline"></Setter> <Style.Triggers> + <DataTrigger Binding="{Binding Type}" Value="Question"> + <Setter Property="Foreground" Value="{StaticResource FSE_InfoBrush}"></Setter> + <Setter Property="Kind" Value="QuestionMarkCircleOutline"></Setter> + </DataTrigger> <DataTrigger Binding="{Binding Type}" Value="Warning"> <Setter Property="Foreground" Value="{StaticResource FSE_WarningBrush}"></Setter> <Setter Property="Kind" Value="AlertOutline"></Setter> diff --git a/Software/Visual_Studio/FSE/Tango.FSE.UI/packages.config b/Software/Visual_Studio/FSE/Tango.FSE.UI/packages.config index fda2f4d3f..a55bb6f29 100644 --- a/Software/Visual_Studio/FSE/Tango.FSE.UI/packages.config +++ b/Software/Visual_Studio/FSE/Tango.FSE.UI/packages.config @@ -4,6 +4,7 @@ <package id="Dragablz" version="0.0.3.203" targetFramework="net461" /> <package id="EntityFramework" version="6.2.0" targetFramework="net461" /> <package id="Google.Protobuf" version="3.4.1" targetFramework="net461" /> + <package id="Ionic.Zip" version="1.9.1.8" targetFramework="net461" /> <package id="MahApps.Metro" version="1.6.5" targetFramework="net461" /> <package id="MaterialDesignColors" version="1.2.2" targetFramework="net461" /> <package id="MaterialDesignThemes" version="3.0.1" targetFramework="net461" /> diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/Connection/DefaultMachineProvider.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/Connection/DefaultMachineProvider.cs index acdf20fa8..1c0c52196 100644 --- a/Software/Visual_Studio/PPC/Tango.PPC.Common/Connection/DefaultMachineProvider.cs +++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/Connection/DefaultMachineProvider.cs @@ -34,6 +34,7 @@ namespace Tango.PPC.Common.Connection private bool _isInitialized; private Thread _connection_thread; private ObservablesContext _context; + private bool disableConnectionFileLogging; private Machine _machine; /// <summary> @@ -106,11 +107,18 @@ namespace Tango.PPC.Common.Connection { if (MachineOperator.State != TransportComponentState.Connected) { + var fileLogger = LogManager.RegisteredLoggers.FirstOrDefault(x => x.GetType() == typeof(FileLogger)); + try { Thread.Sleep(2000); - LogManager.Log("Starting machine connection procedure...", LogCategory.Debug); + if (fileLogger != null && disableConnectionFileLogging) + { + fileLogger.Enabled = false; + } + + LogManager.Log("Starting machine connection procedure...", LogCategory.Info); var settings = SettingsManager.Default.GetOrCreate<PPCSettings>(); @@ -120,19 +128,19 @@ namespace Tango.PPC.Common.Connection { TimeSpan timeout = TimeSpan.FromSeconds(SettingsManager.Default.GetOrCreate<PPCSettings>().MachineScanningTimeoutSeconds); - LogManager.Log("Scanning for machine on available serial ports...", LogCategory.Debug); + LogManager.Log("Scanning for machine on available serial ports...", LogCategory.Info); Transport.Discovery.UsbCommunicationScanner<ConnectRequest, ConnectResponse> scanner = new Transport.Discovery.UsbCommunicationScanner<ConnectRequest, ConnectResponse>(UsbSerialBaudRates.BR_115200); var response = await scanner.Scan(new ConnectRequest() { Password = "1234" }, settings.EmbeddedDeviceHint, timeout); - LogManager.Log("Machine discovered on port: " + response.Adapter.Address, LogCategory.Debug); + LogManager.Log("Machine discovered on port: " + response.Adapter.Address, LogCategory.Info); LogManager.Log("Device Information:", LogCategory.Debug); - LogManager.Log(response.Response.DeviceInformation.ToJsonString(), LogCategory.Debug); + LogManager.Log(response.Response.DeviceInformation.ToJsonString(), LogCategory.Info); - LogManager.Log("Disconnecting machine operator...", LogCategory.Debug); + LogManager.Log("Disconnecting machine operator...", LogCategory.Info); await MachineOperator.Disconnect(); MachineOperator.Adapter = response.Adapter; MachineOperator.JobHandlingMode = JobHandlerModes.SettingUp; - LogManager.Log("Connecting machine operator...", LogCategory.Debug); + LogManager.Log("Connecting machine operator...", LogCategory.Info); try { await MachineOperator.Connect(); @@ -142,6 +150,8 @@ namespace Tango.PPC.Common.Connection settings.FirmwareVersion = MachineOperator.DeviceInformation.Version; settings.Save(); } + + disableConnectionFileLogging = false; } catch (Exception) { @@ -156,7 +166,7 @@ namespace Tango.PPC.Common.Connection } else { - LogManager.Log($"Connecting to machine on {settings.EmbeddedComPort}...", LogCategory.Debug); + LogManager.Log($"Connecting to machine on {settings.EmbeddedComPort}...", LogCategory.Info); UsbTransportAdapter adapter = new UsbTransportAdapter(settings.EmbeddedComPort, UsbSerialBaudRates.BR_115200); MachineOperator.Adapter = adapter; @@ -170,6 +180,8 @@ namespace Tango.PPC.Common.Connection settings.FirmwareVersion = MachineOperator.DeviceInformation.Version; settings.Save(); } + + disableConnectionFileLogging = false; } catch (Exception) { @@ -200,6 +212,8 @@ namespace Tango.PPC.Common.Connection LogManager.Log("Connecting machine operator..."); await MachineOperator.Connect(); + disableConnectionFileLogging = false; + if (MachineOperator.DeviceInformation != null) { settings.FirmwareVersion = MachineOperator.DeviceInformation.Version; @@ -212,7 +226,18 @@ namespace Tango.PPC.Common.Connection } catch (Exception ex) { - LogManager.Log(ex, LogCategory.Debug, "Error while trying to scan and connect to the machine."); + LogManager.Log(ex, "Error while trying to scan and connect to the machine."); + LogManager.Log("File logging of further connection attempts is now disabled and will resume when connection is successful."); + disableConnectionFileLogging = true; + } + finally + { + await Task.Delay(100); + + if (fileLogger != null) + { + fileLogger.Enabled = true; + } } } 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 edb004344..a7f77855a 100644 --- a/Software/Visual_Studio/PPC/Tango.PPC.Common/FileSystem/DefaultFileSystemService.cs +++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/FileSystem/DefaultFileSystemService.cs @@ -33,6 +33,7 @@ namespace Tango.PPC.Common.FileSystem public String Id { get; set; } public String Path { get; set; } public bool IsPathTempZip { get; set; } + public String UploadPostPath { get; set; } public FileSystemOperation(FileSystemOperationMode mode, String path) { @@ -84,6 +85,7 @@ namespace Tango.PPC.Common.FileSystem webRtcTransporter.ComponentName = "File System Passive WebRTC Transporter"; webRtcTransporter.UseKeepAlive = false; webRtcTransporter.RegisterRequestHandler<ChunkDownloadRequest>(WebRtcChunkDownloadRequestReceived); + webRtcTransporter.RegisterRequestHandler<ChunkUploadRequest>(WebRtcChunkUploadRequestReceived); await webRtcTransporter.Connect(); await receiver.SendGenericResponse(new InitWebRtcResponse(), token); _webRtcClients[receiver] = webRtcTransporter; @@ -99,6 +101,11 @@ namespace Tango.PPC.Common.FileSystem OnChunkDownloadRequest(request, token, transporter); } + private void WebRtcChunkUploadRequestReceived(ITransporter transporter, ChunkUploadRequest request, string token) + { + OnChunkUploadRequest(request, token, transporter); + } + [ExternalBridgeRequestHandlerMethod(typeof(GetFileSystemItemRequest))] public async void OnGetFileSystemItemRequest(GetFileSystemItemRequest request, String token, ExternalBridgeReceiver receiver) { @@ -118,9 +125,10 @@ namespace Tango.PPC.Common.FileSystem { try { - using (var stream = new FileStream(request.Path, FileMode.Create)) { } + var tempFile = TemporaryManager.CreateFile(); + using (var stream = new FileStream(tempFile, FileMode.Create)) { } - FileSystemOperation operation = new FileSystemOperation(FileSystemOperationMode.Upload, request.Path); + FileSystemOperation operation = new FileSystemOperation(FileSystemOperationMode.Upload, tempFile) { UploadPostPath = request.Path }; _operations.Add(operation.Id, operation); await receiver.SendGenericResponse(new FileUploadResponse() { OperationId = operation.Id }, token); @@ -131,6 +139,25 @@ namespace Tango.PPC.Common.FileSystem } } + [ExternalBridgeRequestHandlerMethod(typeof(FolderUploadRequest))] + public async void OnFolderUploadRequest(FolderUploadRequest request, String token, ExternalBridgeReceiver receiver) + { + try + { + var tempFile = TemporaryManager.CreateFile(); + using (var stream = new FileStream(tempFile, FileMode.Create)) { } + + FileSystemOperation operation = new FileSystemOperation(FileSystemOperationMode.Upload, tempFile) { UploadPostPath = request.Path, IsPathTempZip = true }; + _operations.Add(operation.Id, operation); + + await receiver.SendGenericResponse(new FolderUploadResponse() { OperationId = operation.Id }, token); + } + catch (Exception ex) + { + await receiver.SendErrorResponse(ex, token); + } + } + [ExternalBridgeRequestHandlerMethod(typeof(FileDownloadRequest))] public async void OnFileDownloadRequest(FileDownloadRequest request, String token, ExternalBridgeReceiver receiver) { @@ -191,7 +218,7 @@ namespace Tango.PPC.Common.FileSystem } [ExternalBridgeRequestHandlerMethod(typeof(ChunkUploadRequest))] - public async void OnChunkUploadRequest(ChunkUploadRequest request, String token, ExternalBridgeReceiver receiver) + public async void OnChunkUploadRequest(ChunkUploadRequest request, String token, ITransporter receiver) { try { @@ -209,6 +236,32 @@ namespace Tango.PPC.Common.FileSystem stream.Write(request.Data, 0, request.Data.Length); } + if (request.IsCompleted) + { + if (!operation.IsPathTempZip) + { + File.Copy(operation.Path, operation.UploadPostPath, true); + try + { + File.Delete(operation.Path); + } + catch { } + } + else + { + using (Ionic.Zip.ZipFile zip = new Ionic.Zip.ZipFile(operation.Path)) + { + zip.ExtractAll(operation.UploadPostPath, Ionic.Zip.ExtractExistingFileAction.OverwriteSilently); + } + + try + { + File.Delete(operation.Path); + } + catch { } + } + } + await receiver.SendGenericResponse(new ChunkUploadResponse(), token, new TransportResponseConfig() { Priority = QueuePriority.Low }); } catch (Exception ex) @@ -230,12 +283,19 @@ namespace Tango.PPC.Common.FileSystem } FileStream stream = null; + bool removeTempZipFile = false; try { stream = new FileStream(operation.Path, FileMode.Open); stream.Position = request.Position; byte[] data = new byte[Math.Min(request.MaxChunkSize, stream.Length - stream.Position)]; + + if (stream.Position + data.Length == stream.Length) + { + removeTempZipFile = true; + } + await stream.ReadAsync(data, 0, data.Length); stream.Dispose(); stream = null; @@ -249,6 +309,20 @@ namespace Tango.PPC.Common.FileSystem stream?.Dispose(); await receiver.SendErrorResponse(ex, token); } + finally + { + if (operation.IsPathTempZip && removeTempZipFile) + { + try + { + if (File.Exists(operation.Path)) + { + File.Delete(operation.Path); + } + } + catch { } + } + } } [ExternalBridgeRequestHandlerMethod(typeof(AbortOperationRequest))] @@ -271,10 +345,6 @@ namespace Tango.PPC.Common.FileSystem { File.Delete(operation.Path); } - else if (Directory.Exists(operation.Path)) - { - Directory.Delete(operation.Path, true); - } } else if (operation.IsPathTempZip) { @@ -348,6 +418,20 @@ namespace Tango.PPC.Common.FileSystem } } + [ExternalBridgeRequestHandlerMethod(typeof(PerformDiskSpaceOptimizationRequest))] + public async void OnPerformDiskSpaceOptimizationRequest(PerformDiskSpaceOptimizationRequest request, String token, ExternalBridgeReceiver receiver) + { + try + { + var deletedBytes = _manager.PerformDiskSpaceOptimization(); + await receiver.SendGenericResponse(new PerformDiskSpaceOptimizationResponse() { DeletedBytes = deletedBytes }, token); + } + catch (Exception ex) + { + await receiver.SendErrorResponse(ex, token); + } + } + public void OnReceiverDisconnected(ExternalBridgeReceiver receiver) { if (_webRtcClients.ContainsKey(receiver)) diff --git a/Software/Visual_Studio/PPC/Tango.PPC.UI/app.manifest b/Software/Visual_Studio/PPC/Tango.PPC.UI/app.manifest index d72e75011..efc5f8179 100644 --- a/Software/Visual_Studio/PPC/Tango.PPC.UI/app.manifest +++ b/Software/Visual_Studio/PPC/Tango.PPC.UI/app.manifest @@ -16,7 +16,7 @@ Remove this element if your application requires this virtualization for backwards compatibility. --> - <requestedExecutionLevel level="requireAdministrator" uiAccess="false" /> + <!--<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />--> </requestedPrivileges> </security> </trustInfo> diff --git a/Software/Visual_Studio/Tango.Core/Helpers/FileHelper.cs b/Software/Visual_Studio/Tango.Core/Helpers/FileHelper.cs index 0b65de64d..8ee0f4b8f 100644 --- a/Software/Visual_Studio/Tango.Core/Helpers/FileHelper.cs +++ b/Software/Visual_Studio/Tango.Core/Helpers/FileHelper.cs @@ -16,7 +16,7 @@ namespace Tango.Core.Helpers long bytes = Math.Abs(fileSize); int place = System.Convert.ToInt32(Math.Floor(Math.Log(bytes, 1024))); double num = Math.Round(bytes / Math.Pow(1024, place), 1); - return (Math.Sign(fileSize) * num).ToString() + suf[place]; + return (Math.Sign(fileSize) * num).ToString() + " " + suf[place]; } } } diff --git a/Software/Visual_Studio/Tango.FileSystem/FileExplorerControl.cs b/Software/Visual_Studio/Tango.FileSystem/FileExplorerControl.cs index 3660a18f0..0769b3576 100644 --- a/Software/Visual_Studio/Tango.FileSystem/FileExplorerControl.cs +++ b/Software/Visual_Studio/Tango.FileSystem/FileExplorerControl.cs @@ -233,6 +233,22 @@ namespace Tango.FileSystem public static readonly DependencyProperty RenameCommandInternalProperty = DependencyProperty.Register("RenameCommandInternal", typeof(ICommand), typeof(FileExplorerControl), new PropertyMetadata(null)); + public ICommand UploadCommandInternal + { + get { return (ICommand)GetValue(UploadCommandInternalProperty); } + set { SetValue(UploadCommandInternalProperty, value); } + } + public static readonly DependencyProperty UploadCommandInternalProperty = + DependencyProperty.Register("UploadCommandInternal", typeof(ICommand), typeof(FileExplorerControl), new PropertyMetadata(null)); + + public ICommand UploadCommand + { + get { return (ICommand)GetValue(UploadCommandProperty); } + set { SetValue(UploadCommandProperty, value); } + } + public static readonly DependencyProperty UploadCommandProperty = + DependencyProperty.Register("UploadCommand", typeof(ICommand), typeof(FileExplorerControl), new PropertyMetadata(null)); + public bool IsContextMenuOpened { get { return (bool)GetValue(IsContextMenuOpenedProperty); } @@ -395,6 +411,11 @@ namespace Tango.FileSystem return true; }); + + UploadCommandInternal = new RelayCommand(() => + { + UploadCommand?.Execute(null); + }); } private void OnIsContextMenuOpenedChanged() @@ -408,6 +429,7 @@ namespace Tango.FileSystem (DeleteCommandInternal as RelayCommand)?.RaiseCanExecuteChanged(); (RenameCommandInternal as RelayCommand)?.RaiseCanExecuteChanged(); (NewFolderCommandInternal as RelayCommand)?.RaiseCanExecuteChanged(); + (UploadCommandInternal as RelayCommand)?.RaiseCanExecuteChanged(); if (IsContextMenuOpened) { @@ -808,6 +830,7 @@ namespace Tango.FileSystem try { + Debug.WriteLine("Drag Started..."); var ef = DragDrop.DoDragDrop(this, new DataObject(DataFormats.FileDrop, files, false), DragDropEffects.Copy); } catch (Exception ex) @@ -816,6 +839,8 @@ namespace Tango.FileSystem Debugger.Break(); } + Debug.WriteLine("Drag Stopped..."); + await Task.Delay(3000); foreach (var watcher in watchers) @@ -838,6 +863,7 @@ namespace Tango.FileSystem { if (IsVisible) { + AllowDrop = true; await Task.Delay(100); this.Focus(); Keyboard.Focus(this); diff --git a/Software/Visual_Studio/Tango.FileSystem/FileSystemManager.cs b/Software/Visual_Studio/Tango.FileSystem/FileSystemManager.cs index 46ca080a2..c08304ca8 100644 --- a/Software/Visual_Studio/Tango.FileSystem/FileSystemManager.cs +++ b/Software/Visual_Studio/Tango.FileSystem/FileSystemManager.cs @@ -7,6 +7,7 @@ using System.Text.RegularExpressions; using System.Threading.Tasks; using Tango.Core.Helpers; using Tango.FileSystem.Network; +using Tango.Logging; namespace Tango.FileSystem { @@ -22,7 +23,7 @@ namespace Tango.FileSystem { Path = x.RootDirectory.FullName, DriveType = x.DriveType, - DriveLabel = x.Name, + DriveLabel = $"{x.VolumeLabel} ({x.Name.Replace("\\", "")})", Type = FileSystemItemType.Drive, }).Cast<FileSystemItemDTO>().ToList(); @@ -198,5 +199,61 @@ namespace Tango.FileSystem Path = fullPath }); } + + public long PerformDiskSpaceOptimization() + { + var tempDir = Path.GetTempPath(); + var logsFolder = FileLogger.DefaultLogsFolder; + + long sizeBefore = GetDirectorySize(new DirectoryInfo(tempDir)) + GetDirectorySize(new DirectoryInfo(logsFolder)); + + foreach (var file in Directory.GetFiles(tempDir, "*.*", SearchOption.AllDirectories)) + { + try + { + FileInfo fileInfo = new FileInfo(file); + if (fileInfo.LastWriteTime < DateTime.Now.AddDays(-1)) + { + File.Delete(file); + } + } + catch { } + } + + foreach (var file in Directory.GetFiles(logsFolder, "*.*", SearchOption.AllDirectories)) + { + try + { + FileInfo fileInfo = new FileInfo(file); + if (fileInfo.LastWriteTime < DateTime.Now.AddDays(-2)) + { + File.Delete(file); + } + } + catch { } + } + + long sizeAfter = GetDirectorySize(new DirectoryInfo(tempDir)) + GetDirectorySize(new DirectoryInfo(logsFolder)); + + return Math.Max(sizeBefore - sizeAfter, 0); + } + + public static long GetDirectorySize(DirectoryInfo d) + { + long size = 0; + // Add file sizes. + FileInfo[] fis = d.GetFiles(); + foreach (FileInfo fi in fis) + { + size += fi.Length; + } + // Add subdirectory sizes. + DirectoryInfo[] dis = d.GetDirectories(); + foreach (DirectoryInfo di in dis) + { + size += GetDirectorySize(di); + } + return size; + } } } diff --git a/Software/Visual_Studio/Tango.FileSystem/Network/ChunkDownloadRequest.cs b/Software/Visual_Studio/Tango.FileSystem/Network/ChunkDownloadRequest.cs index 16951930e..caedad88b 100644 --- a/Software/Visual_Studio/Tango.FileSystem/Network/ChunkDownloadRequest.cs +++ b/Software/Visual_Studio/Tango.FileSystem/Network/ChunkDownloadRequest.cs @@ -10,6 +10,6 @@ namespace Tango.FileSystem.Network { public String OperationId { get; set; } public long Position { get; set; } - public long MaxChunkSize { get; set; } = 1024 * 1024; + public long MaxChunkSize { get; set; } = 1024 * 10; } } diff --git a/Software/Visual_Studio/Tango.FileSystem/Network/ChunkUploadRequest.cs b/Software/Visual_Studio/Tango.FileSystem/Network/ChunkUploadRequest.cs index 98a27efd2..ed6b3e45f 100644 --- a/Software/Visual_Studio/Tango.FileSystem/Network/ChunkUploadRequest.cs +++ b/Software/Visual_Studio/Tango.FileSystem/Network/ChunkUploadRequest.cs @@ -10,5 +10,6 @@ namespace Tango.FileSystem.Network { public String OperationId { get; set; } public byte[] Data { get; set; } + public bool IsCompleted { get; set; } } } diff --git a/Software/Visual_Studio/Tango.FileSystem/Network/FolderUploadRequest.cs b/Software/Visual_Studio/Tango.FileSystem/Network/FolderUploadRequest.cs new file mode 100644 index 000000000..79a93eff3 --- /dev/null +++ b/Software/Visual_Studio/Tango.FileSystem/Network/FolderUploadRequest.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Tango.FileSystem.Network +{ + public class FolderUploadRequest + { + public String Path { get; set; } + } +} diff --git a/Software/Visual_Studio/Tango.FileSystem/Network/FolderUploadResponse.cs b/Software/Visual_Studio/Tango.FileSystem/Network/FolderUploadResponse.cs new file mode 100644 index 000000000..dc661dc95 --- /dev/null +++ b/Software/Visual_Studio/Tango.FileSystem/Network/FolderUploadResponse.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Tango.FileSystem.Network +{ + public class FolderUploadResponse + { + public String OperationId { get; set; } + } +} diff --git a/Software/Visual_Studio/Tango.FileSystem/Network/PerformDiskSpaceOptimizationRequest.cs b/Software/Visual_Studio/Tango.FileSystem/Network/PerformDiskSpaceOptimizationRequest.cs new file mode 100644 index 000000000..bd69bf9d8 --- /dev/null +++ b/Software/Visual_Studio/Tango.FileSystem/Network/PerformDiskSpaceOptimizationRequest.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Tango.FileSystem.Network +{ + public class PerformDiskSpaceOptimizationRequest + { + } +} diff --git a/Software/Visual_Studio/Tango.FileSystem/Network/PerformDiskSpaceOptimizationResponse.cs b/Software/Visual_Studio/Tango.FileSystem/Network/PerformDiskSpaceOptimizationResponse.cs new file mode 100644 index 000000000..73cbbf566 --- /dev/null +++ b/Software/Visual_Studio/Tango.FileSystem/Network/PerformDiskSpaceOptimizationResponse.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Tango.FileSystem.Network +{ + public class PerformDiskSpaceOptimizationResponse + { + public long DeletedBytes { get; set; } + } +} diff --git a/Software/Visual_Studio/Tango.FileSystem/Tango.FileSystem.csproj b/Software/Visual_Studio/Tango.FileSystem/Tango.FileSystem.csproj index a31af216c..d78419b7f 100644 --- a/Software/Visual_Studio/Tango.FileSystem/Tango.FileSystem.csproj +++ b/Software/Visual_Studio/Tango.FileSystem/Tango.FileSystem.csproj @@ -64,10 +64,14 @@ <Compile Include="Network\CreateFolderResponse.cs" /> <Compile Include="Network\DeleteRequest.cs" /> <Compile Include="Network\DeleteResponse.cs" /> + <Compile Include="Network\FolderUploadRequest.cs" /> + <Compile Include="Network\FolderUploadResponse.cs" /> <Compile Include="Network\InitWebRtcRequest.cs" /> <Compile Include="Network\InitWebRtcResponse.cs" /> <Compile Include="Network\MoveRequest.cs" /> <Compile Include="Network\MoveResponse.cs" /> + <Compile Include="Network\PerformDiskSpaceOptimizationRequest.cs" /> + <Compile Include="Network\PerformDiskSpaceOptimizationResponse.cs" /> <Compile Include="VirtualFileDataObject.cs" /> <Page Include="Themes\Generic.xaml"> <Generator>MSBuild:Compile</Generator> @@ -131,6 +135,10 @@ <Project>{a34ee0f0-649d-41c8-8489-b6f1cc6924ee}</Project> <Name>Tango.Core</Name> </ProjectReference> + <ProjectReference Include="..\Tango.Logging\Tango.Logging.csproj"> + <Project>{BC932DBD-7CDB-488C-99E4-F02CF441F55E}</Project> + <Name>Tango.Logging</Name> + </ProjectReference> <ProjectReference Include="..\Tango.SharedUI\Tango.SharedUI.csproj"> <Project>{8491d07b-c1f6-4b62-a412-41b9fd2d6538}</Project> <Name>Tango.SharedUI</Name> diff --git a/Software/Visual_Studio/Tango.Transport/Adapters/SignalRTransportAdapter.cs b/Software/Visual_Studio/Tango.Transport/Adapters/SignalRTransportAdapter.cs index def4cd741..de472d993 100644 --- a/Software/Visual_Studio/Tango.Transport/Adapters/SignalRTransportAdapter.cs +++ b/Software/Visual_Studio/Tango.Transport/Adapters/SignalRTransportAdapter.cs @@ -30,7 +30,7 @@ namespace Tango.Transport.Adapters public String Hub { get; set; } /// <summary> - /// Gets or sets the serial number of the remote machine (Use onlt for <see cref="SignalRTransportAdapterMode.CreateSession"/>) mode. + /// Gets or sets the serial number of the remote machine (Use only for <see cref="SignalRTransportAdapterMode.CreateSession"/>) mode. /// </summary> public String SerialNumber { get; set; } @@ -66,7 +66,7 @@ namespace Tango.Transport.Adapters public SignalRTransportAdapter() : base() { ConnectionTimeout = TimeSpan.FromSeconds(30); - WriteInterval = TimeSpan.FromMilliseconds(10); + WriteInterval = TimeSpan.FromMilliseconds(1); ComponentName = $"SignalR Adapter {_component_counter++}"; } @@ -161,9 +161,10 @@ namespace Tango.Transport.Adapters { if (!completed) { + completed = true; + LogManager.Log($"SignalR adapter session created ({SessionID})..."); LogManager.Log("SingalR adapter connected."); - completed = true; State = TransportComponentState.Connected; StartPushThread(); @@ -173,8 +174,12 @@ namespace Tango.Transport.Adapters } catch (Exception ex) { - LogManager.Log(ex, "Error occurred after session created."); - completionSource.SetException(ex); + if (!completed) + { + LogManager.Log(ex, "Error occurred after session created."); + completed = true; + completionSource.SetException(ex); + } } }); } @@ -211,8 +216,12 @@ namespace Tango.Transport.Adapters } catch (Exception ex) { - LogManager.Log(ex, "Error occurred on connection state changed event."); - completionSource.SetException(ex); + if (!completed) + { + completed = true; + LogManager.Log(ex, "Error occurred on connection state changed event."); + completionSource.SetException(ex); + } } }; diff --git a/Software/Visual_Studio/Tango.Transport/Adapters/UsbTransportAdapter.cs b/Software/Visual_Studio/Tango.Transport/Adapters/UsbTransportAdapter.cs index 485eda628..4785e11c8 100644 --- a/Software/Visual_Studio/Tango.Transport/Adapters/UsbTransportAdapter.cs +++ b/Software/Visual_Studio/Tango.Transport/Adapters/UsbTransportAdapter.cs @@ -290,7 +290,6 @@ namespace Tango.Transport.Adapters { try { - LogManager.Log($"Finalizing USB transport adapter ({Address})."); _serialPort.Close(); _serialPort.Dispose(); } diff --git a/Software/Visual_Studio/Tango.Transport/ITransportAdapter.cs b/Software/Visual_Studio/Tango.Transport/ITransportAdapter.cs index d77ad1ed4..1c397aae3 100644 --- a/Software/Visual_Studio/Tango.Transport/ITransportAdapter.cs +++ b/Software/Visual_Studio/Tango.Transport/ITransportAdapter.cs @@ -16,6 +16,11 @@ namespace Tango.Transport public interface ITransportAdapter : ITransportComponent, INotifyPropertyChanged { /// <summary> + /// Gets the last failed state exception/reason. + /// </summary> + Exception FailedStateException { get; } + + /// <summary> /// Gets the total bytes received. /// </summary> long TotalBytesReceived { get; } diff --git a/Software/Visual_Studio/Tango.Transport/TransportAdapterBase.cs b/Software/Visual_Studio/Tango.Transport/TransportAdapterBase.cs index bdcf0ee64..ebae5855a 100644 --- a/Software/Visual_Studio/Tango.Transport/TransportAdapterBase.cs +++ b/Software/Visual_Studio/Tango.Transport/TransportAdapterBase.cs @@ -87,6 +87,10 @@ namespace Tango.Transport set { _address = value; RaisePropertyChangedAuto(); } } + /// <summary> + /// Gets the last failed state exception/reason. + /// </summary> + public Exception FailedStateException { get; private set; } private TransportComponentState _state; /// <summary> @@ -115,6 +119,7 @@ namespace Tango.Transport /// <param name="ex">The ex.</param> protected virtual void OnFailed(Exception ex) { + FailedStateException = ex; LogManager.Log(ex, $"{ComponentName}: Adapter failed."); Disconnect().Wait(); State = TransportComponentState.Failed; diff --git a/Software/Visual_Studio/Tango.Transport/TransporterBase.cs b/Software/Visual_Studio/Tango.Transport/TransporterBase.cs index 916992bc3..5e076738c 100644 --- a/Software/Visual_Studio/Tango.Transport/TransporterBase.cs +++ b/Software/Visual_Studio/Tango.Transport/TransporterBase.cs @@ -241,7 +241,7 @@ namespace Tango.Transport { if (e == TransportComponentState.Failed && FailsWithAdapter) { - OnFailed(new CommunicationException("The adapter has failed. Going into a failed state...")); + OnFailed(new CommunicationException($"The adapter has failed with exception '{Adapter.FailedStateException.Message}' and the transporter is configured to fail with the adapter.")); } } @@ -1382,6 +1382,12 @@ namespace Tango.Transport message.SetResult(true, true); } } + catch (ThreadAbortException) + { + Exception requestException = FailedStateException != null ? FailedStateException : new TransporterDisconnectedException("The transporter push thread has been aborted."); + OnRequestFailed(message, requestException); + message.SetException(requestException); + } catch (Exception ex) { OnRequestFailed(message, ex); |
