From 96fe20a20e7c107473cefeda3b06950955952bec Mon Sep 17 00:00:00 2001 From: Roy Ben Shabat Date: Sun, 15 Mar 2020 01:55:42 +0200 Subject: Improved Console. Increased SignalR adapter connect timeout. Implemented Tango.FileSystem !!! --- .../Tango.FileSystem/FileSystemManager.cs | 89 ++++++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 Software/Visual_Studio/Tango.FileSystem/FileSystemManager.cs (limited to 'Software/Visual_Studio/Tango.FileSystem/FileSystemManager.cs') diff --git a/Software/Visual_Studio/Tango.FileSystem/FileSystemManager.cs b/Software/Visual_Studio/Tango.FileSystem/FileSystemManager.cs new file mode 100644 index 000000000..9a051793d --- /dev/null +++ b/Software/Visual_Studio/Tango.FileSystem/FileSystemManager.cs @@ -0,0 +1,89 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Tango.FileSystem.Network; + +namespace Tango.FileSystem +{ + public class FileSystemManager + { + public FileSystemItemDTO GetRoot() + { + FileSystemItemDTO folder = new FileSystemItemDTO(); + folder.Path = "This PC"; + folder.IsRoot = true; + folder.Type = FileSystemItemType.Folder; + folder.Items = DriveInfo.GetDrives().Select(x => new FileSystemItemDTO() + { + Path = x.RootDirectory.FullName, + DriveType = x.DriveType, + DriveLabel = x.Name, + Type = FileSystemItemType.Drive, + }).Cast().ToList(); + + return folder; + } + + public FileSystemItemDTO GetFolder(String path) + { + List items = new List(); + + if (String.IsNullOrWhiteSpace(path)) + { + return GetRoot(); + } + + if (!Directory.Exists(path)) + { + throw new DirectoryNotFoundException("The specified directory could not be located."); + } + + foreach (var directory in Directory.GetDirectories(path)) + { + items.Add(new FileSystemItemDTO() + { + Path = directory, + Type = FileSystemItemType.Folder, + DateModified = Directory.GetLastWriteTimeUtc(directory), + }); + } + + foreach (var file in Directory.GetFiles(path)) + { + items.Add(new FileSystemItemDTO() + { + Path = file, + Type = FileSystemItemType.File, + DateModified = File.GetLastWriteTimeUtc(file), + Size = new FileInfo(file).Length + }); + } + + return new FileSystemItemDTO() + { + Path = path, + Type = path.Length == 3 ? FileSystemItemType.Drive : FileSystemItemType.Folder, + Items = items + }; + } + + public void Delete(String path) + { + if (Directory.Exists(path)) + { + Directory.Delete(path); + } + else if (File.Exists(path)) + { + File.Delete(path); + } + else + { + throw new FileNotFoundException("Could not locate the specified file or directory."); + } + } + } +} -- cgit v1.3.1 From adabe4e1b99bc57f0381fb0a5bb3192ac0fdff18 Mon Sep 17 00:00:00 2001 From: Roy Ben Shabat Date: Mon, 16 Mar 2020 14:32:39 +0200 Subject: Working on FSE/PPC FileSystem Provider/Service. --- .../ViewModels/FileSystemViewVM.cs | 80 +++++++- .../Tango.FSE.PPCConsole/Views/FileSystemView.xaml | 73 +++++++- .../Controls/FileSystemControl.xaml | 34 ++-- .../FSE/Tango.FSE.Common/FSEViewModel.cs | 7 + .../FileSystem/FileSystemHandler.cs | 85 +++++++++ .../FileSystem/FileSystemHandlerStatus.cs | 18 ++ .../FileSystem/FileSystemHandlerType.cs | 17 ++ .../FileSystem/IFileSystemProvider.cs | 19 ++ .../FSE/Tango.FSE.Common/Tango.FSE.Common.csproj | 4 + .../FileSystem/DefaultFileSystemProvider.cs | 72 ++++++++ .../FSE/Tango.FSE.UI/Tango.FSE.UI.csproj | 5 + .../FSE/Tango.FSE.UI/ViewModelLocator.cs | 4 + .../FileSystem/DefaultFileSystemService.cs | 202 +++++++++++++++++++++ .../FileSystem/IFileSystemService.cs | 13 ++ .../PPC/Tango.PPC.Common/Tango.PPC.Common.csproj | 8 +- .../PPC/Tango.PPC.UI/ViewModelLocator.cs | 3 + .../Tango.FileSystem/FileSystemDataGrid.cs | 30 +++ .../Tango.FileSystem/FileSystemDataGridRow.cs | 40 ++++ .../Tango.FileSystem/FileSystemItem.cs | 5 +- .../Tango.FileSystem/FileSystemManager.cs | 23 ++- .../Network/AbortOperationRequest.cs | 13 ++ .../Network/AbortOperationResponse.cs | 12 ++ .../Network/ChunkDownloadRequest.cs | 15 ++ .../Network/ChunkDownloadResponse.cs | 13 ++ .../Tango.FileSystem/Network/ChunkUploadRequest.cs | 14 ++ .../Network/ChunkUploadResponse.cs | 13 ++ .../Network/FileDownloadRequest.cs | 13 ++ .../Network/FileDownloadResponse.cs | 14 ++ .../Tango.FileSystem/Network/FileUploadRequest.cs | 1 - .../Tango.FileSystem/Network/FileUploadResponse.cs | 2 +- .../Network/FolderDownloadRequest.cs | 13 ++ .../Network/FolderDownloadResponse.cs | 14 ++ .../Network/GetFileSystemItemRequest.cs | 2 + .../Network/StartFileDownloadRequest.cs | 13 -- .../Network/StartFileDownloadResponse.cs | 15 -- .../Network/StartFolderDownloadRequest.cs | 13 -- .../Network/StartFolderDownloadResponse.cs | 15 -- .../Tango.FileSystem/Tango.FileSystem.csproj | 16 +- .../Tango.FileSystem/Themes/Generic.xaml | 32 +--- 39 files changed, 850 insertions(+), 135 deletions(-) create mode 100644 Software/Visual_Studio/FSE/Tango.FSE.Common/FileSystem/FileSystemHandler.cs create mode 100644 Software/Visual_Studio/FSE/Tango.FSE.Common/FileSystem/FileSystemHandlerStatus.cs create mode 100644 Software/Visual_Studio/FSE/Tango.FSE.Common/FileSystem/FileSystemHandlerType.cs create mode 100644 Software/Visual_Studio/FSE/Tango.FSE.Common/FileSystem/IFileSystemProvider.cs create mode 100644 Software/Visual_Studio/FSE/Tango.FSE.UI/FileSystem/DefaultFileSystemProvider.cs create mode 100644 Software/Visual_Studio/PPC/Tango.PPC.Common/FileSystem/DefaultFileSystemService.cs create mode 100644 Software/Visual_Studio/PPC/Tango.PPC.Common/FileSystem/IFileSystemService.cs create mode 100644 Software/Visual_Studio/Tango.FileSystem/FileSystemDataGrid.cs create mode 100644 Software/Visual_Studio/Tango.FileSystem/FileSystemDataGridRow.cs create mode 100644 Software/Visual_Studio/Tango.FileSystem/Network/AbortOperationRequest.cs create mode 100644 Software/Visual_Studio/Tango.FileSystem/Network/AbortOperationResponse.cs create mode 100644 Software/Visual_Studio/Tango.FileSystem/Network/ChunkDownloadRequest.cs create mode 100644 Software/Visual_Studio/Tango.FileSystem/Network/ChunkDownloadResponse.cs create mode 100644 Software/Visual_Studio/Tango.FileSystem/Network/ChunkUploadRequest.cs create mode 100644 Software/Visual_Studio/Tango.FileSystem/Network/ChunkUploadResponse.cs create mode 100644 Software/Visual_Studio/Tango.FileSystem/Network/FileDownloadRequest.cs create mode 100644 Software/Visual_Studio/Tango.FileSystem/Network/FileDownloadResponse.cs create mode 100644 Software/Visual_Studio/Tango.FileSystem/Network/FolderDownloadRequest.cs create mode 100644 Software/Visual_Studio/Tango.FileSystem/Network/FolderDownloadResponse.cs delete mode 100644 Software/Visual_Studio/Tango.FileSystem/Network/StartFileDownloadRequest.cs delete mode 100644 Software/Visual_Studio/Tango.FileSystem/Network/StartFileDownloadResponse.cs delete mode 100644 Software/Visual_Studio/Tango.FileSystem/Network/StartFolderDownloadRequest.cs delete mode 100644 Software/Visual_Studio/Tango.FileSystem/Network/StartFolderDownloadResponse.cs (limited to 'Software/Visual_Studio/Tango.FileSystem/FileSystemManager.cs') 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 44bd03c39..f9eff7e6f 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 @@ -1,15 +1,19 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; +using System.Windows.Input; using Tango.Core.Commands; using Tango.FileSystem; using Tango.FSE.Common; +using Tango.FSE.Common.Connection; +using static Tango.SharedUI.Controls.NavigationControl; namespace Tango.FSE.PPCConsole.ViewModels { - public class FileSystemViewVM : FSEViewModel + public class FileSystemViewVM : FSEViewModel, INavigationViewModel { private FileSystemItem _currentItem; public FileSystemItem CurrentItem @@ -26,32 +30,92 @@ namespace Tango.FSE.PPCConsole.ViewModels } public RelayCommand NavigateCommand { get; set; } + public RelayCommand OpenItemCommand { get; set; } + public RelayCommand BackCommand { get; set; } public FileSystemViewVM() { - NavigateCommand = new RelayCommand(Navigate); + NavigateCommand = new RelayCommand(NavigateToCurrentPath); + OpenItemCommand = new RelayCommand(OpenFileSystemItem); + BackCommand = new RelayCommand(NavigateBack, () => !(CurrentItem is FolderItem) || !(CurrentItem as FolderItem).IsRoot); + } + + private async void NavigateBack() + { + if (CurrentItem.Path.Length == 3) + { + await Navigate(null); + } + else + { + String parent = Path.GetDirectoryName(CurrentItem.Path); + await Navigate(parent); + } + } + + public override void OnApplicationStarted() + { + base.OnApplicationStarted(); + MachineProvider.MachineConnected += MachineProvider_MachineConnected; } public override void OnApplicationReady() { base.OnApplicationReady(); + } + + private async void MachineProvider_MachineConnected(object sender, MachineConnectedEventArgs e) + { + await Navigate(null); + } + + private async void NavigateToCurrentPath() + { + await Navigate(CurrentPath); + } - FileSystemManager manager = new FileSystemManager(); - CurrentItem = FileSystemItem.FromDTO(manager.GetFolder(@"C:\")); + private async void OpenFileSystemItem(FileSystemItem item) + { + if (item != null) + { + await Navigate(item.Path); + } } - private void Navigate() + private async Task Navigate(String path) { - if (CurrentPath.IsNotNullOrEmpty()) + try + { + IsFree = false; + + Mouse.OverrideCursor = Cursors.AppStarting; + + if (path != null) + { + CurrentItem = await FileSystemProvider.GetFolder(path) as FileSystemItem; + } + else + { + CurrentItem = await FileSystemProvider.GetThisPC() as FileSystemItem; + } + } + catch (Exception ex) + { + IsFree = true; + Mouse.OverrideCursor = null; + await NotificationProvider.ShowError($"Error navigating to the specified path.\n{ex.FlattenMessage()}"); + } + finally { - FileSystemManager manager = new FileSystemManager(); - CurrentItem = FileSystemItem.FromDTO(manager.GetFolder(CurrentPath)); + IsFree = true; + Mouse.OverrideCursor = null; } } private void OnCurrentItemChanged() { CurrentPath = CurrentItem.Path; + InvalidateRelayCommands(); } } } 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 ad503c7de..fc23ca42c 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 @@ -10,7 +10,7 @@ xmlns:material="http://materialdesigninxaml.net/winfx/xaml/themes" mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800" d:DataContext="{d:DesignInstance Type=vm:FileSystemViewVM, IsDesignTimeCreatable=False}" DataContext="{x:Static global:ViewModelLocator.FileSystemViewVM}"> - + @@ -36,7 +36,7 @@ - @@ -45,17 +45,74 @@ - - - - - + + + + + + + + + + + + + + + + + + - + + + + + + + + + + Devices + + Local Disk 1 + Local Disk 2 + + + Computer + + Desktop + Downloads + Documents + Tango + + + + + + 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 ec490e893..7230d97fb 100644 --- a/Software/Visual_Studio/FSE/Tango.FSE.Common/Controls/FileSystemControl.xaml +++ b/Software/Visual_Studio/FSE/Tango.FSE.Common/Controls/FileSystemControl.xaml @@ -7,6 +7,14 @@ + + + + - + - + \ No newline at end of file diff --git a/Software/Visual_Studio/FSE/Tango.FSE.Common/FSEViewModel.cs b/Software/Visual_Studio/FSE/Tango.FSE.Common/FSEViewModel.cs index 997eca676..b3e832b58 100644 --- a/Software/Visual_Studio/FSE/Tango.FSE.Common/FSEViewModel.cs +++ b/Software/Visual_Studio/FSE/Tango.FSE.Common/FSEViewModel.cs @@ -17,6 +17,7 @@ using Tango.FSE.BL; using Tango.FSE.Common.Authentication; using Tango.FSE.Common.Connection; using Tango.FSE.Common.Diagnostics; +using Tango.FSE.Common.FileSystem; using Tango.FSE.Common.FSEApplication; using Tango.FSE.Common.Gateway; using Tango.FSE.Common.Logging; @@ -118,6 +119,12 @@ namespace Tango.FSE.Common [TangoInject] public IResolutionService ResolutionService { get; set; } + /// + /// Gets or sets the file system provider. + /// + [TangoInject] + public IFileSystemProvider FileSystemProvider { get; set; } + /// /// Gets or sets the FSE service. /// diff --git a/Software/Visual_Studio/FSE/Tango.FSE.Common/FileSystem/FileSystemHandler.cs b/Software/Visual_Studio/FSE/Tango.FSE.Common/FileSystem/FileSystemHandler.cs new file mode 100644 index 000000000..1b1e5f747 --- /dev/null +++ b/Software/Visual_Studio/FSE/Tango.FSE.Common/FileSystem/FileSystemHandler.cs @@ -0,0 +1,85 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Tango.Core; + +namespace Tango.FSE.Common.FileSystem +{ + public class FileSystemHandler : ExtendedObject + { + private Action _abortAction; + + public FileSystemHandlerType Type { get; set; } + + private FileSystemHandlerStatus _status; + public FileSystemHandlerStatus Status + { + get { return _status; } + set + { + if (_status != value) + { + _status = value; + RaisePropertyChangedAuto(); + } + } + } + + private double _position; + public double Position + { + get { return _position; } + set { _position = value; RaisePropertyChangedAuto(); } + } + + private double _length; + public double Length + { + get { return _length; } + set { _length = value; RaisePropertyChangedAuto(); } + } + + private Exception _failedException; + public Exception FailedException + { + get { return _failedException; } + set { _failedException = value; RaisePropertyChangedAuto(); } + } + + public String Name { get; set; } + public String Destination { get; set; } + + public FileSystemHandler(String name, String destination, Action abortAction) + { + Name = name; + Destination = destination; + _abortAction = abortAction; + } + + internal void InvalidateProgress(double position, double length) + { + Position = position; + Length = length; + Status = (Type == FileSystemHandlerType.FileDownload || Type == FileSystemHandlerType.FolderDownload) ? FileSystemHandlerStatus.Downloading : FileSystemHandlerStatus.Uploading; + } + + internal void RaiseFailed(Exception exception) + { + Status = FileSystemHandlerStatus.Failed; + FailedException = exception; + } + + internal void RaiseCompleted() + { + Status = FileSystemHandlerStatus.Completed; + } + + public void Abort() + { + Status = FileSystemHandlerStatus.Aborted; + _abortAction?.Invoke(); + } + } +} diff --git a/Software/Visual_Studio/FSE/Tango.FSE.Common/FileSystem/FileSystemHandlerStatus.cs b/Software/Visual_Studio/FSE/Tango.FSE.Common/FileSystem/FileSystemHandlerStatus.cs new file mode 100644 index 000000000..fe3a030a1 --- /dev/null +++ b/Software/Visual_Studio/FSE/Tango.FSE.Common/FileSystem/FileSystemHandlerStatus.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Tango.FSE.Common.FileSystem +{ + public enum FileSystemHandlerStatus + { + Pending, + Downloading, + Uploading, + Failed, + Aborted, + Completed + } +} diff --git a/Software/Visual_Studio/FSE/Tango.FSE.Common/FileSystem/FileSystemHandlerType.cs b/Software/Visual_Studio/FSE/Tango.FSE.Common/FileSystem/FileSystemHandlerType.cs new file mode 100644 index 000000000..4c60cb828 --- /dev/null +++ b/Software/Visual_Studio/FSE/Tango.FSE.Common/FileSystem/FileSystemHandlerType.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Tango.FSE.Common.FileSystem +{ + public enum FileSystemHandlerType + { + FileDownload, + FileUpload, + FolderDownload, + FolderUpload + } +} diff --git a/Software/Visual_Studio/FSE/Tango.FSE.Common/FileSystem/IFileSystemProvider.cs b/Software/Visual_Studio/FSE/Tango.FSE.Common/FileSystem/IFileSystemProvider.cs new file mode 100644 index 000000000..16099963b --- /dev/null +++ b/Software/Visual_Studio/FSE/Tango.FSE.Common/FileSystem/IFileSystemProvider.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Tango.FileSystem; +using static System.Environment; + +namespace Tango.FSE.Common.FileSystem +{ + public interface IFileSystemProvider + { + Task GetFolder(String path); + Task GetSpecialFolder(SpecialFolder specialFolder); + Task GetThisPC(); + Task Download(FileSystemItem item, String targetFolderPath); + Task Upload(String sourcePath, String targetPath); + } +} 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 f61b59da3..8690b742a 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 @@ -105,6 +105,10 @@ + + + + diff --git a/Software/Visual_Studio/FSE/Tango.FSE.UI/FileSystem/DefaultFileSystemProvider.cs b/Software/Visual_Studio/FSE/Tango.FSE.UI/FileSystem/DefaultFileSystemProvider.cs new file mode 100644 index 000000000..48da65f16 --- /dev/null +++ b/Software/Visual_Studio/FSE/Tango.FSE.UI/FileSystem/DefaultFileSystemProvider.cs @@ -0,0 +1,72 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Tango.FileSystem; +using Tango.FileSystem.Network; +using Tango.FSE.Common.Connection; +using Tango.FSE.Common.FileSystem; +using Tango.Transport; + +namespace Tango.FSE.UI.FileSystem +{ + public class DefaultFileSystemProvider : IFileSystemProvider + { + private IMachineProvider _machineProvider; + + public DefaultFileSystemProvider(IMachineProvider machineProvider) + { + _machineProvider = machineProvider; + } + + public async Task GetFolder(string path) + { + var response = await _machineProvider.MachineOperator.SendGenericRequest(new GetFileSystemItemRequest() + { + Path = path + }, new TransportRequestConfig() + { + Timeout = TimeSpan.FromSeconds(30), + }); + + return FileSystemItem.FromDTO(response.FileSystemItem) as IFileSystemContainer; + } + + public async Task GetSpecialFolder(Environment.SpecialFolder specialFolder) + { + var response = await _machineProvider.MachineOperator.SendGenericRequest(new GetFileSystemItemRequest() + { + SpecialFolder = specialFolder + }, new TransportRequestConfig() + { + Timeout = TimeSpan.FromSeconds(30), + }); + + return FileSystemItem.FromDTO(response.FileSystemItem) as IFileSystemContainer; + } + + public async Task GetThisPC() + { + var response = await _machineProvider.MachineOperator.SendGenericRequest(new GetFileSystemItemRequest() + { + //No parameters at all + }, new TransportRequestConfig() + { + Timeout = TimeSpan.FromSeconds(30), + }); + + return FileSystemItem.FromDTO(response.FileSystemItem) as IFileSystemContainer; + } + + public Task Download(FileSystemItem item, string targetFolderPath) + { + throw new NotImplementedException(); + } + + public Task Upload(string sourcePath, string targetPath) + { + throw new NotImplementedException(); + } + } +} 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 61f0f2e70..a1bdda00f 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 @@ -133,6 +133,7 @@ + @@ -324,6 +325,10 @@ {63561e19-ff5a-414b-a5ef-e30711543e1d} Tango.Emulations + + {c6ebbbbe-2123-44dc-aef7-a0d47d736ac0} + Tango.FileSystem + {4206AC58-3B57-4699-8835-90BF6DB01A61} Tango.Integration diff --git a/Software/Visual_Studio/FSE/Tango.FSE.UI/ViewModelLocator.cs b/Software/Visual_Studio/FSE/Tango.FSE.UI/ViewModelLocator.cs index f24a0cf99..6a3cf610a 100644 --- a/Software/Visual_Studio/FSE/Tango.FSE.UI/ViewModelLocator.cs +++ b/Software/Visual_Studio/FSE/Tango.FSE.UI/ViewModelLocator.cs @@ -10,6 +10,7 @@ using Tango.FSE.Common.Authentication; using Tango.FSE.Common.Connection; using Tango.FSE.Common.Console; using Tango.FSE.Common.Diagnostics; +using Tango.FSE.Common.FileSystem; using Tango.FSE.Common.FSEApplication; using Tango.FSE.Common.Gateway; using Tango.FSE.Common.Logging; @@ -26,6 +27,7 @@ using Tango.FSE.UI.Authentication; using Tango.FSE.UI.Connection; using Tango.FSE.UI.Console; using Tango.FSE.UI.Diagnostics; +using Tango.FSE.UI.FileSystem; using Tango.FSE.UI.FSEApplication; using Tango.FSE.UI.Gateway; using Tango.FSE.UI.Logging; @@ -61,6 +63,7 @@ namespace Tango.FSE.UI TangoIOC.Default.Unregister(); TangoIOC.Default.Unregister(); TangoIOC.Default.Unregister(); + TangoIOC.Default.Unregister(); //TangoIOC.Default.Unregister(); //TangoIOC.Default.Unregister(); //TangoIOC.Default.Unregister(); @@ -85,6 +88,7 @@ namespace Tango.FSE.UI TangoIOC.Default.Register(); TangoIOC.Default.Register(); TangoIOC.Default.Register(); + TangoIOC.Default.Register(); TangoIOC.Default.Register(); TangoIOC.Default.Register(); diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/FileSystem/DefaultFileSystemService.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/FileSystem/DefaultFileSystemService.cs new file mode 100644 index 000000000..ffbba2e7a --- /dev/null +++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/FileSystem/DefaultFileSystemService.cs @@ -0,0 +1,202 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.IO.Compression; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Tango.Core; +using Tango.Core.DI; +using Tango.FileSystem; +using Tango.FileSystem.Network; +using Tango.Integration.ExternalBridge; +using Tango.PPC.Common.ExternalBridge; +using Tango.Transport; + +namespace Tango.PPC.Common.FileSystem +{ + [TangoCreateWhenRegistered] + public class DefaultFileSystemService : ExtendedObject, IFileSystemService, IExternalBridgeRequestHandler + { + private enum FileSystemOperationMode + { + Upload, + Download + } + + private class FileSystemOperation + { + public FileSystemOperationMode Mode { get; set; } + public String Id { get; set; } + public String Path { get; set; } + + public FileSystemOperation(FileSystemOperationMode mode, String path) + { + Mode = mode; + Id = Guid.NewGuid().ToString(); + Path = path; + } + } + + private FileSystemManager _manager; + private Dictionary _operations; + + public bool Enabled { get; set; } = true; + + public DefaultFileSystemService(IPPCExternalBridgeService externalBridge) + { + _manager = new FileSystemManager(); + _operations = new Dictionary(); + externalBridge.RegisterRequestHandler(this); + } + + [ExternalBridgeRequestHandlerMethod(typeof(GetFileSystemItemRequest))] + public async void OnGetFileSystemItemRequest(GetFileSystemItemRequest request, String token, ExternalBridgeReceiver receiver) + { + try + { + FileSystemItemDTO dto = _manager.GetFolder(request); + await receiver.SendGenericResponse(new GetFileSystemItemResponse() { FileSystemItem = dto }, token); + } + catch (Exception ex) + { + await receiver.SendErrorResponse(ex, token); + } + } + + [ExternalBridgeRequestHandlerMethod(typeof(FileUploadRequest))] + public async void OnFileUploadRequest(FileUploadRequest request, String token, ExternalBridgeReceiver receiver) + { + try + { + using (var stream = new FileStream(request.Path, FileMode.Create)) { } + + FileSystemOperation operation = new FileSystemOperation(FileSystemOperationMode.Upload, request.Path); + _operations.Add(operation.Id, operation); + + await receiver.SendGenericResponse(new FileUploadResponse() { 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) + { + try + { + if (!File.Exists(request.Path)) + { + await receiver.SendErrorResponse(new FileNotFoundException("Could not find the specified file."), token); + return; + } + + FileSystemOperation operation = new FileSystemOperation(FileSystemOperationMode.Download, request.Path); + + _operations.Add(operation.Id, operation); + + await receiver.SendGenericResponse(new FileDownloadResponse() + { + OperationId = operation.Id, + Length = new FileInfo(request.Path).Length + }, token); + } + catch (Exception ex) + { + await receiver.SendErrorResponse(ex, token); + } + } + + [ExternalBridgeRequestHandlerMethod(typeof(ChunkUploadRequest))] + public async void OnChunkUploadRequest(ChunkUploadRequest request, String token, ExternalBridgeReceiver receiver) + { + try + { + FileSystemOperation operation; + _operations.TryGetValue(request.OperationId, out operation); + + if (operation == null) + { + await receiver.SendErrorResponse(new ArgumentException("Invalid operation id."), token); + return; + } + + using (var stream = new FileStream(operation.Path, FileMode.Append)) + { + stream.Write(request.Data, 0, request.Data.Length); + } + + await receiver.SendGenericResponse(new ChunkUploadResponse(), token); + } + catch (Exception ex) + { + await receiver.SendErrorResponse(ex, token); + } + } + + [ExternalBridgeRequestHandlerMethod(typeof(ChunkDownloadRequest))] + public async void OnChunkDownloadRequest(ChunkDownloadRequest request, String token, ExternalBridgeReceiver receiver) + { + FileSystemOperation operation; + _operations.TryGetValue(request.OperationId, out operation); + + if (operation == null) + { + await receiver.SendErrorResponse(new ArgumentException("Invalid operation id."), token); + return; + } + + using (FileStream stream = new FileStream(operation.Path, FileMode.Open)) + { + stream.Position = request.Position; + byte[] data = new byte[Math.Min(request.MaxChunkSize, stream.Length - stream.Position)]; + await stream.ReadAsync(data, 0, data.Length); + await receiver.SendGenericResponse(new ChunkDownloadResponse() + { + Data = data + }, token); + } + } + + [ExternalBridgeRequestHandlerMethod(typeof(AbortOperationRequest))] + public async void OnAbortOperationRequest(AbortOperationRequest request, String token, ExternalBridgeReceiver receiver) + { + FileSystemOperation operation; + _operations.TryGetValue(request.OperationId, out operation); + + if (operation == null) + { + await receiver.SendErrorResponse(new ArgumentException("Invalid operation id."), token); + return; + } + + try + { + if (operation.Mode == FileSystemOperationMode.Upload) + { + if (File.Exists(operation.Path)) + { + File.Delete(operation.Path); + } + else if (Directory.Exists(operation.Path)) + { + Directory.Delete(operation.Path, true); + } + } + + await receiver.SendGenericResponse(new AbortOperationResponse(), token); + } + catch (Exception ex) + { + await receiver.SendErrorResponse(ex, token); + } + } + + public void OnReceiverDisconnected(ExternalBridgeReceiver receiver) + { + + } + } +} diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/FileSystem/IFileSystemService.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/FileSystem/IFileSystemService.cs new file mode 100644 index 000000000..050bb1cd6 --- /dev/null +++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/FileSystem/IFileSystemService.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Tango.PPC.Common.FileSystem +{ + public interface IFileSystemService + { + bool Enabled { get; set; } + } +} diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/Tango.PPC.Common.csproj b/Software/Visual_Studio/PPC/Tango.PPC.Common/Tango.PPC.Common.csproj index 4551fe427..664e9e228 100644 --- a/Software/Visual_Studio/PPC/Tango.PPC.Common/Tango.PPC.Common.csproj +++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/Tango.PPC.Common.csproj @@ -152,6 +152,8 @@ + + @@ -368,6 +370,10 @@ {4399AF76-DB52-4CFB-8020-6F85BDB29FD5} Tango.Explorer + + {c6ebbbbe-2123-44dc-aef7-a0d47d736ac0} + Tango.FileSystem + {4206ac58-3b57-4699-8835-90bf6db01a61} Tango.Integration @@ -446,7 +452,7 @@ - + \ No newline at end of file diff --git a/Software/Visual_Studio/PPC/Tango.PPC.UI/ViewModelLocator.cs b/Software/Visual_Studio/PPC/Tango.PPC.UI/ViewModelLocator.cs index 9e150221d..edc9cb1cd 100644 --- a/Software/Visual_Studio/PPC/Tango.PPC.UI/ViewModelLocator.cs +++ b/Software/Visual_Studio/PPC/Tango.PPC.UI/ViewModelLocator.cs @@ -13,6 +13,7 @@ using Tango.PPC.Common.Console; using Tango.PPC.Common.Diagnostics; using Tango.PPC.Common.EventLogging; using Tango.PPC.Common.ExternalBridge; +using Tango.PPC.Common.FileSystem; using Tango.PPC.Common.HotSpot; using Tango.PPC.Common.MachineSetup; using Tango.PPC.Common.MachineUpdate; @@ -86,6 +87,7 @@ namespace Tango.PPC.UI TangoIOC.Default.Unregister(); TangoIOC.Default.Unregister(); TangoIOC.Default.Unregister(); + TangoIOC.Default.Unregister(); if (App.StartupArgs.Contains("-webDebug")) { @@ -123,6 +125,7 @@ namespace Tango.PPC.UI TangoIOC.Default.Register(); TangoIOC.Default.Register(); TangoIOC.Default.Register(); + TangoIOC.Default.Register(); TangoIOC.Default.Register(); TangoIOC.Default.Register(); diff --git a/Software/Visual_Studio/Tango.FileSystem/FileSystemDataGrid.cs b/Software/Visual_Studio/Tango.FileSystem/FileSystemDataGrid.cs new file mode 100644 index 000000000..c1c31cf95 --- /dev/null +++ b/Software/Visual_Studio/Tango.FileSystem/FileSystemDataGrid.cs @@ -0,0 +1,30 @@ +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.FileSystem +{ + public class FileSystemDataGrid : DataGrid + { + static FileSystemDataGrid() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(FileSystemDataGrid), new FrameworkPropertyMetadata(typeof(FileSystemDataGrid))); + } + + protected override DependencyObject GetContainerForItemOverride() + { + return new FileSystemDataGridRow(); + } + } +} diff --git a/Software/Visual_Studio/Tango.FileSystem/FileSystemDataGridRow.cs b/Software/Visual_Studio/Tango.FileSystem/FileSystemDataGridRow.cs new file mode 100644 index 000000000..4cc67f888 --- /dev/null +++ b/Software/Visual_Studio/Tango.FileSystem/FileSystemDataGridRow.cs @@ -0,0 +1,40 @@ +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.FileSystem +{ + public class FileSystemDataGridRow : DataGridRow + { + public ICommand DoubleClickCommand + { + get { return (ICommand)GetValue(DoubleClickCommandProperty); } + set { SetValue(DoubleClickCommandProperty, value); } + } + public static readonly DependencyProperty DoubleClickCommandProperty = + DependencyProperty.Register("DoubleClickCommand", typeof(ICommand), typeof(FileSystemDataGridRow), new PropertyMetadata(null)); + + protected override void OnPreviewMouseDoubleClick(MouseButtonEventArgs e) + { + base.OnPreviewMouseDoubleClick(e); + + DoubleClickCommand?.Execute(DataContext); + } + + static FileSystemDataGridRow() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(FileSystemDataGridRow), new FrameworkPropertyMetadata(typeof(FileSystemDataGridRow))); + } + } +} diff --git a/Software/Visual_Studio/Tango.FileSystem/FileSystemItem.cs b/Software/Visual_Studio/Tango.FileSystem/FileSystemItem.cs index 6f8190f6c..536409f63 100644 --- a/Software/Visual_Studio/Tango.FileSystem/FileSystemItem.cs +++ b/Software/Visual_Studio/Tango.FileSystem/FileSystemItem.cs @@ -52,14 +52,15 @@ namespace Tango.FileSystem { DriveType = dto.DriveType, Label = dto.DriveLabel, - Items = dto.Items.Select(x => FromDTO(x)).ToObservableCollection() + Items = dto.Items?.Select(x => FromDTO(x)).ToObservableCollection() }; } else if (dto.Type == FileSystemItemType.Folder) { item = new FolderItem() { - Items = dto.Items.Select(x => FromDTO(x)).ToObservableCollection() + Items = dto.Items?.Select(x => FromDTO(x)).ToObservableCollection(), + IsRoot = dto.IsRoot, }; } else if (dto.Type == FileSystemItemType.File) diff --git a/Software/Visual_Studio/Tango.FileSystem/FileSystemManager.cs b/Software/Visual_Studio/Tango.FileSystem/FileSystemManager.cs index 9a051793d..f0b86becf 100644 --- a/Software/Visual_Studio/Tango.FileSystem/FileSystemManager.cs +++ b/Software/Visual_Studio/Tango.FileSystem/FileSystemManager.cs @@ -16,7 +16,7 @@ namespace Tango.FileSystem folder.Path = "This PC"; folder.IsRoot = true; folder.Type = FileSystemItemType.Folder; - folder.Items = DriveInfo.GetDrives().Select(x => new FileSystemItemDTO() + folder.Items = DriveInfo.GetDrives().Where(x => x.DriveType == DriveType.Fixed || x.DriveType == DriveType.Removable || x.DriveType == DriveType.Network).Select(x => new FileSystemItemDTO() { Path = x.RootDirectory.FullName, DriveType = x.DriveType, @@ -27,21 +27,26 @@ namespace Tango.FileSystem return folder; } - public FileSystemItemDTO GetFolder(String path) + public FileSystemItemDTO GetFolder(GetFileSystemItemRequest request) { List items = new List(); - if (String.IsNullOrWhiteSpace(path)) + if (request.SpecialFolder.HasValue) + { + request.Path = Environment.GetFolderPath(request.SpecialFolder.Value); + } + + if (String.IsNullOrWhiteSpace(request.Path)) { return GetRoot(); } - if (!Directory.Exists(path)) + if (!Directory.Exists(request.Path)) { throw new DirectoryNotFoundException("The specified directory could not be located."); } - foreach (var directory in Directory.GetDirectories(path)) + foreach (var directory in Directory.GetDirectories(request.Path)) { items.Add(new FileSystemItemDTO() { @@ -51,7 +56,7 @@ namespace Tango.FileSystem }); } - foreach (var file in Directory.GetFiles(path)) + foreach (var file in Directory.GetFiles(request.Path)) { items.Add(new FileSystemItemDTO() { @@ -64,8 +69,8 @@ namespace Tango.FileSystem return new FileSystemItemDTO() { - Path = path, - Type = path.Length == 3 ? FileSystemItemType.Drive : FileSystemItemType.Folder, + Path = request.Path, + Type = request.Path.Length == 3 ? FileSystemItemType.Drive : FileSystemItemType.Folder, Items = items }; } @@ -74,7 +79,7 @@ namespace Tango.FileSystem { if (Directory.Exists(path)) { - Directory.Delete(path); + Directory.Delete(path, true); } else if (File.Exists(path)) { diff --git a/Software/Visual_Studio/Tango.FileSystem/Network/AbortOperationRequest.cs b/Software/Visual_Studio/Tango.FileSystem/Network/AbortOperationRequest.cs new file mode 100644 index 000000000..3e912b9f0 --- /dev/null +++ b/Software/Visual_Studio/Tango.FileSystem/Network/AbortOperationRequest.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 AbortOperationRequest + { + public String OperationId { get; set; } + } +} diff --git a/Software/Visual_Studio/Tango.FileSystem/Network/AbortOperationResponse.cs b/Software/Visual_Studio/Tango.FileSystem/Network/AbortOperationResponse.cs new file mode 100644 index 000000000..c5669ad48 --- /dev/null +++ b/Software/Visual_Studio/Tango.FileSystem/Network/AbortOperationResponse.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 AbortOperationResponse + { + } +} diff --git a/Software/Visual_Studio/Tango.FileSystem/Network/ChunkDownloadRequest.cs b/Software/Visual_Studio/Tango.FileSystem/Network/ChunkDownloadRequest.cs new file mode 100644 index 000000000..16951930e --- /dev/null +++ b/Software/Visual_Studio/Tango.FileSystem/Network/ChunkDownloadRequest.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Tango.FileSystem.Network +{ + public class ChunkDownloadRequest + { + public String OperationId { get; set; } + public long Position { get; set; } + public long MaxChunkSize { get; set; } = 1024 * 1024; + } +} diff --git a/Software/Visual_Studio/Tango.FileSystem/Network/ChunkDownloadResponse.cs b/Software/Visual_Studio/Tango.FileSystem/Network/ChunkDownloadResponse.cs new file mode 100644 index 000000000..df72e193f --- /dev/null +++ b/Software/Visual_Studio/Tango.FileSystem/Network/ChunkDownloadResponse.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 ChunkDownloadResponse + { + public byte[] Data { get; set; } + } +} diff --git a/Software/Visual_Studio/Tango.FileSystem/Network/ChunkUploadRequest.cs b/Software/Visual_Studio/Tango.FileSystem/Network/ChunkUploadRequest.cs new file mode 100644 index 000000000..98a27efd2 --- /dev/null +++ b/Software/Visual_Studio/Tango.FileSystem/Network/ChunkUploadRequest.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Tango.FileSystem.Network +{ + public class ChunkUploadRequest + { + public String OperationId { get; set; } + public byte[] Data { get; set; } + } +} diff --git a/Software/Visual_Studio/Tango.FileSystem/Network/ChunkUploadResponse.cs b/Software/Visual_Studio/Tango.FileSystem/Network/ChunkUploadResponse.cs new file mode 100644 index 000000000..5cd6a3f80 --- /dev/null +++ b/Software/Visual_Studio/Tango.FileSystem/Network/ChunkUploadResponse.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 ChunkUploadResponse + { + + } +} diff --git a/Software/Visual_Studio/Tango.FileSystem/Network/FileDownloadRequest.cs b/Software/Visual_Studio/Tango.FileSystem/Network/FileDownloadRequest.cs new file mode 100644 index 000000000..b63937f8b --- /dev/null +++ b/Software/Visual_Studio/Tango.FileSystem/Network/FileDownloadRequest.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 FileDownloadRequest + { + public String Path { get; set; } + } +} diff --git a/Software/Visual_Studio/Tango.FileSystem/Network/FileDownloadResponse.cs b/Software/Visual_Studio/Tango.FileSystem/Network/FileDownloadResponse.cs new file mode 100644 index 000000000..c57a9bad6 --- /dev/null +++ b/Software/Visual_Studio/Tango.FileSystem/Network/FileDownloadResponse.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Tango.FileSystem.Network +{ + public class FileDownloadResponse + { + public long Length { get; set; } + public String OperationId { get; set; } + } +} diff --git a/Software/Visual_Studio/Tango.FileSystem/Network/FileUploadRequest.cs b/Software/Visual_Studio/Tango.FileSystem/Network/FileUploadRequest.cs index 50c35c584..f3cbc2d54 100644 --- a/Software/Visual_Studio/Tango.FileSystem/Network/FileUploadRequest.cs +++ b/Software/Visual_Studio/Tango.FileSystem/Network/FileUploadRequest.cs @@ -9,6 +9,5 @@ namespace Tango.FileSystem.Network public class FileUploadRequest { public String Path { get; set; } - public byte[] Data { get; set; } } } diff --git a/Software/Visual_Studio/Tango.FileSystem/Network/FileUploadResponse.cs b/Software/Visual_Studio/Tango.FileSystem/Network/FileUploadResponse.cs index 4f4bc0d52..c0af1a797 100644 --- a/Software/Visual_Studio/Tango.FileSystem/Network/FileUploadResponse.cs +++ b/Software/Visual_Studio/Tango.FileSystem/Network/FileUploadResponse.cs @@ -8,6 +8,6 @@ namespace Tango.FileSystem.Network { public class FileUploadResponse { - + public String OperationId { get; set; } } } diff --git a/Software/Visual_Studio/Tango.FileSystem/Network/FolderDownloadRequest.cs b/Software/Visual_Studio/Tango.FileSystem/Network/FolderDownloadRequest.cs new file mode 100644 index 000000000..cb65a8942 --- /dev/null +++ b/Software/Visual_Studio/Tango.FileSystem/Network/FolderDownloadRequest.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 FolderDownloadRequest + { + public String Path { get; set; } + } +} diff --git a/Software/Visual_Studio/Tango.FileSystem/Network/FolderDownloadResponse.cs b/Software/Visual_Studio/Tango.FileSystem/Network/FolderDownloadResponse.cs new file mode 100644 index 000000000..239ff2e59 --- /dev/null +++ b/Software/Visual_Studio/Tango.FileSystem/Network/FolderDownloadResponse.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Tango.FileSystem.Network +{ + public class FolderDownloadResponse + { + public long Length { get; set; } + public String OperationId { get; set; } + } +} diff --git a/Software/Visual_Studio/Tango.FileSystem/Network/GetFileSystemItemRequest.cs b/Software/Visual_Studio/Tango.FileSystem/Network/GetFileSystemItemRequest.cs index f69c7bd98..1ed6c19e4 100644 --- a/Software/Visual_Studio/Tango.FileSystem/Network/GetFileSystemItemRequest.cs +++ b/Software/Visual_Studio/Tango.FileSystem/Network/GetFileSystemItemRequest.cs @@ -3,11 +3,13 @@ using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; +using static System.Environment; namespace Tango.FileSystem.Network { public class GetFileSystemItemRequest { public String Path { get; set; } + public SpecialFolder? SpecialFolder { get; set; } } } diff --git a/Software/Visual_Studio/Tango.FileSystem/Network/StartFileDownloadRequest.cs b/Software/Visual_Studio/Tango.FileSystem/Network/StartFileDownloadRequest.cs deleted file mode 100644 index aac03af38..000000000 --- a/Software/Visual_Studio/Tango.FileSystem/Network/StartFileDownloadRequest.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Tango.FileSystem.Network -{ - public class StartFileDownloadRequest - { - public String Path { get; set; } - } -} diff --git a/Software/Visual_Studio/Tango.FileSystem/Network/StartFileDownloadResponse.cs b/Software/Visual_Studio/Tango.FileSystem/Network/StartFileDownloadResponse.cs deleted file mode 100644 index 187d15254..000000000 --- a/Software/Visual_Studio/Tango.FileSystem/Network/StartFileDownloadResponse.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Tango.FileSystem.Network -{ - public class StartFileDownloadResponse - { - public byte[] Data { get; set; } - public long Position { get; set; } - public long Length { get; set; } - } -} diff --git a/Software/Visual_Studio/Tango.FileSystem/Network/StartFolderDownloadRequest.cs b/Software/Visual_Studio/Tango.FileSystem/Network/StartFolderDownloadRequest.cs deleted file mode 100644 index e7989bd98..000000000 --- a/Software/Visual_Studio/Tango.FileSystem/Network/StartFolderDownloadRequest.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Tango.FileSystem.Network -{ - public class StartFolderDownloadRequest - { - public String Path { get; set; } - } -} diff --git a/Software/Visual_Studio/Tango.FileSystem/Network/StartFolderDownloadResponse.cs b/Software/Visual_Studio/Tango.FileSystem/Network/StartFolderDownloadResponse.cs deleted file mode 100644 index c48d4c04c..000000000 --- a/Software/Visual_Studio/Tango.FileSystem/Network/StartFolderDownloadResponse.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Tango.FileSystem.Network -{ - public class StartFolderDownloadResponse - { - public byte[] ZipData { get; set; } - public long Position { get; set; } - public long Length { get; set; } - } -} diff --git a/Software/Visual_Studio/Tango.FileSystem/Tango.FileSystem.csproj b/Software/Visual_Studio/Tango.FileSystem/Tango.FileSystem.csproj index 90dfd78d2..733493f02 100644 --- a/Software/Visual_Studio/Tango.FileSystem/Tango.FileSystem.csproj +++ b/Software/Visual_Studio/Tango.FileSystem/Tango.FileSystem.csproj @@ -50,6 +50,14 @@ + + + + + + + + MSBuild:Compile @@ -73,10 +81,10 @@ - - - - + + + + Code diff --git a/Software/Visual_Studio/Tango.FileSystem/Themes/Generic.xaml b/Software/Visual_Studio/Tango.FileSystem/Themes/Generic.xaml index c9e561e29..f793be947 100644 --- a/Software/Visual_Studio/Tango.FileSystem/Themes/Generic.xaml +++ b/Software/Visual_Studio/Tango.FileSystem/Themes/Generic.xaml @@ -7,6 +7,14 @@ + + + + - + - + + + + + + + + + + + + + - + - + @@ -94,20 +96,24 @@ Devices - Local Disk 1 - Local Disk 2 + + + + + + + Computer - Desktop - Downloads - Documents - Tango + Desktop + Documents + Application Data - + - + diff --git a/Software/Visual_Studio/Tango.FileSystem/FileSystemManager.cs b/Software/Visual_Studio/Tango.FileSystem/FileSystemManager.cs index f0b86becf..44c8f1901 100644 --- a/Software/Visual_Studio/Tango.FileSystem/FileSystemManager.cs +++ b/Software/Visual_Studio/Tango.FileSystem/FileSystemManager.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; +using System.Text.RegularExpressions; using System.Threading.Tasks; using Tango.FileSystem.Network; @@ -41,6 +42,21 @@ namespace Tango.FileSystem return GetRoot(); } + try + { + if (request.Path.Count(x => x == '%') == 2) + { + var variable = Regex.Match(request.Path, "(?<=%)(.*?)(?=%)").Value; + request.Path = request.Path.Replace($"%{variable}%", Environment.ExpandEnvironmentVariables($"%{variable}%")); + } + } + catch + { + throw new ArgumentException("Could not parse environment variable."); + } + + request.Path = Path.GetFullPath(request.Path); + if (!Directory.Exists(request.Path)) { throw new DirectoryNotFoundException("The specified directory could not be located."); -- cgit v1.3.1 From d48b2d23515d06a21ad241380986bf8f31773195 Mon Sep 17 00:00:00 2001 From: Roy Ben Shabat Date: Sun, 22 Mar 2020 00:04:44 +0200 Subject: Implemented WebRtcTransportAdapter. Implemented FileSystem via WebRTC. Improved FileSystemControl keyboard control. Implemented FileSystemControl context menu. Improved Transported custom request handler registration. Implemented FS copy/move/delete. Implemented InputBox. --- .../ViewModels/FileSystemViewVM.cs | 146 +++++++- .../ViewModels/RemoteDesktopViewVM.cs | 6 + .../Tango.FSE.PPCConsole/Views/FileSystemView.xaml | 61 ++-- .../Connection/MachineConnectedEventArgs.cs | 1 + .../Controls/FileSystemControl.xaml | 72 +++- .../FileSystem/FileSystemHandler.cs | 28 ++ .../FileSystem/IFileSystemProvider.cs | 6 + .../Notifications/INotificationProvider.cs | 26 +- .../Notifications/InputBoxResult.cs | 14 + .../Tango.FSE.Common/Notifications/InputBoxVM.cs | 34 ++ .../FSE/Tango.FSE.Common/Tango.FSE.Common.csproj | 2 + .../Connection/DefaultMachineProvider.cs | 5 + .../FileSystem/DefaultFileSystemProvider.cs | 195 ++++++++++- .../Navigation/DefaultNavigationManager.cs | 13 +- .../Notifications/DefaultNotificationProvider.cs | 65 ++++ .../RemoteDesktop/DefaultRemoteDesktopProvider.cs | 2 +- .../SystemInfo/DefaultSystemInfoProvider.cs | 2 +- .../FSE/Tango.FSE.UI/ViewModels/LayoutViewVM.cs | 6 + .../FSE/Tango.FSE.UI/Views/LayoutView.xaml | 2 +- .../FSE/Tango.FSE.UI/Views/MainView.xaml | 71 ++++ .../FSE/Tango.FSE.UI/Views/MainView.xaml.cs | 45 +++ .../FileSystem/DefaultFileSystemService.cs | 117 ++++++- .../FileSystem/IFileSystemService.cs | 1 + .../SideChains/WebRtc.NET/src/conductor.cc | 2 +- .../Tango.FileSystem/FileExplorerControl.cs | 380 ++++++++++++++++++++- .../Tango.FileSystem/FileSystemItem.cs | 10 +- .../Tango.FileSystem/FileSystemManager.cs | 46 +++ .../Tango.FileSystem/Network/CopyRequest.cs | 14 + .../Tango.FileSystem/Network/CopyResponse.cs | 12 + .../Tango.FileSystem/Network/DeleteRequest.cs | 13 + .../Tango.FileSystem/Network/DeleteResponse.cs | 12 + .../Tango.FileSystem/Network/InitWebRtcRequest.cs | 13 + .../Tango.FileSystem/Network/InitWebRtcResponse.cs | 12 + .../Tango.FileSystem/Network/MoveRequest.cs | 14 + .../Tango.FileSystem/Network/MoveResponse.cs | 12 + .../Tango.FileSystem/Tango.FileSystem.csproj | 8 + .../Tango.FileSystem/Themes/Generic.xaml | 17 +- .../Tango.SharedUI/Controls/NavigationControl.cs | 6 +- .../Visual_Studio/Tango.SystemInfo/WMIReader.cs | 8 +- .../Visual_Studio/Tango.Transport/ITransporter.cs | 2 +- .../Tango.Transport/TransporterBase.cs | 10 +- .../Tango.WebRTC/Network/IceCandidateRequest.cs | 13 + .../Tango.WebRTC/Network/IceCandidateResponse.cs | 12 + .../Tango.WebRTC/Network/OfferRequest.cs | 13 + .../Tango.WebRTC/Network/OfferResponse.cs | 13 + .../Visual_Studio/Tango.WebRTC/Tango.WebRTC.csproj | 15 + .../Tango.WebRTC/WebRtcTransportAdapter.cs | 307 +++++++++++++++++ .../WebRtcTransportAdapterDisconnectedException.cs | 16 + .../Tango.WebRTC/WebRtcTransportAdapterMode.cs | 14 + 49 files changed, 1846 insertions(+), 68 deletions(-) create mode 100644 Software/Visual_Studio/FSE/Tango.FSE.Common/Notifications/InputBoxResult.cs create mode 100644 Software/Visual_Studio/FSE/Tango.FSE.Common/Notifications/InputBoxVM.cs create mode 100644 Software/Visual_Studio/Tango.FileSystem/Network/CopyRequest.cs create mode 100644 Software/Visual_Studio/Tango.FileSystem/Network/CopyResponse.cs create mode 100644 Software/Visual_Studio/Tango.FileSystem/Network/DeleteRequest.cs create mode 100644 Software/Visual_Studio/Tango.FileSystem/Network/DeleteResponse.cs create mode 100644 Software/Visual_Studio/Tango.FileSystem/Network/InitWebRtcRequest.cs create mode 100644 Software/Visual_Studio/Tango.FileSystem/Network/InitWebRtcResponse.cs create mode 100644 Software/Visual_Studio/Tango.FileSystem/Network/MoveRequest.cs create mode 100644 Software/Visual_Studio/Tango.FileSystem/Network/MoveResponse.cs create mode 100644 Software/Visual_Studio/Tango.WebRTC/Network/IceCandidateRequest.cs create mode 100644 Software/Visual_Studio/Tango.WebRTC/Network/IceCandidateResponse.cs create mode 100644 Software/Visual_Studio/Tango.WebRTC/Network/OfferRequest.cs create mode 100644 Software/Visual_Studio/Tango.WebRTC/Network/OfferResponse.cs create mode 100644 Software/Visual_Studio/Tango.WebRTC/WebRtcTransportAdapter.cs create mode 100644 Software/Visual_Studio/Tango.WebRTC/WebRtcTransportAdapterDisconnectedException.cs create mode 100644 Software/Visual_Studio/Tango.WebRTC/WebRtcTransportAdapterMode.cs (limited to 'Software/Visual_Studio/Tango.FileSystem/FileSystemManager.cs') 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 cba25303e..f074294b2 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 @@ -1,4 +1,5 @@ -using System; +using MaterialDesignThemes.Wpf; +using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; @@ -53,6 +54,10 @@ namespace Tango.FSE.PPCConsole.ViewModels public RelayCommand DeleteFileSystemHandlerCommand { get; set; } public RelayCommand OpenFileSystemHandlerDestinationCommand { get; set; } public RelayCommand RetryFailedFileSystemHandlerCommand { get; set; } + public RelayCommand> CopyPasteCommand { get; set; } + public RelayCommand> CutPasteCommand { get; set; } + public RelayCommand> DownloadCommand { get; set; } + public RelayCommand RenameCommand { get; set; } public FileSystemViewVM() @@ -69,6 +74,10 @@ namespace Tango.FSE.PPCConsole.ViewModels DeleteFileSystemHandlerCommand = new RelayCommand(DeleteFileSystemHandler); OpenFileSystemHandlerDestinationCommand = new RelayCommand(OpenFileSystemHandlerDestination); RetryFailedFileSystemHandlerCommand = new RelayCommand(RetryFailedFileSystemHandler); + CopyPasteCommand = new RelayCommand>((items) => PasteItems(items, false)); + CutPasteCommand = new RelayCommand>((items) => PasteItems(items, true)); + DownloadCommand = new RelayCommand>(DownloadSelectedItems); + RenameCommand = new RelayCommand(RenameFileSystemItem); } private async void NavigateBack() @@ -97,7 +106,10 @@ namespace Tango.FSE.PPCConsole.ViewModels private async void MachineProvider_MachineConnected(object sender, MachineConnectedEventArgs e) { - await Navigate(null); + if (e.DifferentFromPrevious) + { + await Navigate(null); + } } private async void NavigateToCurrentPath() @@ -123,9 +135,41 @@ namespace Tango.FSE.PPCConsole.ViewModels { if (items != null && items.Count > 0) { - if (await NotificationProvider.ShowWarningQuestion("Are you sure you want to delete the selected files/folders?", "DELETE")) + if (await NotificationProvider.ShowWarningQuestion($"Are you sure you want to delete {(items.Count == 1 ? $"'{items.First().Name}'" : $"the {items.Count} selected files/folders")}?", "DELETE")) { - //TODO: Delete items + using (var task = NotificationProvider.PushTaskItem("Removing...")) + { + int remainingItems = items.Count; + + foreach (var item in items) + { + task.UpdateProgress($"Removing '{item.Name}'..."); + + try + { + remainingItems--; + await FileSystemProvider.Delete(item); + } + catch (Exception ex) + { + LogManager.Log(ex, $"Could not remove '{item.Name}'."); + + if (remainingItems > 0) + { + if (!await NotificationProvider.ShowWarningQuestion($"Could not remove '{item.Name}'.\n{ex.FlattenMessage()}\nDo you wish to continue removing the remaining items?")) + { + break; + } + } + else + { + await NotificationProvider.ShowError($"Could not remove '{item.Name}'.\n{ex.FlattenMessage()}"); + } + } + } + } + + NavigateToCurrentPath(); } } } @@ -135,6 +179,15 @@ namespace Tango.FSE.PPCConsole.ViewModels foreach (var item in items.Where(x => x.FileSystemItem.Type != FileSystemItemType.Drive)) { Debug.WriteLine($"Dropped out: {item.FileSystemItem.Name} => {item.Destination}"); + + if (File.Exists(Path.Combine(item.Destination, item.FileSystemItem.Name)) || Directory.Exists(Path.Combine(item.Destination, item.FileSystemItem.Name))) + { + if (!await NotificationProvider.ShowWarningQuestion($"'{item.FileSystemItem.Name}' already exists on '{Path.GetDirectoryName(item.Destination)}'. Do you want to overwrite?")) + { + continue; + } + } + var handler = await FileSystemProvider.Download(item.FileSystemItem, item.Destination); FileSystemHandlers.Insert(0, handler); } @@ -234,6 +287,91 @@ namespace Tango.FSE.PPCConsole.ViewModels } } + private async void PasteItems(List items, bool move = false) + { + using (var task = NotificationProvider.PushTaskItem("Please wait...")) + { + int remainingItems = items.Count; + + foreach (var item in items) + { + Debug.WriteLine($"{(move ? "Cut" : "Copy")} Paste Item '{item.Name}' To '{CurrentItem.Name}'."); + + try + { + remainingItems--; + + if (move) + { + task.UpdateProgress($"Moving '{item.Name}'..."); + await FileSystemProvider.Move(item, CurrentItem); + } + else + { + task.UpdateProgress($"Copying '{item.Name}'..."); + await FileSystemProvider.Copy(item, CurrentItem); + } + } + catch (Exception ex) + { + string operation = move ? "move" : "copy"; + + LogManager.Log(ex, $"Could not {operation} '{item.Name}'."); + + if (remainingItems > 0) + { + if (!await NotificationProvider.ShowWarningQuestion($"Could not {operation} '{item.Name}'.\n{ex.FlattenMessage()}\nDo you wish to continue with the remaining items?")) + { + break; + } + } + else + { + await NotificationProvider.ShowError($"Could not {operation} '{item.Name}'.\n{ex.FlattenMessage()}"); + } + } + } + + NavigateToCurrentPath(); + } + } + + private void DownloadSelectedItems(List items) + { + + } + + private async void RenameFileSystemItem(FileSystemItem item) + { + if (item.Type != FileSystemItemType.Drive) + { + var result = await NotificationProvider.ShowInputBox( + "Rename", + $"Please enter a new {(item.Type == FileSystemItemType.File ? "file" : "folder")} name and press 'ENTER'.", + PackIconKind.Rename, item.Name, + $"{(item.Type == FileSystemItemType.File ? "file" : "folder")} name", + 100, + "RENAME"); + + if (result.Confirmed && result.Input != item.Name) + { + try + { + using (NotificationProvider.PushTaskItem("Renaming...")) + { + await FileSystemProvider.Rename(item, result.Input); + item.Path = Path.Combine(Path.GetDirectoryName(item.Path), result.Input); + } + } + catch (Exception ex) + { + LogManager.Log(ex, $"Error renaming '{item.Path}' to {result.Input}."); + await NotificationProvider.ShowError($"Error renaming '{item.Name}'.\n{ex.FlattenMessage()}"); + } + } + } + } + private void OnCurrentItemChanged() { CurrentPath = CurrentItem.Path; diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/ViewModels/RemoteDesktopViewVM.cs b/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/ViewModels/RemoteDesktopViewVM.cs index 1708322bc..9c2b139d9 100644 --- a/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/ViewModels/RemoteDesktopViewVM.cs +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/ViewModels/RemoteDesktopViewVM.cs @@ -47,6 +47,12 @@ namespace Tango.FSE.PPCConsole.ViewModels base.OnApplicationStarted(); RemoteDesktopProvider.FrameReceived += RemoteDesktopProvider_FrameReceived; + MachineProvider.MachineConnected += MachineProvider_MachineConnected; + } + + private void MachineProvider_MachineConnected(object sender, Common.Connection.MachineConnectedEventArgs e) + { + Source = null; } private async void StartRemoteDesktop() 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 8038ea905..d1143ede4 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 @@ -17,7 +17,7 @@ - + @@ -92,7 +92,7 @@ - + Devices @@ -121,6 +121,7 @@ + DropCommand="{Binding DropCommand}" + CutPasteCommand="{Binding CutPasteCommand}" + CopyPasteCommand="{Binding CopyPasteCommand}" + DownloadCommand="{Binding DownloadCommand}" + BackCommand="{Binding BackCommand}" + RenameCommand="{Binding RenameCommand}"/> - - - - Transfer Queue - + + + + + Transfer Queue + () - - + + + + + Enable fast communication channel + + @@ -221,13 +233,13 @@ + + + + + + + @@ -239,11 +251,16 @@ - - - / - - + + + + / + + + + /s + + diff --git a/Software/Visual_Studio/FSE/Tango.FSE.Common/Connection/MachineConnectedEventArgs.cs b/Software/Visual_Studio/FSE/Tango.FSE.Common/Connection/MachineConnectedEventArgs.cs index ae4d18538..003fe407b 100644 --- a/Software/Visual_Studio/FSE/Tango.FSE.Common/Connection/MachineConnectedEventArgs.cs +++ b/Software/Visual_Studio/FSE/Tango.FSE.Common/Connection/MachineConnectedEventArgs.cs @@ -10,5 +10,6 @@ namespace Tango.FSE.Common.Connection public class MachineConnectedEventArgs : EventArgs { public IExternalBridgeClient MachineOperator { get; set; } + public bool DifferentFromPrevious { get; set; } } } 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 7230d97fb..62de7cf48 100644 --- a/Software/Visual_Studio/FSE/Tango.FSE.Common/Controls/FileSystemControl.xaml +++ b/Software/Visual_Studio/FSE/Tango.FSE.Common/Controls/FileSystemControl.xaml @@ -2,6 +2,8 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:converters="clr-namespace:Tango.SharedUI.Converters;assembly=Tango.SharedUI" xmlns:local="clr-namespace:Tango.FileSystem;assembly=Tango.FileSystem" + xmlns:material="http://materialdesigninxaml.net/winfx/xaml/themes" + xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" xmlns:l="clr-namespace:Tango.FSE.Common.Controls"> @@ -9,6 +11,11 @@ @@ -123,7 +187,7 @@ - + @@ -149,7 +213,7 @@ - + @@ -158,7 +222,7 @@ - + @@ -167,7 +231,7 @@ - + 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 48fb35c0f..a748a63cc 100644 --- a/Software/Visual_Studio/FSE/Tango.FSE.Common/FileSystem/FileSystemHandler.cs +++ b/Software/Visual_Studio/FSE/Tango.FSE.Common/FileSystem/FileSystemHandler.cs @@ -12,6 +12,8 @@ namespace Tango.FSE.Common.FileSystem { private Action _abortAction; private FileSystemHandlerStatus _statusBeforePause; + private System.Timers.Timer _transferRateTimer; + private double _lastPosition; public FileSystemHandlerType Type { get; set; } @@ -65,6 +67,13 @@ namespace Tango.FSE.Common.FileSystem set { _length = value; RaisePropertyChangedAuto(); } } + private long _transferRate; + public long TransferRate + { + get { return _transferRate; } + set { _transferRate = value; RaisePropertyChangedAuto(); } + } + private Exception _failedException; public Exception FailedException { @@ -83,8 +92,27 @@ namespace Tango.FSE.Common.FileSystem _abortAction = abortAction; } + private void _transferRateTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) + { + if (Status == FileSystemHandlerStatus.Aborted || Status == FileSystemHandlerStatus.Completed || Status == FileSystemHandlerStatus.Failed) + { + _transferRateTimer.Dispose(); + return; + } + + TransferRate = (long)(Position - _lastPosition); + _lastPosition = Position; + } + internal void InvalidateProgress(double position, double length) { + if (_transferRateTimer == null) + { + _transferRateTimer = new System.Timers.Timer(1000); + _transferRateTimer.Elapsed += _transferRateTimer_Elapsed; + _transferRateTimer.Start(); + } + Position = position; Length = length; 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 c7e00610a..253bf801b 100644 --- a/Software/Visual_Studio/FSE/Tango.FSE.Common/FileSystem/IFileSystemProvider.cs +++ b/Software/Visual_Studio/FSE/Tango.FSE.Common/FileSystem/IFileSystemProvider.cs @@ -10,10 +10,16 @@ namespace Tango.FSE.Common.FileSystem { public interface IFileSystemProvider { + bool EnableWebRTC { get; set; } + bool IsWebRtcAvailable { get; } Task GetFolder(String path); Task GetSpecialFolder(SpecialFolder specialFolder); Task GetThisPC(); Task Download(FileSystemItem item, String localTargetFolder); Task Upload(String sourcePath, String remoteTargetFolder); + Task Copy(FileSystemItem source, FileSystemItem target); + Task Move(FileSystemItem source, FileSystemItem target); + Task Rename(FileSystemItem source, String newName); + Task Delete(FileSystemItem item); } } diff --git a/Software/Visual_Studio/FSE/Tango.FSE.Common/Notifications/INotificationProvider.cs b/Software/Visual_Studio/FSE/Tango.FSE.Common/Notifications/INotificationProvider.cs index 5985a687d..49402bd41 100644 --- a/Software/Visual_Studio/FSE/Tango.FSE.Common/Notifications/INotificationProvider.cs +++ b/Software/Visual_Studio/FSE/Tango.FSE.Common/Notifications/INotificationProvider.cs @@ -1,4 +1,5 @@ -using System; +using MaterialDesignThemes.Wpf; +using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; @@ -72,6 +73,16 @@ namespace Tango.FSE.Common.Notifications /// bool HasMessageBox { get; } + /// + /// Gets the current input box. + /// + InputBoxVM CurrentInputBox { get; } + + /// + /// Gets a value indicating whether this instance has input box. + /// + bool HasInputBox { get; } + /// /// Gets the current dialog. /// @@ -111,6 +122,19 @@ namespace Tango.FSE.Common.Notifications /// The message. Task ShowSuccess(String message); + /// + /// Shows an input box. + /// + /// The title. + /// The message. + /// The icon. + /// The default input. + /// The input hint. + /// The ok text. + /// The cancel text. + /// + Task ShowInputBox(String title, String message, PackIconKind icon = PackIconKind.InfoOutline, String defaultInput = null, String inputHint = null, int? maxChars = null, String okText = null, String cancelText = null); + /// /// Shows a question message box. /// diff --git a/Software/Visual_Studio/FSE/Tango.FSE.Common/Notifications/InputBoxResult.cs b/Software/Visual_Studio/FSE/Tango.FSE.Common/Notifications/InputBoxResult.cs new file mode 100644 index 000000000..1765dd655 --- /dev/null +++ b/Software/Visual_Studio/FSE/Tango.FSE.Common/Notifications/InputBoxResult.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Tango.FSE.Common.Notifications +{ + public class InputBoxResult + { + public bool Confirmed { get; set; } + public String Input { get; set; } + } +} diff --git a/Software/Visual_Studio/FSE/Tango.FSE.Common/Notifications/InputBoxVM.cs b/Software/Visual_Studio/FSE/Tango.FSE.Common/Notifications/InputBoxVM.cs new file mode 100644 index 000000000..7744d04d0 --- /dev/null +++ b/Software/Visual_Studio/FSE/Tango.FSE.Common/Notifications/InputBoxVM.cs @@ -0,0 +1,34 @@ +using MaterialDesignThemes.Wpf; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Tango.FSE.Common.Notifications +{ + public class InputBoxVM : MessageBoxVM + { + public PackIconKind Icon { get; set; } + public String InputHint { get; set; } + + public int MaxCharacters { get; set; } + + private String _input; + public String Input + { + get { return _input; } + set { _input = value; RaisePropertyChangedAuto(); InvalidateRelayCommands(); } + } + + public InputBoxVM() : base() + { + HasCancel = true; + } + + protected override bool CanOK() + { + return base.CanOK() && Input.IsNotNullOrEmpty(); + } + } +} 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 e3ecb319d..02df7140f 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 @@ -124,6 +124,8 @@ + + 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 f4ee8c461..790d21d6c 100644 --- a/Software/Visual_Studio/FSE/Tango.FSE.UI/Connection/DefaultMachineProvider.cs +++ b/Software/Visual_Studio/FSE/Tango.FSE.UI/Connection/DefaultMachineProvider.cs @@ -26,6 +26,7 @@ namespace Tango.FSE.UI.Connection public class DefaultMachineProvider : ExtendedObject, IMachineProvider { private List _eventRegistrations; + private String _lastMachineSerialNumber; private class EventRegistration { @@ -184,6 +185,7 @@ namespace Tango.FSE.UI.Connection IsBusy = true; String serial = vm.GetMachineSerialNumber(); + machine.SerialNumber = serial; using (var task = NotificationProvider.PushTaskItem($"Connecting to machine '{serial}'...")) { @@ -328,7 +330,10 @@ namespace Tango.FSE.UI.Connection MachineConnected?.Invoke(this, new MachineConnectedEventArgs() { MachineOperator = machineOperator, + DifferentFromPrevious = machineOperator.SerialNumber != _lastMachineSerialNumber, }); + + _lastMachineSerialNumber = machineOperator.SerialNumber; } protected virtual void OnMachineDisconnected(IExternalBridgeClient machineOperator, Exception exception) 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 bcc39d11d..1f6641f3a 100644 --- a/Software/Visual_Studio/FSE/Tango.FSE.UI/FileSystem/DefaultFileSystemProvider.cs +++ b/Software/Visual_Studio/FSE/Tango.FSE.UI/FileSystem/DefaultFileSystemProvider.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.IO.Compression; using System.Linq; @@ -13,16 +14,97 @@ using Tango.FileSystem.Network; using Tango.FSE.Common.Connection; using Tango.FSE.Common.FileSystem; using Tango.Transport; +using Tango.Transport.Transporters; +using Tango.WebRTC; namespace Tango.FSE.UI.FileSystem { public class DefaultFileSystemProvider : ExtendedObject, IFileSystemProvider { 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_WEB_RTC = 1024 * 15; + private List _activeHandlers; + + private bool _enableWebRTC; + public bool EnableWebRTC + { + get { return _enableWebRTC; } + set { _enableWebRTC = value; RaisePropertyChangedAuto(); } + } + + private bool _isWebRtcAvailable; + public bool IsWebRtcAvailable + { + get { return _isWebRtcAvailable; } + private set { _isWebRtcAvailable = value; RaisePropertyChangedAuto(); } + } public DefaultFileSystemProvider(IMachineProvider machineProvider) { + _activeHandlers = new List(); + + EnableWebRTC = true; //TODO: From Settings.. _machineProvider = machineProvider; + _machineProvider.MachineConnected += _machineProvider_MachineConnected; + _machineProvider.MachineDisconnected += _machineProvider_MachineDisconnected; + } + + private void _machineProvider_MachineDisconnected(object sender, MachineDisconnectedEventArgs e) + { + IsWebRtcAvailable = false; + + foreach (var handler in _activeHandlers.ToList()) + { + try + { + handler.RaiseFailed(new TransporterDisconnectedException("Machine disconnected.")); + } + catch (Exception ex) + { + Debug.WriteLine(ex); + } + } + + _activeHandlers.Clear(); + } + + private async void _machineProvider_MachineConnected(object sender, MachineConnectedEventArgs e) + { + if (EnableWebRTC) + { + try + { + IsWebRtcAvailable = false; + + await _machineProvider.MachineOperator.SendGenericRequest(new InitWebRtcRequest() + { + DataChannelName = WEB_RTC_CHANNEL_NAME + }, new TransportRequestConfig() + { + Timeout = TimeSpan.FromSeconds(60), + Priority = QueuePriority.Low + }); + + _webRtcTransporter = new BasicTransporter(new WebRtcTransportAdapter(_machineProvider.MachineOperator, WebRtcTransportAdapterMode.Active, WEB_RTC_CHANNEL_NAME)); + _webRtcTransporter.UseKeepAlive = false; + _webRtcTransporter.ComponentName = "File System Active WebRTC Transporter"; + await _webRtcTransporter.Connect(); + + IsWebRtcAvailable = true; + + LogManager.Log("FileSystem via WebRTC is ready."); + } + catch (Exception ex) + { + IsWebRtcAvailable = false; + EnableWebRTC = false; + + LogManager.Log(ex, "Error initializing FileSystem via WebRTC."); + } + } } public async Task GetFolder(string path) @@ -100,6 +182,8 @@ namespace Tango.FSE.UI.FileSystem } }); + _activeHandlers.Add(handler); + ThreadFactory.StartNew(async () => { try @@ -135,11 +219,13 @@ namespace Tango.FSE.UI.FileSystem } catch (Exception ex) { + _activeHandlers.Remove(handler); handler.RaiseFailed(ex); return; } long position = 0; + bool webRtcFailed = false; var tempFile = TemporaryManager.CreateFile(); @@ -153,13 +239,32 @@ namespace Tango.FSE.UI.FileSystem try { - var response = await _machineProvider.MachineOperator.SendGenericRequest( - new ChunkDownloadRequest() + ChunkDownloadResponse response = null; + ChunkDownloadRequest request = new ChunkDownloadRequest() { - MaxChunkSize = 1024 * 10, + MaxChunkSize = MAX_CHUNK_SIZE, OperationId = operationId, Position = position, - }, new TransportRequestConfig() { Timeout = TimeSpan.FromSeconds(30), Priority = QueuePriority.Low }); + }; + + if (_webRtcTransporter != null && _webRtcTransporter.State == TransportComponentState.Connected && EnableWebRTC && !webRtcFailed) + { + try + { + request.MaxChunkSize = MAX_CHUNK_SIZE_WEB_RTC; + response = await _webRtcTransporter.SendGenericRequest(request, new TransportRequestConfig() { Timeout = TimeSpan.FromSeconds(30), Priority = QueuePriority.Low }); + } + catch (Exception ex) + { + webRtcFailed = true; + LogManager.Log(ex, "WebRTC chunk download failed. Falling back to standard download..."); + continue; + } + } + else + { + response = await _machineProvider.MachineOperator.SendGenericRequest(request, new TransportRequestConfig() { Timeout = TimeSpan.FromSeconds(30), Priority = QueuePriority.Low }); + } using (FileStream fs = new FileStream(tempFile, FileMode.Append)) { @@ -171,6 +276,7 @@ namespace Tango.FSE.UI.FileSystem } catch (Exception ex) { + _activeHandlers.Remove(handler); tempFile.Delete(); handler.RaiseFailed(ex); return; @@ -203,6 +309,8 @@ namespace Tango.FSE.UI.FileSystem { tempFile.Delete(); } + + _activeHandlers.Remove(handler); }); return Task.FromResult(handler); @@ -212,5 +320,84 @@ namespace Tango.FSE.UI.FileSystem { throw new NotImplementedException(); } + + public async Task Copy(FileSystemItem source, FileSystemItem target) + { + if (source.Type == FileSystemItemType.Drive) + { + throw new NotSupportedException("The source file system item is not supported for copying."); + } + if (target.Type == FileSystemItemType.File) + { + throw new NotSupportedException("The target file system item is not a valid container."); + } + + await _machineProvider.MachineOperator.SendGenericRequest(new CopyRequest() + { + + Source = source.Path, + Destination = Path.Combine(target.Path, source.Name) + + }, new TransportRequestConfig() + { + Timeout = TimeSpan.FromSeconds(120), + }); + } + + public async Task Move(FileSystemItem source, FileSystemItem target) + { + if (source.Type == FileSystemItemType.Drive) + { + throw new NotSupportedException("The source file system item is not supported for copying."); + } + if (target.Type == FileSystemItemType.File) + { + throw new NotSupportedException("The target file system item is not a valid container."); + } + + await _machineProvider.MachineOperator.SendGenericRequest(new MoveRequest() + { + + Source = source.Path, + Destination = Path.Combine(target.Path, source.Name) + + }, new TransportRequestConfig() + { + Timeout = TimeSpan.FromSeconds(120), + }); + } + + public async Task Rename(FileSystemItem source, string newName) + { + if (source.Type == FileSystemItemType.Drive) + { + throw new NotSupportedException("The source file system item is not supported for copying."); + } + if (newName.ToList().Exists(x => Path.GetInvalidFileNameChars().Contains(x))) + { + throw new ArgumentException("The new name contains invalid characters."); + } + + await _machineProvider.MachineOperator.SendGenericRequest(new MoveRequest() + { + + Source = source.Path, + Destination = Path.Combine(Path.GetDirectoryName(source.Path), newName) + + }); + } + + public async Task Delete(FileSystemItem item) + { + if (item.Type == FileSystemItemType.Drive) + { + throw new NotSupportedException("The source file system item is not supported for deletion."); + } + + await _machineProvider.MachineOperator.SendGenericRequest(new DeleteRequest() + { + Path = item.Path + }, new TransportRequestConfig() { Timeout = TimeSpan.FromSeconds(120) }); + } } } diff --git a/Software/Visual_Studio/FSE/Tango.FSE.UI/Navigation/DefaultNavigationManager.cs b/Software/Visual_Studio/FSE/Tango.FSE.UI/Navigation/DefaultNavigationManager.cs index cf0b9d895..cdb328de2 100644 --- a/Software/Visual_Studio/FSE/Tango.FSE.UI/Navigation/DefaultNavigationManager.cs +++ b/Software/Visual_Studio/FSE/Tango.FSE.UI/Navigation/DefaultNavigationManager.cs @@ -172,7 +172,7 @@ namespace Tango.FSE.UI.Navigation if (_currentVM != null && _currentVM is INavigationBlocker) { - if (!await(_currentVM as INavigationBlocker).OnNavigateOutRequest()) + if (!await (_currentVM as INavigationBlocker).OnNavigateOutRequest()) { return false; } @@ -273,7 +273,16 @@ namespace Tango.FSE.UI.Navigation _lastFullPath = fullPath; - MainView.Instance.NavigationControl.NavigateTo(NavigationView.LayoutView.ToString()); + if (MainView.Instance.NavigationControl.GetSelectedElementNavigationName() != NavigationView.LayoutView.ToString()) + { + NotifyOnBeforeNavigated(null, LayoutView.Instance.DataContext); + + MainView.Instance.NavigationControl.NavigateTo(NavigationView.LayoutView.ToString(), () => + { + NotifyOnNavigated(null, LayoutView.Instance.DataContext); + }); + } + var navigationControl = LayoutView.Instance.NavigationControl; CurrentModule = module; var moduleView = navigationControl.NavigateTo(module.Name); 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 40d2e58e7..666619b59 100644 --- a/Software/Visual_Studio/FSE/Tango.FSE.UI/Notifications/DefaultNotificationProvider.cs +++ b/Software/Visual_Studio/FSE/Tango.FSE.UI/Notifications/DefaultNotificationProvider.cs @@ -127,6 +127,32 @@ namespace Tango.FSE.UI.Notifications } } + private InputBoxVM _currentInputBox; + /// + /// Gets the current input box. + /// + public InputBoxVM CurrentInputBox + { + get { return _currentInputBox; } + private set + { + _currentInputBox = value; + RaisePropertyChangedAuto(); + RaisePropertyChanged(nameof(HasInputBox)); + } + } + + /// + /// Gets a value indicating whether this instance has input box. + /// + public bool HasInputBox + { + get + { + return CurrentInputBox != null; + } + } + private FrameworkElement _currentDialog; /// /// Gets the current dialog if any. @@ -285,6 +311,45 @@ namespace Tango.FSE.UI.Notifications return source.Task; } + public Task ShowInputBox(string title, string message, PackIconKind icon = PackIconKind.InformationOutline, string defaultInput = null, string inputHint = null, int? maxChars = null, string okText = null, string cancelText = null) + { + TaskCompletionSource source = new TaskCompletionSource(); + + InputBoxVM vm = new InputBoxVM(); + vm.Title = title; + vm.Message = message; + vm.Icon = icon; + vm.Input = defaultInput; + vm.InputHint = inputHint; + if (maxChars != null) + { + vm.MaxCharacters = maxChars.Value; + } + if (okText != null) + { + vm.OKText = okText; + } + if (cancelText != null) + { + vm.CancelText = cancelText; + } + + vm.Accepted += () => + { + CurrentInputBox = null; + source.SetResult(new InputBoxResult() { Confirmed = true, Input = vm.Input }); + }; + vm.Canceled += () => + { + CurrentInputBox = null; + source.SetResult(new InputBoxResult() { Confirmed = false, Input = vm.Input }); + }; + + CurrentInputBox = vm; + + return source.Task; + } + /// /// Called when the message box has been closed. /// diff --git a/Software/Visual_Studio/FSE/Tango.FSE.UI/RemoteDesktop/DefaultRemoteDesktopProvider.cs b/Software/Visual_Studio/FSE/Tango.FSE.UI/RemoteDesktop/DefaultRemoteDesktopProvider.cs index 623b819d2..bb1fa9b58 100644 --- a/Software/Visual_Studio/FSE/Tango.FSE.UI/RemoteDesktop/DefaultRemoteDesktopProvider.cs +++ b/Software/Visual_Studio/FSE/Tango.FSE.UI/RemoteDesktop/DefaultRemoteDesktopProvider.cs @@ -123,7 +123,7 @@ namespace Tango.FSE.UI.RemoteDesktop _machineProvider.MachineOperator.RegisterRequestHandler(OnIceCandidateRequestReceived); } - private async void OnIceCandidateRequestReceived(WebRtcIceCandidateRequest request, string token) + private async void OnIceCandidateRequestReceived(ITransporter transporter, WebRtcIceCandidateRequest request, string token) { LogManager.Log("Ice candidate request received from the remote peer."); await _machineProvider.MachineOperator.SendGenericResponse(new WebRtcIceCandidateResponse() { }, token); diff --git a/Software/Visual_Studio/FSE/Tango.FSE.UI/SystemInfo/DefaultSystemInfoProvider.cs b/Software/Visual_Studio/FSE/Tango.FSE.UI/SystemInfo/DefaultSystemInfoProvider.cs index 9b72b308a..e691939f0 100644 --- a/Software/Visual_Studio/FSE/Tango.FSE.UI/SystemInfo/DefaultSystemInfoProvider.cs +++ b/Software/Visual_Studio/FSE/Tango.FSE.UI/SystemInfo/DefaultSystemInfoProvider.cs @@ -34,7 +34,7 @@ namespace Tango.FSE.UI.SystemInfo { var response = await MachineProvider.MachineOperator.SendGenericRequest(new GetMachineInformationRequest(), new Transport.TransportRequestConfig() { - Timeout = TimeSpan.FromSeconds(30) + Timeout = TimeSpan.FromSeconds(120) }); _package = response.Package; 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 4e6621b9e..9ed73afb2 100644 --- a/Software/Visual_Studio/FSE/Tango.FSE.UI/ViewModels/LayoutViewVM.cs +++ b/Software/Visual_Studio/FSE/Tango.FSE.UI/ViewModels/LayoutViewVM.cs @@ -170,6 +170,12 @@ namespace Tango.FSE.UI.ViewModels MachineProvider.MachineConnected += MachineProvider_MachineConnected; } + public override void OnNavigatedTo() + { + base.OnNavigatedTo(); + this.SetFocus(nameof(ToggleConnectionPaneCommand)); + } + private void DiagnosticsProvider_FrameReceived(object sender, Common.Diagnostics.DiagnosticsFrameReceivedEventArgs e) { Debug.WriteLine("Diagnostics Received..."); diff --git a/Software/Visual_Studio/FSE/Tango.FSE.UI/Views/LayoutView.xaml b/Software/Visual_Studio/FSE/Tango.FSE.UI/Views/LayoutView.xaml index cbe57e8dc..1c3cec748 100644 --- a/Software/Visual_Studio/FSE/Tango.FSE.UI/Views/LayoutView.xaml +++ b/Software/Visual_Studio/FSE/Tango.FSE.UI/Views/LayoutView.xaml @@ -272,7 +272,7 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Software/Visual_Studio/FSE/Tango.FSE.UI/Views/MainView.xaml.cs b/Software/Visual_Studio/FSE/Tango.FSE.UI/Views/MainView.xaml.cs index ae56ff7db..3d29032b1 100644 --- a/Software/Visual_Studio/FSE/Tango.FSE.UI/Views/MainView.xaml.cs +++ b/Software/Visual_Studio/FSE/Tango.FSE.UI/Views/MainView.xaml.cs @@ -60,5 +60,50 @@ namespace Tango.FSE.UI.Views } } } + + private async void GridInputBox_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e) + { + if (gridInputBox.IsVisible) + { + _previousFocusedElement = Keyboard.FocusedElement as UIElement; + await Task.Delay(100); + txtInput.Focus(); + Keyboard.Focus(txtInput); + txtInput.SelectAll(); + btnInputOK.IsDefault = true; + } + else + { + _previousFocusedElement?.Focus(); + } + } + + private void TxtInput_KeyDown(object sender, KeyEventArgs e) + { + if (e.Key == Key.Enter) + { + if (btnInputOK.Command != null) + { + if (btnInputOK.Command.CanExecute(null)) + { + btnInputOK.Command.Execute(null); + } + } + + e.Handled = true; + } + else if (e.Key == Key.Escape) + { + if (btnInputCancel.Command != null) + { + if (btnInputCancel.Command.CanExecute(null)) + { + btnInputCancel.Command.Execute(null); + } + } + + e.Handled = 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 cf7a21e13..512935b50 100644 --- a/Software/Visual_Studio/PPC/Tango.PPC.Common/FileSystem/DefaultFileSystemService.cs +++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/FileSystem/DefaultFileSystemService.cs @@ -13,6 +13,8 @@ using Tango.FileSystem.Network; using Tango.Integration.ExternalBridge; using Tango.PPC.Common.ExternalBridge; using Tango.Transport; +using Tango.Transport.Transporters; +using Tango.WebRTC; namespace Tango.PPC.Common.FileSystem { @@ -42,16 +44,61 @@ namespace Tango.PPC.Common.FileSystem private FileSystemManager _manager; private Dictionary _operations; + private Dictionary _webRtcClients; public bool Enabled { get; set; } = true; + public bool EnableWebRTC { get; set; } = true; public DefaultFileSystemService(IPPCExternalBridgeService externalBridge) { + _webRtcClients = new Dictionary(); _manager = new FileSystemManager(); _operations = new Dictionary(); externalBridge.RegisterRequestHandler(this); } + [ExternalBridgeRequestHandlerMethod(typeof(InitWebRtcRequest))] + public async void OnInitWebRtcRequest(InitWebRtcRequest request, String token, ExternalBridgeReceiver receiver) + { + try + { + if (!EnableWebRTC) + { + await receiver.SendErrorResponse(new InvalidOperationException("The file system service WebRTC channel is disabled on this machine."), token); + return; + } + + if (_webRtcClients.ContainsKey(receiver)) + { + _webRtcClients[receiver].Dispose(); + } + + var webRtcAdapter = new WebRtcTransportAdapter(receiver, WebRtcTransportAdapterMode.Passive, request.DataChannelName); + webRtcAdapter.Ready += (x, e) => + { + LogManager.Log("File System via WebRTC is ready."); + }; + + BasicTransporter webRtcTransporter = new BasicTransporter(webRtcAdapter); + webRtcTransporter = new BasicTransporter(webRtcAdapter); + webRtcTransporter.ComponentName = "File System Passive WebRTC Transporter"; + webRtcTransporter.UseKeepAlive = false; + webRtcTransporter.RegisterRequestHandler(WebRtcChunkDownloadRequestReceived); + await webRtcTransporter.Connect(); + await receiver.SendGenericResponse(new InitWebRtcResponse(), token); + _webRtcClients[receiver] = webRtcTransporter; + } + catch (Exception ex) + { + await receiver.SendErrorResponse(ex, token); + } + } + + private void WebRtcChunkDownloadRequestReceived(ITransporter transporter, ChunkDownloadRequest request, string token) + { + OnChunkDownloadRequest(request, token, transporter); + } + [ExternalBridgeRequestHandlerMethod(typeof(GetFileSystemItemRequest))] public async void OnGetFileSystemItemRequest(GetFileSystemItemRequest request, String token, ExternalBridgeReceiver receiver) { @@ -171,7 +218,7 @@ namespace Tango.PPC.Common.FileSystem } [ExternalBridgeRequestHandlerMethod(typeof(ChunkDownloadRequest))] - public async void OnChunkDownloadRequest(ChunkDownloadRequest request, String token, ExternalBridgeReceiver receiver) + public async void OnChunkDownloadRequest(ChunkDownloadRequest request, String token, ITransporter receiver) { FileSystemOperation operation; _operations.TryGetValue(request.OperationId, out operation); @@ -182,16 +229,26 @@ namespace Tango.PPC.Common.FileSystem return; } - using (FileStream stream = new FileStream(operation.Path, FileMode.Open)) + FileStream stream = null; + + try { + stream = new FileStream(operation.Path, FileMode.Open); stream.Position = request.Position; byte[] data = new byte[Math.Min(request.MaxChunkSize, stream.Length - stream.Position)]; await stream.ReadAsync(data, 0, data.Length); + stream.Dispose(); + stream = null; await receiver.SendGenericResponse(new ChunkDownloadResponse() { Data = data }, token, new TransportResponseConfig() { Priority = QueuePriority.Low }); } + catch (Exception ex) + { + stream?.Dispose(); + await receiver.SendErrorResponse(ex, token); + } } [ExternalBridgeRequestHandlerMethod(typeof(AbortOperationRequest))] @@ -235,9 +292,63 @@ namespace Tango.PPC.Common.FileSystem } } - public void OnReceiverDisconnected(ExternalBridgeReceiver receiver) + [ExternalBridgeRequestHandlerMethod(typeof(MoveRequest))] + public async void OnMoveRequest(MoveRequest request, String token, ExternalBridgeReceiver receiver) + { + try + { + _manager.Move(request); + await receiver.SendGenericResponse(new MoveResponse(), token); + } + catch (Exception ex) + { + await receiver.SendErrorResponse(ex, token); + } + } + + [ExternalBridgeRequestHandlerMethod(typeof(CopyRequest))] + public async void OnCopyRequest(CopyRequest request, String token, ExternalBridgeReceiver receiver) + { + try + { + _manager.Copy(request); + await receiver.SendGenericResponse(new CopyResponse(), token); + } + catch (Exception ex) + { + await receiver.SendErrorResponse(ex, token); + } + } + + [ExternalBridgeRequestHandlerMethod(typeof(DeleteRequest))] + public async void OnDeleteRequest(DeleteRequest request, String token, ExternalBridgeReceiver receiver) { + try + { + _manager.Delete(request.Path); + await receiver.SendGenericResponse(new DeleteResponse(), token); + } + catch (Exception ex) + { + await receiver.SendErrorResponse(ex, token); + } + } + public void OnReceiverDisconnected(ExternalBridgeReceiver receiver) + { + if (_webRtcClients.ContainsKey(receiver)) + { + try + { + var webRtcTransporter = _webRtcClients[receiver]; + _webRtcClients.Remove(receiver); + webRtcTransporter.Dispose(); + } + catch (Exception ex) + { + LogManager.Log(ex, "Error disposing the WebRTC transporter."); + } + } } } } diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/FileSystem/IFileSystemService.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/FileSystem/IFileSystemService.cs index 050bb1cd6..6cf3321a3 100644 --- a/Software/Visual_Studio/PPC/Tango.PPC.Common/FileSystem/IFileSystemService.cs +++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/FileSystem/IFileSystemService.cs @@ -9,5 +9,6 @@ namespace Tango.PPC.Common.FileSystem public interface IFileSystemService { bool Enabled { get; set; } + bool EnableWebRTC { get; set; } } } diff --git a/Software/Visual_Studio/SideChains/WebRtc.NET/src/conductor.cc b/Software/Visual_Studio/SideChains/WebRtc.NET/src/conductor.cc index c7c4f3ba5..8a2efa438 100644 --- a/Software/Visual_Studio/SideChains/WebRtc.NET/src/conductor.cc +++ b/Software/Visual_Studio/SideChains/WebRtc.NET/src/conductor.cc @@ -516,7 +516,7 @@ namespace Native // A data buffer was successfully received. void Conductor::OnMessage(const webrtc::DataBuffer& buffer) { - LOG(INFO) << __FUNCTION__; + //LOG(INFO) << __FUNCTION__; //Causes low performance when debugging if (buffer.binary) { diff --git a/Software/Visual_Studio/Tango.FileSystem/FileExplorerControl.cs b/Software/Visual_Studio/Tango.FileSystem/FileExplorerControl.cs index 23cac7733..60061780b 100644 --- a/Software/Visual_Studio/Tango.FileSystem/FileExplorerControl.cs +++ b/Software/Visual_Studio/Tango.FileSystem/FileExplorerControl.cs @@ -16,6 +16,7 @@ using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; +using Tango.Core.Commands; using Tango.Core.IO; namespace Tango.FileSystem @@ -28,6 +29,41 @@ namespace Tango.FileSystem private Point _dragOutStartPoint; private bool _isMouseDown; private List _selectedItemsBeforeDrag; + private List _copyItems; + private bool _isCut; + private bool _isAfterContextMenu; + + #region IsCut Attached Property + + /// + /// Determines whether the draggable element is currently being dragged. + /// + public static readonly DependencyProperty IsCutProperty = + DependencyProperty.RegisterAttached("IsCut", + typeof(bool), typeof(FileExplorerControl), + new FrameworkPropertyMetadata(false)); + + /// + /// Sets the IsCut attached property. + /// + /// The element. + /// if set to true [value]. + public static void SetIsCut(FrameworkElement element, bool value) + { + element.SetValue(IsCutProperty, value); + } + + /// + /// Gets the is dragging attached property. + /// + /// The element. + /// + public static bool GetIsCut(FrameworkElement element) + { + return (bool)element.GetValue(IsCutProperty); + } + + #endregion public IFileSystemContainer CurrentItem { @@ -35,7 +71,7 @@ namespace Tango.FileSystem set { SetValue(CurrentItemProperty, value); } } public static readonly DependencyProperty CurrentItemProperty = - DependencyProperty.Register("CurrentItem", typeof(IFileSystemContainer), typeof(FileExplorerControl), new PropertyMetadata(null)); + DependencyProperty.Register("CurrentItem", typeof(IFileSystemContainer), typeof(FileExplorerControl), new PropertyMetadata(null, (d, e) => (d as FileExplorerControl).OnCurrentItemChanged())); public FileSystemItem SelectedItem { @@ -61,6 +97,14 @@ namespace Tango.FileSystem public static readonly DependencyProperty DeleteCommandProperty = DependencyProperty.Register("DeleteCommand", typeof(ICommand), typeof(FileExplorerControl), new PropertyMetadata(null)); + public ICommand DeleteCommandInternal + { + get { return (ICommand)GetValue(DeleteCommandInternalProperty); } + set { SetValue(DeleteCommandInternalProperty, value); } + } + public static readonly DependencyProperty DeleteCommandInternalProperty = + DependencyProperty.Register("DeleteCommandInternal", typeof(ICommand), typeof(FileExplorerControl), new PropertyMetadata(null)); + public ICommand DropCommand { get { return (ICommand)GetValue(DropCommandProperty); } @@ -77,6 +121,110 @@ namespace Tango.FileSystem public static readonly DependencyProperty DragCommandProperty = DependencyProperty.Register("DragCommand", typeof(ICommand), typeof(FileExplorerControl), new PropertyMetadata(null)); + public ICommand CopyCommand + { + get { return (ICommand)GetValue(CopyCommandProperty); } + set { SetValue(CopyCommandProperty, value); } + } + public static readonly DependencyProperty CopyCommandProperty = + DependencyProperty.Register("CopyCommand", typeof(ICommand), typeof(FileExplorerControl), new PropertyMetadata(null)); + + public ICommand CutCommand + { + get { return (ICommand)GetValue(CutCommandProperty); } + set { SetValue(CutCommandProperty, value); } + } + public static readonly DependencyProperty CutCommandProperty = + DependencyProperty.Register("CutCommand", typeof(ICommand), typeof(FileExplorerControl), new PropertyMetadata(null)); + + public ICommand CopyPasteCommand + { + get { return (ICommand)GetValue(CopyPasteCommandProperty); } + set { SetValue(CopyPasteCommandProperty, value); } + } + public static readonly DependencyProperty CopyPasteCommandProperty = + DependencyProperty.Register("CopyPasteCommand", typeof(ICommand), typeof(FileExplorerControl), new PropertyMetadata(null)); + + public ICommand CutPasteCommand + { + get { return (ICommand)GetValue(CutPasteCommandProperty); } + set { SetValue(CutPasteCommandProperty, value); } + } + public static readonly DependencyProperty CutPasteCommandProperty = + DependencyProperty.Register("CutPasteCommand", typeof(ICommand), typeof(FileExplorerControl), new PropertyMetadata(null)); + + public ICommand PasteCommandInternal + { + get { return (ICommand)GetValue(PasteCommandInternalProperty); } + set { SetValue(PasteCommandInternalProperty, value); } + } + public static readonly DependencyProperty PasteCommandInternalProperty = + DependencyProperty.Register("PasteCommandInternal", typeof(ICommand), typeof(FileExplorerControl), new PropertyMetadata(null)); + + public ICommand SelectAllCommand + { + get { return (ICommand)GetValue(SelectAllCommandProperty); } + set { SetValue(SelectAllCommandProperty, value); } + } + public static readonly DependencyProperty SelectAllCommandProperty = + DependencyProperty.Register("SelectAllCommand", typeof(ICommand), typeof(FileExplorerControl), new PropertyMetadata(null)); + + public ICommand DownloadCommandInternal + { + get { return (ICommand)GetValue(DownloadCommandInternalProperty); } + set { SetValue(DownloadCommandInternalProperty, value); } + } + public static readonly DependencyProperty DownloadCommandInternalProperty = + DependencyProperty.Register("DownloadCommandInternal", typeof(ICommand), typeof(FileExplorerControl), new PropertyMetadata(null)); + + public ICommand DownloadCommand + { + get { return (ICommand)GetValue(DownloadCommandProperty); } + set { SetValue(DownloadCommandProperty, value); } + } + public static readonly DependencyProperty DownloadCommandProperty = + DependencyProperty.Register("DownloadCommand", typeof(ICommand), typeof(FileExplorerControl), new PropertyMetadata(null)); + + public ICommand OpenCommand + { + get { return (ICommand)GetValue(OpenCommandProperty); } + set { SetValue(OpenCommandProperty, value); } + } + public static readonly DependencyProperty OpenCommandProperty = + DependencyProperty.Register("OpenCommand", typeof(ICommand), typeof(FileExplorerControl), new PropertyMetadata(null)); + + public ICommand BackCommand + { + get { return (ICommand)GetValue(BackCommandProperty); } + set { SetValue(BackCommandProperty, value); } + } + public static readonly DependencyProperty BackCommandProperty = + DependencyProperty.Register("BackCommand", typeof(ICommand), typeof(FileExplorerControl), new PropertyMetadata(null)); + + public ICommand RenameCommand + { + get { return (ICommand)GetValue(RenameCommandProperty); } + set { SetValue(RenameCommandProperty, value); } + } + public static readonly DependencyProperty RenameCommandProperty = + DependencyProperty.Register("RenameCommand", typeof(ICommand), typeof(FileExplorerControl), new PropertyMetadata(null)); + + public ICommand RenameCommandInternal + { + get { return (ICommand)GetValue(RenameCommandInternalProperty); } + set { SetValue(RenameCommandInternalProperty, value); } + } + public static readonly DependencyProperty RenameCommandInternalProperty = + DependencyProperty.Register("RenameCommandInternal", typeof(ICommand), typeof(FileExplorerControl), new PropertyMetadata(null)); + + public bool IsContextMenuOpened + { + get { return (bool)GetValue(IsContextMenuOpenedProperty); } + set { SetValue(IsContextMenuOpenedProperty, value); } + } + public static readonly DependencyProperty IsContextMenuOpenedProperty = + DependencyProperty.Register("IsContextMenuOpened", typeof(bool), typeof(FileExplorerControl), new PropertyMetadata(false, (d, e) => (d as FileExplorerControl).OnIsContextMenuOpenedChanged())); + public ImageSource DriveIcon { get { return (ImageSource)GetValue(DriveIconProperty); } @@ -124,7 +272,138 @@ namespace Tango.FileSystem public FileExplorerControl() { + Focusable = true; + + _copyItems = new List(); _selectedItemsBeforeDrag = new List(); + + CopyCommand = new RelayCommand(() => + { + ResetItemsCut(); + _copyItems.Clear(); + _copyItems.AddRange(SelectedItems.ToList()); + _isCut = false; + }, () => SelectedItems != null && SelectedItems.Count > 0); + + CutCommand = new RelayCommand(() => + { + _copyItems.Clear(); + _copyItems.AddRange(SelectedItems.ToList()); + CutSelectedItems(); + _isCut = true; + + }, () => SelectedItems != null && SelectedItems.Count > 0); + + PasteCommandInternal = new RelayCommand(() => + { + if (_isCut) + { + CutPasteCommand?.Execute(_copyItems.ToList()); + } + else + { + CopyPasteCommand?.Execute(_copyItems.ToList()); + } + + ResetItemsCut(); + _copyItems.Clear(); + }, () => _copyItems.Count > 0); + + SelectAllCommand = new RelayCommand(() => + { + ResetItemsCut(); + _copyItems.Clear(); + SelectedItems.Clear(); + + if (CurrentItem != null) + { + foreach (var item in CurrentItem.Items) + { + SelectedItems.Add(item); + } + } + }); + + DownloadCommandInternal = new RelayCommand(() => + { + + DownloadCommand?.Execute(SelectedItems.ToList()); + + }, () => SelectedItems != null && SelectedItems.Count > 0 && SelectedItems.All(x => x.Type != FileSystemItemType.Drive)); + + OpenCommand = new RelayCommand(() => + { + ItemDoubleClickedCommand?.Execute(SelectedItems.FirstOrDefault()); + }, () => SelectedItems != null && SelectedItems.Count == 1); + + DeleteCommandInternal = new RelayCommand(() => + { + + DeleteCommand?.Execute(SelectedItems.ToList()); + + }, () => SelectedItems != null && SelectedItems.Count > 1 && SelectedItems.All(x => x.Type != FileSystemItemType.Drive)); + + RenameCommandInternal = new RelayCommand(() => + { + + RenameCommand?.Execute(SelectedItems.FirstOrDefault()); + + }, () => SelectedItems != null && SelectedItems.Count == 1 && SelectedItems.All(x => x.Type != FileSystemItemType.Drive)); + } + + private void OnIsContextMenuOpenedChanged() + { + _isMouseDown = false; + (PasteCommandInternal as RelayCommand)?.RaiseCanExecuteChanged(); + (CutCommand as RelayCommand)?.RaiseCanExecuteChanged(); + (CopyCommand as RelayCommand)?.RaiseCanExecuteChanged(); + (DownloadCommandInternal as RelayCommand)?.RaiseCanExecuteChanged(); + (OpenCommand as RelayCommand)?.RaiseCanExecuteChanged(); + (DeleteCommandInternal as RelayCommand)?.RaiseCanExecuteChanged(); + (RenameCommandInternal as RelayCommand)?.RaiseCanExecuteChanged(); + + if (IsContextMenuOpened) + { + _isAfterContextMenu = true; + } + } + + private void ResetItemsCut() + { + foreach (var item in _listBox.Items) + { + var element = _listBox.ItemContainerGenerator.ContainerFromItem(item) as FrameworkElement; + if (element != null) + { + SetIsCut(element, false); + } + + element = _datagrid.ItemContainerGenerator.ContainerFromItem(item) as FrameworkElement; + if (element != null) + { + SetIsCut(element, false); + } + } + } + + private void CutSelectedItems() + { + ResetItemsCut(); + + foreach (var item in SelectedItems.ToList()) + { + var element = _listBox.ItemContainerGenerator.ContainerFromItem(item) as FrameworkElement; + if (element != null) + { + SetIsCut(element, true); + } + + element = _datagrid.ItemContainerGenerator.ContainerFromItem(item) as FrameworkElement; + if (element != null) + { + SetIsCut(element, true); + } + } } public override void OnApplyTemplate() @@ -138,17 +417,77 @@ namespace Tango.FileSystem _datagrid.SelectionChanged += _datagrid_SelectionChanged; } - protected override void OnPreviewKeyUp(KeyEventArgs e) + protected override void OnPreviewKeyDown(KeyEventArgs e) { - base.OnPreviewKeyUp(e); + base.OnPreviewKeyDown(e); if (e.Key == Key.Delete) { - if (SelectedItems != null && SelectedItems.Count > 0) + if (DeleteCommandInternal != null && DeleteCommandInternal.CanExecute(null)) + { + DeleteCommandInternal?.Execute(null); + } + } + else if (e.Key == Key.Return) + { + if (OpenCommand != null && OpenCommand.CanExecute(null)) + { + OpenCommand?.Execute(null); + } + } + else if (e.Key == Key.C && Keyboard.IsKeyDown(Key.LeftCtrl)) + { + if (CopyCommand != null && CopyCommand.CanExecute(null)) { - DeleteCommand?.Execute(SelectedItems); + CopyCommand.Execute(null); } } + else if (e.Key == Key.X && Keyboard.IsKeyDown(Key.LeftCtrl)) + { + if (CutCommand != null && CutCommand.CanExecute(null)) + { + CutCommand.Execute(null); + } + } + else if (e.Key == Key.V && Keyboard.IsKeyDown(Key.LeftCtrl)) + { + if (PasteCommandInternal != null && PasteCommandInternal.CanExecute(null)) + { + PasteCommandInternal.Execute(null); + } + } + else if (e.Key == Key.A && Keyboard.IsKeyDown(Key.LeftCtrl)) + { + if (SelectAllCommand != null && SelectAllCommand.CanExecute(null)) + { + SelectAllCommand.Execute(null); + } + } + else if (e.Key == Key.D && Keyboard.IsKeyDown(Key.LeftCtrl)) + { + if (DownloadCommandInternal != null && DownloadCommandInternal.CanExecute(null)) + { + DownloadCommandInternal.Execute(null); + } + } + else if (e.Key == Key.F2) + { + if (RenameCommandInternal != null && RenameCommandInternal.CanExecute(null)) + { + RenameCommandInternal.Execute(null); + } + } + else if (e.Key == Key.Down) + { + if (SelectedItems != null && SelectedItems.Count == 0 && CurrentItem != null && CurrentItem.Items.Count > 0) + { + SelectedItems.Add(CurrentItem.Items.FirstOrDefault()); + } + } + else if (e.Key == Key.Back) + { + BackCommand?.Execute(null); + } } private void _datagrid_SelectionChanged(object sender, SelectionChangedEventArgs e) @@ -279,6 +618,12 @@ namespace Tango.FileSystem { base.OnPreviewMouseLeftButtonDown(e); + if (_isAfterContextMenu) + { + _isAfterContextMenu = false; + return; + } + if (!AllowDrag) return; if (e.OriginalSource is FrameworkElement) @@ -309,7 +654,7 @@ namespace Tango.FileSystem { base.OnPreviewMouseMove(e); - if (_isMouseDown) + if (_isMouseDown && !IsContextMenuOpened) { Point mpos = e.GetPosition(null); Vector diff = this._dragOutStartPoint - mpos; @@ -375,7 +720,7 @@ namespace Tango.FileSystem } //Notify to user with all items! - Dispatcher.BeginInvoke(new Action(() => + Dispatcher.BeginInvoke(new Action(() => { DragCommand?.Execute(notifyItems); })); @@ -400,7 +745,16 @@ namespace Tango.FileSystem } string[] files = dropItems.Select(x => x.Item2.Path).ToArray(); - var ef = DragDrop.DoDragDrop(this, new DataObject(DataFormats.FileDrop, files, false), DragDropEffects.Copy); + + try + { + var ef = DragDrop.DoDragDrop(this, new DataObject(DataFormats.FileDrop, files, false), DragDropEffects.Copy); + } + catch (Exception ex) + { + Debug.WriteLine(ex); + Debugger.Break(); + } await Task.Delay(3000); @@ -419,5 +773,15 @@ namespace Tango.FileSystem } } } + + private async void OnCurrentItemChanged() + { + if (IsVisible) + { + await Task.Delay(100); + this.Focus(); + Keyboard.Focus(this); + } + } } } diff --git a/Software/Visual_Studio/Tango.FileSystem/FileSystemItem.cs b/Software/Visual_Studio/Tango.FileSystem/FileSystemItem.cs index 536409f63..c8b2fce32 100644 --- a/Software/Visual_Studio/Tango.FileSystem/FileSystemItem.cs +++ b/Software/Visual_Studio/Tango.FileSystem/FileSystemItem.cs @@ -5,13 +5,19 @@ using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Media.Imaging; +using Tango.Core; using Tango.FileSystem.Network; namespace Tango.FileSystem { - public abstract class FileSystemItem + public abstract class FileSystemItem : ExtendedObject { - public String Path { get; set; } + private String _path; + public String Path + { + get { return _path; } + set { _path = value; RaisePropertyChangedAuto(); RaisePropertyChanged(nameof(Name)); } + } public FileSystemItemType Type { get; protected set; } diff --git a/Software/Visual_Studio/Tango.FileSystem/FileSystemManager.cs b/Software/Visual_Studio/Tango.FileSystem/FileSystemManager.cs index 44c8f1901..b8e59c322 100644 --- a/Software/Visual_Studio/Tango.FileSystem/FileSystemManager.cs +++ b/Software/Visual_Studio/Tango.FileSystem/FileSystemManager.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; +using Tango.Core.Helpers; using Tango.FileSystem.Network; namespace Tango.FileSystem @@ -106,5 +107,50 @@ namespace Tango.FileSystem throw new FileNotFoundException("Could not locate the specified file or directory."); } } + + public void Move(MoveRequest request) + { + if (Directory.Exists(request.Destination)) + { + throw new IOException($"'{Path.GetFileName(request.Destination)}' already exists on the target folder."); + } + + if (File.Exists(request.Source)) + { + File.Move(request.Source, request.Destination); + } + else if (Directory.Exists(request.Source)) + { + Directory.Move(request.Source, request.Destination); + } + else + { + throw new FileNotFoundException("Could not locate the source file or folder."); + } + } + + public void Copy(CopyRequest request) + { + if (File.Exists(request.Source)) + { + if (request.Source == request.Destination) + { + while (File.Exists(request.Destination)) + { + request.Destination = Path.Combine(Path.GetDirectoryName(request.Destination), Path.GetFileNameWithoutExtension(request.Destination)) + " copy" + Path.GetExtension(request.Destination); + } + } + File.Copy(request.Source, request.Destination, true); + } + else if (Directory.Exists(request.Source)) + { + Directory.CreateDirectory(Path.GetDirectoryName(request.Destination)); + PathHelper.CopyDirectory(request.Source, request.Destination, true); + } + else + { + throw new FileNotFoundException("Could not locate the source file or folder."); + } + } } } diff --git a/Software/Visual_Studio/Tango.FileSystem/Network/CopyRequest.cs b/Software/Visual_Studio/Tango.FileSystem/Network/CopyRequest.cs new file mode 100644 index 000000000..2e7b8a406 --- /dev/null +++ b/Software/Visual_Studio/Tango.FileSystem/Network/CopyRequest.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Tango.FileSystem.Network +{ + public class CopyRequest + { + public String Source { get; set; } + public String Destination { get; set; } + } +} diff --git a/Software/Visual_Studio/Tango.FileSystem/Network/CopyResponse.cs b/Software/Visual_Studio/Tango.FileSystem/Network/CopyResponse.cs new file mode 100644 index 000000000..e22ce6542 --- /dev/null +++ b/Software/Visual_Studio/Tango.FileSystem/Network/CopyResponse.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 CopyResponse + { + } +} diff --git a/Software/Visual_Studio/Tango.FileSystem/Network/DeleteRequest.cs b/Software/Visual_Studio/Tango.FileSystem/Network/DeleteRequest.cs new file mode 100644 index 000000000..300acdb09 --- /dev/null +++ b/Software/Visual_Studio/Tango.FileSystem/Network/DeleteRequest.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 DeleteRequest + { + public String Path { get; set; } + } +} diff --git a/Software/Visual_Studio/Tango.FileSystem/Network/DeleteResponse.cs b/Software/Visual_Studio/Tango.FileSystem/Network/DeleteResponse.cs new file mode 100644 index 000000000..37afdab0b --- /dev/null +++ b/Software/Visual_Studio/Tango.FileSystem/Network/DeleteResponse.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 DeleteResponse + { + } +} diff --git a/Software/Visual_Studio/Tango.FileSystem/Network/InitWebRtcRequest.cs b/Software/Visual_Studio/Tango.FileSystem/Network/InitWebRtcRequest.cs new file mode 100644 index 000000000..5d8f1eb3a --- /dev/null +++ b/Software/Visual_Studio/Tango.FileSystem/Network/InitWebRtcRequest.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 InitWebRtcRequest + { + public String DataChannelName { get; set; } + } +} diff --git a/Software/Visual_Studio/Tango.FileSystem/Network/InitWebRtcResponse.cs b/Software/Visual_Studio/Tango.FileSystem/Network/InitWebRtcResponse.cs new file mode 100644 index 000000000..3425a9096 --- /dev/null +++ b/Software/Visual_Studio/Tango.FileSystem/Network/InitWebRtcResponse.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 InitWebRtcResponse + { + } +} diff --git a/Software/Visual_Studio/Tango.FileSystem/Network/MoveRequest.cs b/Software/Visual_Studio/Tango.FileSystem/Network/MoveRequest.cs new file mode 100644 index 000000000..0d9f593d3 --- /dev/null +++ b/Software/Visual_Studio/Tango.FileSystem/Network/MoveRequest.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Tango.FileSystem.Network +{ + public class MoveRequest + { + public String Source { get; set; } + public String Destination { get; set; } + } +} diff --git a/Software/Visual_Studio/Tango.FileSystem/Network/MoveResponse.cs b/Software/Visual_Studio/Tango.FileSystem/Network/MoveResponse.cs new file mode 100644 index 000000000..05d78c573 --- /dev/null +++ b/Software/Visual_Studio/Tango.FileSystem/Network/MoveResponse.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 MoveResponse + { + } +} diff --git a/Software/Visual_Studio/Tango.FileSystem/Tango.FileSystem.csproj b/Software/Visual_Studio/Tango.FileSystem/Tango.FileSystem.csproj index 733493f02..a1218f12d 100644 --- a/Software/Visual_Studio/Tango.FileSystem/Tango.FileSystem.csproj +++ b/Software/Visual_Studio/Tango.FileSystem/Tango.FileSystem.csproj @@ -58,6 +58,14 @@ + + + + + + + + MSBuild:Compile diff --git a/Software/Visual_Studio/Tango.FileSystem/Themes/Generic.xaml b/Software/Visual_Studio/Tango.FileSystem/Themes/Generic.xaml index f793be947..9cc27c7c6 100644 --- a/Software/Visual_Studio/Tango.FileSystem/Themes/Generic.xaml +++ b/Software/Visual_Studio/Tango.FileSystem/Themes/Generic.xaml @@ -21,6 +21,13 @@ + + + + + + + @@ -123,7 +130,7 @@ - + @@ -149,7 +156,7 @@ - + @@ -158,7 +165,7 @@ - + @@ -167,7 +174,7 @@ - + diff --git a/Software/Visual_Studio/Tango.SharedUI/Controls/NavigationControl.cs b/Software/Visual_Studio/Tango.SharedUI/Controls/NavigationControl.cs index ce1fca7ac..c2d544042 100644 --- a/Software/Visual_Studio/Tango.SharedUI/Controls/NavigationControl.cs +++ b/Software/Visual_Studio/Tango.SharedUI/Controls/NavigationControl.cs @@ -728,6 +728,11 @@ namespace Tango.SharedUI.Controls return element; } + public String GetSelectedElementNavigationName() + { + return GetNavigationName(SelectedElement); + } + /// /// This method needs to be called in order for // the element to print visibly at the correct size. @@ -750,7 +755,6 @@ namespace Tango.SharedUI.Controls catch { } } } - #endregion } } diff --git a/Software/Visual_Studio/Tango.SystemInfo/WMIReader.cs b/Software/Visual_Studio/Tango.SystemInfo/WMIReader.cs index d7d909089..2d8c95aae 100644 --- a/Software/Visual_Studio/Tango.SystemInfo/WMIReader.cs +++ b/Software/Visual_Studio/Tango.SystemInfo/WMIReader.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Text; using System.Management; +using System.Diagnostics; namespace Tango.SystemInfo { @@ -45,13 +46,16 @@ namespace Tango.SystemInfo Value = item[property].ToString() }); } - catch (SystemException) { /* ignore error */ } + catch (SystemException) + { + //Debug.WriteLine($"System Exception on {className}, {property}"); + } } } } catch (ManagementException e) { - /* Do Nothing */ + //Debug.WriteLine($"Management Exception on {className}"); } return hardwareList; diff --git a/Software/Visual_Studio/Tango.Transport/ITransporter.cs b/Software/Visual_Studio/Tango.Transport/ITransporter.cs index af80f6b1e..1187b2684 100644 --- a/Software/Visual_Studio/Tango.Transport/ITransporter.cs +++ b/Software/Visual_Studio/Tango.Transport/ITransporter.cs @@ -13,7 +13,7 @@ using System.Collections.ObjectModel; namespace Tango.Transport { - public delegate void RequestHandlerCallbackDelegate(Request request, String token); + public delegate void RequestHandlerCallbackDelegate(ITransporter transporter, Request request, String token); /// /// Represents a transportation engine which can send and receive message using a Transport adapter. diff --git a/Software/Visual_Studio/Tango.Transport/TransporterBase.cs b/Software/Visual_Studio/Tango.Transport/TransporterBase.cs index 3672baf63..11ce20b0a 100644 --- a/Software/Visual_Studio/Tango.Transport/TransporterBase.cs +++ b/Software/Visual_Studio/Tango.Transport/TransporterBase.cs @@ -37,7 +37,7 @@ namespace Tango.Transport private class RequestHandler { public Type RequestType { get; set; } - public Action Callback { get; set; } + public Action Callback { get; set; } } private const int MESSAGE_TOKEN_LENGTH = 36; @@ -316,7 +316,7 @@ namespace Tango.Transport { try { - handler.Callback.Invoke(request, container.Token); + handler.Callback.Invoke(this, request, container.Token); } catch { @@ -339,7 +339,7 @@ namespace Tango.Transport { try { - handler.Callback.Invoke(innerRequest, container.Token); + handler.Callback.Invoke(this, innerRequest, container.Token); } catch { @@ -1216,9 +1216,9 @@ namespace Tango.Transport { RequestHandler handler = new RequestHandler(); handler.RequestType = typeof(Request); - handler.Callback = (obj, token) => + handler.Callback = (transporter, obj, token) => { - callback?.Invoke(obj as Request, token); + callback?.Invoke(transporter, obj as Request, token); }; _requestHandlers.Add(handler); diff --git a/Software/Visual_Studio/Tango.WebRTC/Network/IceCandidateRequest.cs b/Software/Visual_Studio/Tango.WebRTC/Network/IceCandidateRequest.cs new file mode 100644 index 000000000..b65a8d6d4 --- /dev/null +++ b/Software/Visual_Studio/Tango.WebRTC/Network/IceCandidateRequest.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Tango.WebRTC.Network +{ + public class IceCandidateRequest + { + public IceCandidate IceCandidate { get; set; } + } +} diff --git a/Software/Visual_Studio/Tango.WebRTC/Network/IceCandidateResponse.cs b/Software/Visual_Studio/Tango.WebRTC/Network/IceCandidateResponse.cs new file mode 100644 index 000000000..a9daad732 --- /dev/null +++ b/Software/Visual_Studio/Tango.WebRTC/Network/IceCandidateResponse.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Tango.WebRTC.Network +{ + public class IceCandidateResponse + { + } +} diff --git a/Software/Visual_Studio/Tango.WebRTC/Network/OfferRequest.cs b/Software/Visual_Studio/Tango.WebRTC/Network/OfferRequest.cs new file mode 100644 index 000000000..0d82310db --- /dev/null +++ b/Software/Visual_Studio/Tango.WebRTC/Network/OfferRequest.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Tango.WebRTC.Network +{ + public class OfferRequest + { + public Offer Offer { get; set; } + } +} diff --git a/Software/Visual_Studio/Tango.WebRTC/Network/OfferResponse.cs b/Software/Visual_Studio/Tango.WebRTC/Network/OfferResponse.cs new file mode 100644 index 000000000..2207c31a6 --- /dev/null +++ b/Software/Visual_Studio/Tango.WebRTC/Network/OfferResponse.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Tango.WebRTC.Network +{ + public class OfferResponse + { + public Answer Answer { get; set; } + } +} diff --git a/Software/Visual_Studio/Tango.WebRTC/Tango.WebRTC.csproj b/Software/Visual_Studio/Tango.WebRTC/Tango.WebRTC.csproj index d9cea42dc..5edff1e71 100644 --- a/Software/Visual_Studio/Tango.WebRTC/Tango.WebRTC.csproj +++ b/Software/Visual_Studio/Tango.WebRTC/Tango.WebRTC.csproj @@ -48,11 +48,18 @@ + + + + + + + @@ -63,6 +70,14 @@ {a34ee0f0-649d-41c8-8489-b6f1cc6924ee} Tango.Core + + {BC932DBD-7CDB-488C-99E4-F02CF441F55E} + Tango.Logging + + + {74e700b0-1156-4126-be40-ee450d3c3026} + Tango.Transport + \ No newline at end of file diff --git a/Software/Visual_Studio/Tango.WebRTC/WebRtcTransportAdapter.cs b/Software/Visual_Studio/Tango.WebRTC/WebRtcTransportAdapter.cs new file mode 100644 index 000000000..850ddb3de --- /dev/null +++ b/Software/Visual_Studio/Tango.WebRTC/WebRtcTransportAdapter.cs @@ -0,0 +1,307 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Tango.Core; +using Tango.Core.Threading; +using Tango.Transport; +using Tango.Transport.Adapters; +using Tango.WebRTC.Network; + +namespace Tango.WebRTC +{ + public class WebRtcTransportAdapter : TransportAdapterBase + { + private WebRtcClient _client; + private bool _answerReceived; + private List _queuedIceCandidates; + + public event EventHandler Ready; + + public ITransporter SignalingTransporter { get; set; } + + public WebRtcTransportAdapterMode Mode { get; set; } + + public String DataChannelName { get; set; } + + public WebRtcTransportAdapter(ITransporter signalingTransporter, WebRtcTransportAdapterMode mode) : this(signalingTransporter, mode, null) + { + + } + + public WebRtcTransportAdapter(ITransporter signalingTransporter, WebRtcTransportAdapterMode mode, String dataChannelName) + { + SignalingTransporter = signalingTransporter; + Mode = mode; + DataChannelName = dataChannelName; + Address = dataChannelName; + ComponentName = $"WebRTC Adapter {_component_counter++}"; + + SignalingTransporter.RegisterRequestHandler(OnIceCandidateRequestReceived); + SignalingTransporter.RegisterRequestHandler(OnOfferRequestReceived); + } + + public override void Write(byte[] data, bool immidiate = false) + { + ThrowIfDisposed(); + + try + { + _client.SendBinary(data); + } + catch (Exception ex) + { + OnFailed(ex); + } + } + + public override Task Connect() + { + ThrowIfDisposed(); + + TaskCompletionSource completionSource = new TaskCompletionSource(); + bool completed = false; + + _queuedIceCandidates = new List(); + _answerReceived = false; + + ThreadFactory.StartNew(async () => + { + if (State != TransportComponentState.Connected) + { + try + { + _client = new WebRtcClient(); + + if (DataChannelName != null) + { + _client.DataChannelName = DataChannelName; + } + + Address = _client.DataChannelName; + + _client.NewIceCandidate += WebRtc_NewIceCandidate; + _client.Disconnected += WebRtc_Disconnected; + _client.BinaryMessageReceived += WebRtc_BinaryMessageReceived; + _client.Ready += (x, e) => + { + if (!completed) + { + LogManager.Log("WebRTC Active Transport Adapter is ready."); + completed = true; + State = TransportComponentState.Connected; + completionSource.SetResult(true); + Ready?.Invoke(this, new EventArgs()); + } + + if (Mode == WebRtcTransportAdapterMode.Passive) + { + LogManager.Log("WebRTC Passive Transport Adapter is ready."); + Ready?.Invoke(this, new EventArgs()); + } + }; + + LogManager.Log("Initializing WebRTC client..."); + await _client.Init(); + + if (Mode == WebRtcTransportAdapterMode.Active) + { + LogManager.Log("Creating WebRTC offer..."); + var offer = await _client.CreateOffer(); + + LogManager.Log("Sending WebRTC offer via signaling transporter..."); + var response = await SignalingTransporter.SendGenericRequest(new OfferRequest() { Offer = offer }, new TransportRequestConfig() + { + Timeout = TimeSpan.FromSeconds(30), + ShouldLog = true + }); + + LogManager.Log("WebRTC offer sent and responded with an answer. Setting WebRTC answer..."); + _client.SetAnswer(response.Answer); + _answerReceived = true; + + foreach (var ice in _queuedIceCandidates.ToList()) + { + LogManager.Log("Sending existing ice candidate..."); + + try + { + await SignalingTransporter.SendGenericRequest(new IceCandidateRequest() { IceCandidate = ice }, new TransportRequestConfig() + { + Timeout = TimeSpan.FromSeconds(30), + ShouldLog = true + }); + } + catch (Exception ex) + { + LogManager.Log(ex, "Error sending ice candidate."); + } + } + } + else + { + LogManager.Log("Waiting for offer..."); + completed = true; + State = TransportComponentState.Connected; + completionSource.SetResult(true); + } + } + catch (Exception ex) + { + completionSource.SetException(ex); + } + } + else + { + completionSource.SetResult(true); + } + }); + + if (Mode == WebRtcTransportAdapterMode.Active) + { + TimeoutTask.StartNew(() => + { + if (!completed) + { + completed = true; + completionSource.SetException(new TimeoutException("Could not reach the remote peer using the WebRTC adapter.")); + } + + }, TimeSpan.FromSeconds(30)); + } + + return completionSource.Task; + } + + private void WebRtc_BinaryMessageReceived(object sender, DataMessageReceivedEventArgs e) + { + OnDataAvailable(e.Data); + } + + private async void OnOfferRequestReceived(ITransporter transporter, OfferRequest request, string token) + { + if (Mode == WebRtcTransportAdapterMode.Passive) + { + var answer = await _client.CreateAnswer(request.Offer); + await SignalingTransporter.SendGenericResponse(new OfferResponse() { Answer = answer }, token); + _answerReceived = true; + + foreach (var ice in _queuedIceCandidates.ToList()) + { + LogManager.Log("Sending existing ice candidate..."); + + try + { + await SignalingTransporter.SendGenericRequest(new IceCandidateRequest() { IceCandidate = ice }, new TransportRequestConfig() + { + Timeout = TimeSpan.FromSeconds(30), + ShouldLog = true + }); + } + catch (Exception ex) + { + LogManager.Log(ex, "Error sending ice candidate to remote peer."); + } + } + } + } + + private async void WebRtc_NewIceCandidate(object sender, NewIceCandidateEventArgs e) + { + try + { + if (_answerReceived) + { + LogManager.Log("New WebRTC candidate available. Sending ice to remote peer..."); + + await SignalingTransporter.SendGenericRequest(new IceCandidateRequest() { IceCandidate = e.IceCandidate }, new TransportRequestConfig() + { + Timeout = TimeSpan.FromSeconds(30), + ShouldLog = true + }); + } + else + { + if (Mode == WebRtcTransportAdapterMode.Active) + { + LogManager.Log("New WebRTC candidate available. Will be sent after an answer is received..."); + } + else + { + LogManager.Log("New WebRTC candidate available. Will be sent after an offer is received..."); + } + + _queuedIceCandidates.Add(e.IceCandidate); + } + } + catch (Exception ex) + { + LogManager.Log(ex, "Error sending ice candidate to remote peer."); + } + } + + private async void OnIceCandidateRequestReceived(ITransporter transporter, IceCandidateRequest request, string token) + { + try + { + LogManager.Log("Ice candidate request received from the remote peer."); + await SignalingTransporter.SendGenericResponse(new IceCandidateResponse() { }, token); + + LogManager.Log("Adding ice candidate..."); + _client.AddIceCandidate(request.IceCandidate); + LogManager.Log("Ice candidate added."); + } + catch (Exception ex) + { + LogManager.Log(ex, "Error occurred on ice candidate received handling."); + } + } + + private void WebRtc_Disconnected(object sender, EventArgs e) + { + OnFailed(new WebRtcTransportAdapterDisconnectedException("WebRtc Transport Adapter RTC client has disconnected.")); + } + + public override Task Disconnect() + { + TaskCompletionSource completionSource = new TaskCompletionSource(); + + ThreadFactory.StartNew(() => + { + if (State != TransportComponentState.Disconnected) + { + if (_client != null) + { + LogManager.Log("Disposing WebRTC client..."); + + _client.NewIceCandidate -= WebRtc_NewIceCandidate; + _client.Disconnected -= WebRtc_Disconnected; + _client.BinaryMessageReceived -= WebRtc_BinaryMessageReceived; + + try + { + _client.Dispose(); + _client = null; + LogManager.Log("WebRTC client disposed."); + } + catch (Exception ex) + { + LogManager.Log(ex, "Error disposing WebRTC client."); + } + } + + State = TransportComponentState.Disconnected; + completionSource.SetResult(true); + } + else + { + completionSource.SetResult(true); + } + }); + + return completionSource.Task; + } + } +} diff --git a/Software/Visual_Studio/Tango.WebRTC/WebRtcTransportAdapterDisconnectedException.cs b/Software/Visual_Studio/Tango.WebRTC/WebRtcTransportAdapterDisconnectedException.cs new file mode 100644 index 000000000..bd82a3233 --- /dev/null +++ b/Software/Visual_Studio/Tango.WebRTC/WebRtcTransportAdapterDisconnectedException.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Tango.WebRTC +{ + public class WebRtcTransportAdapterDisconnectedException : Exception + { + public WebRtcTransportAdapterDisconnectedException(String message) : base(message) + { + + } + } +} diff --git a/Software/Visual_Studio/Tango.WebRTC/WebRtcTransportAdapterMode.cs b/Software/Visual_Studio/Tango.WebRTC/WebRtcTransportAdapterMode.cs new file mode 100644 index 000000000..8068697f5 --- /dev/null +++ b/Software/Visual_Studio/Tango.WebRTC/WebRtcTransportAdapterMode.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Tango.WebRTC +{ + public enum WebRtcTransportAdapterMode + { + Active, + Passive + } +} -- cgit v1.3.1 From 5a5b63afd5c4ff1d6a1dbd6996ed0a5a494387d0 Mon Sep 17 00:00:00 2001 From: Roy Ben Shabat Date: Sun, 22 Mar 2020 14:53:04 +0200 Subject: Working on storage provider. --- .../ViewModels/FileSystemViewVM.cs | 8 +- .../Controls/FileSystemControl.xaml | 4 +- .../FSE/Tango.FSE.Common/FSEViewModel.cs | 7 + .../Tango.FSE.Common/Storage/IStorageProvider.cs | 36 ++ .../FSE/Tango.FSE.Common/Storage/IStorageResult.cs | 13 + .../Tango.FSE.Common/Storage/MultiStorageResult.cs | 24 ++ .../Storage/SingleStorageResult.cs | 19 + .../FSE/Tango.FSE.Common/Tango.FSE.Common.csproj | 4 + .../Tango.FSE.UI/Storage/DefaultStorageProvider.cs | 389 +++++++++++++++++++++ .../FSE/Tango.FSE.UI/Tango.FSE.UI.csproj | 8 + .../FSE/Tango.FSE.UI/ViewModelLocator.cs | 4 + .../FSE/Tango.FSE.UI/ViewModels/LoginViewVM.cs | 5 + .../FSE/Tango.FSE.UI/Views/MainView.xaml | 178 ++++++++++ .../FSE/Tango.FSE.UI/Views/MainView.xaml.cs | 13 + .../Visual_Studio/FSE/Tango.FSE.UI/packages.config | 2 + .../Tango.FileSystem/FileSystemManager.cs | 22 ++ 16 files changed, 732 insertions(+), 4 deletions(-) create mode 100644 Software/Visual_Studio/FSE/Tango.FSE.Common/Storage/IStorageProvider.cs create mode 100644 Software/Visual_Studio/FSE/Tango.FSE.Common/Storage/IStorageResult.cs create mode 100644 Software/Visual_Studio/FSE/Tango.FSE.Common/Storage/MultiStorageResult.cs create mode 100644 Software/Visual_Studio/FSE/Tango.FSE.Common/Storage/SingleStorageResult.cs create mode 100644 Software/Visual_Studio/FSE/Tango.FSE.UI/Storage/DefaultStorageProvider.cs (limited to 'Software/Visual_Studio/Tango.FileSystem/FileSystemManager.cs') 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 f074294b2..4e2ca1882 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 @@ -336,9 +336,13 @@ namespace Tango.FSE.PPCConsole.ViewModels } } - private void DownloadSelectedItems(List items) + private async void DownloadSelectedItems(List items) { - + var result = await StorageProvider.SelectFolder("Select download destination folder"); + if (result) + { + Debug.WriteLine($"Download to {result.SelectedItem}"); + } } private async void RenameFileSystemItem(FileSystemItem item) 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 62de7cf48..fcea26001 100644 --- a/Software/Visual_Studio/FSE/Tango.FSE.Common/Controls/FileSystemControl.xaml +++ b/Software/Visual_Studio/FSE/Tango.FSE.Common/Controls/FileSystemControl.xaml @@ -93,7 +93,7 @@ - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Devices + + + + + + + + + + + Computer + + Desktop + Documents + Application Data + + + + + + + + + + + + + + + + + + + diff --git a/Software/Visual_Studio/FSE/Tango.FSE.UI/Views/MainView.xaml.cs b/Software/Visual_Studio/FSE/Tango.FSE.UI/Views/MainView.xaml.cs index 3d29032b1..b98ad1a52 100644 --- a/Software/Visual_Studio/FSE/Tango.FSE.UI/Views/MainView.xaml.cs +++ b/Software/Visual_Studio/FSE/Tango.FSE.UI/Views/MainView.xaml.cs @@ -21,6 +21,7 @@ namespace Tango.FSE.UI.Views public partial class MainView : UserControl { private UIElement _previousFocusedElement; + private object _lastSelectedItem; public static MainView Instance { get; set; } @@ -105,5 +106,17 @@ namespace Tango.FSE.UI.Views e.Handled = true; } } + + private void ListView_SelectionChanged(object sender, SelectionChangedEventArgs e) + { + if (listView.SelectedItem != null) + { + _lastSelectedItem = listView.SelectedItem; + } + else + { + listView.SelectedItem = _lastSelectedItem; + } + } } } diff --git a/Software/Visual_Studio/FSE/Tango.FSE.UI/packages.config b/Software/Visual_Studio/FSE/Tango.FSE.UI/packages.config index c795da787..fda2f4d3f 100644 --- a/Software/Visual_Studio/FSE/Tango.FSE.UI/packages.config +++ b/Software/Visual_Studio/FSE/Tango.FSE.UI/packages.config @@ -14,4 +14,6 @@ + + \ No newline at end of file diff --git a/Software/Visual_Studio/Tango.FileSystem/FileSystemManager.cs b/Software/Visual_Studio/Tango.FileSystem/FileSystemManager.cs index b8e59c322..c18df97c9 100644 --- a/Software/Visual_Studio/Tango.FileSystem/FileSystemManager.cs +++ b/Software/Visual_Studio/Tango.FileSystem/FileSystemManager.cs @@ -92,6 +92,28 @@ namespace Tango.FileSystem }; } + public Task GetFolder(String path) + { + return Task.Factory.StartNew(() => + { + return FileSystemItem.FromDTO(GetFolder(new GetFileSystemItemRequest() + { + Path = path, + })); + }); + } + + public Task GetFolder(Environment.SpecialFolder specialFolder) + { + return Task.Factory.StartNew(() => + { + return FileSystemItem.FromDTO(GetFolder(new GetFileSystemItemRequest() + { + SpecialFolder = specialFolder + })); + }); + } + public void Delete(String path) { if (Directory.Exists(path)) -- cgit v1.3.1 From 912e8423fbf63a025dac977d5749e60d4c57df68 Mon Sep 17 00:00:00 2001 From: Roy Ben Shabat Date: Tue, 24 Mar 2020 18:31:31 +0200 Subject: Working on storage. --- .../Controls/FileSystemControl.xaml | 11 +++-- .../Tango.FSE.Common/Storage/IStorageProvider.cs | 5 +++ .../FSE/Tango.FSE.Common/Storage/StorageMode.cs | 16 +++++++ .../FSE/Tango.FSE.Common/Tango.FSE.Common.csproj | 1 + .../Tango.FSE.UI/Storage/DefaultStorageProvider.cs | 50 ++++++++++++++++++--- .../FSE/Tango.FSE.UI/ViewModels/LoginViewVM.cs | 4 +- .../FSE/Tango.FSE.UI/Views/MainView.xaml | 4 +- .../Tango.FileSystem/FileExplorerControl.cs | 51 ++++++++++++++-------- .../Tango.FileSystem/FileSystemManager.cs | 27 +++++++----- .../Network/GetFileSystemItemRequest.cs | 2 + 10 files changed, 131 insertions(+), 40 deletions(-) create mode 100644 Software/Visual_Studio/FSE/Tango.FSE.Common/Storage/StorageMode.cs (limited to 'Software/Visual_Studio/Tango.FileSystem/FileSystemManager.cs') 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 fcea26001..eb1007609 100644 --- a/Software/Visual_Studio/FSE/Tango.FSE.Common/Controls/FileSystemControl.xaml +++ b/Software/Visual_Studio/FSE/Tango.FSE.Common/Controls/FileSystemControl.xaml @@ -93,7 +93,7 @@ - + - + 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 00ec312a5..602e6057c 100644 --- a/Software/Visual_Studio/FSE/Tango.FSE.Common/Storage/IStorageProvider.cs +++ b/Software/Visual_Studio/FSE/Tango.FSE.Common/Storage/IStorageProvider.cs @@ -4,6 +4,7 @@ using System.Collections.ObjectModel; using System.Linq; using System.Text; using System.Threading.Tasks; +using System.Windows.Controls; using Tango.Core.Commands; using Tango.FileSystem; @@ -19,6 +20,10 @@ namespace Tango.FSE.Common.Storage FileSystemItem CurrentItem { get; set; } String CurrentPath { get; set; } ObservableCollection SelectedItems { get; set; } + FileSystemItem SelectedItem { get; set; } + SelectionMode SelectionMode { get; set; } + StorageMode StorageMode { get; set; } + String Filter { get; set; } List Drives { get; } RelayCommand OKCommand { get; } RelayCommand CancelCommand { get; } diff --git a/Software/Visual_Studio/FSE/Tango.FSE.Common/Storage/StorageMode.cs b/Software/Visual_Studio/FSE/Tango.FSE.Common/Storage/StorageMode.cs new file mode 100644 index 000000000..d2d519b84 --- /dev/null +++ b/Software/Visual_Studio/FSE/Tango.FSE.Common/Storage/StorageMode.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Tango.FSE.Common.Storage +{ + public enum StorageMode + { + OpenFile, + OpenFiles, + SaveFile, + SelectFolder, + } +} 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 8c4165245..ce56e7f69 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 @@ -144,6 +144,7 @@ + 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 c37400cf6..9a40888a3 100644 --- a/Software/Visual_Studio/FSE/Tango.FSE.UI/Storage/DefaultStorageProvider.cs +++ b/Software/Visual_Studio/FSE/Tango.FSE.UI/Storage/DefaultStorageProvider.cs @@ -8,6 +8,7 @@ using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; +using System.Windows.Controls; using System.Windows.Input; using Tango.Core; using Tango.Core.Commands; @@ -65,6 +66,8 @@ namespace Tango.FSE.UI.Storage set { _currentPath = value; RaisePropertyChangedAuto(); } } + public String Filter { get; set; } = "*.*"; + private bool _isBusy; public bool IsBusy { @@ -81,6 +84,27 @@ namespace Tango.FSE.UI.Storage public ObservableCollection SelectedItems { get; set; } + private FileSystemItem _selectedItem; + public FileSystemItem SelectedItem + { + get { return _selectedItem; } + set { _selectedItem = value; RaisePropertyChangedAuto(); InvalidateRelayCommands(); } + } + + private SelectionMode _selectionMode; + public SelectionMode SelectionMode + { + get { return _selectionMode; } + set { _selectionMode = value; RaisePropertyChangedAuto(); } + } + + private StorageMode _storageMode; + public StorageMode StorageMode + { + get { return _storageMode; } + set { _storageMode = value; RaisePropertyChangedAuto(); } + } + public RelayCommand OKCommand { get; } public RelayCommand CancelCommand { get; } public RelayCommand BackCommand { get; set; } @@ -99,7 +123,7 @@ namespace Tango.FSE.UI.Storage BackCommand = new RelayCommand(NavigateBack, () => !(CurrentItem is FolderItem) || !(CurrentItem as FolderItem).IsRoot); NavigateSpecialFolderCommand = new RelayCommand(NavigateToSpecialFolder); NavigateToFolderCommand = new RelayCommand(async (x) => await Navigate(x)); - OKCommand = new RelayCommand(OnAccept); + OKCommand = new RelayCommand(OnAccept, CanOK); CancelCommand = new RelayCommand(OnCancel); } @@ -152,7 +176,11 @@ namespace Tango.FSE.UI.Storage var root = await _manager.GetFolder(String.Empty); Drives = (root as IFileSystemContainer).Items.Cast().ToList(); - CurrentItem = await _manager.GetFolder(Environment.CurrentDirectory); + StorageMode = StorageMode.SelectFolder; + SelectionMode = SelectionMode.Single; + + await Navigate(Environment.CurrentDirectory); + IsOpened = true; _okCompletionAction = () => @@ -160,7 +188,7 @@ namespace Tango.FSE.UI.Storage SingleStorageResult result = new SingleStorageResult() { Confirmed = true, - SelectedItem = SelectedItems.First().Path, + SelectedItem = CurrentItem.Path, }; _completionSource.SetResult(result); @@ -237,15 +265,15 @@ namespace Tango.FSE.UI.Storage if (path != null) { - CurrentItem = await _manager.GetFolder(path) as FileSystemItem; + CurrentItem = await _manager.GetFolder(path, StorageMode == StorageMode.SelectFolder, Filter) as FileSystemItem; } else if (specialFolder != null) { - CurrentItem = await _manager.GetFolder(specialFolder.Value) as FileSystemItem; + CurrentItem = await _manager.GetFolder(specialFolder.Value, StorageMode == StorageMode.SelectFolder, Filter) as FileSystemItem; } else { - CurrentItem = await _manager.GetFolder(String.Empty) as FileSystemItem; + CurrentItem = await _manager.GetFolder(String.Empty, StorageMode == StorageMode.SelectFolder, Filter) as FileSystemItem; } } catch (Exception ex) @@ -267,6 +295,16 @@ namespace Tango.FSE.UI.Storage InvalidateRelayCommands(); } + private bool CanOK() + { + if (StorageMode == StorageMode.SelectFolder && CurrentItem.Type == FileSystemItemType.Drive) + { + return false; + } + + return true; + } + #endregion #region Native diff --git a/Software/Visual_Studio/FSE/Tango.FSE.UI/ViewModels/LoginViewVM.cs b/Software/Visual_Studio/FSE/Tango.FSE.UI/ViewModels/LoginViewVM.cs index 8b14ac69e..8622daab2 100644 --- a/Software/Visual_Studio/FSE/Tango.FSE.UI/ViewModels/LoginViewVM.cs +++ b/Software/Visual_Studio/FSE/Tango.FSE.UI/ViewModels/LoginViewVM.cs @@ -131,9 +131,9 @@ namespace Tango.FSE.UI.ViewModels try { - //var s = await StorageProvider.SelectFolder("Select download destination folder"); + var s = await StorageProvider.SelectFolder("Select download destination folder"); - //return; + return; if (!Validate()) { 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 6a386947f..6941b830a 100644 --- a/Software/Visual_Studio/FSE/Tango.FSE.UI/Views/MainView.xaml +++ b/Software/Visual_Studio/FSE/Tango.FSE.UI/Views/MainView.xaml @@ -233,7 +233,7 @@ - + @@ -341,6 +341,8 @@ Mode="{Binding ElementName=listView,Path=SelectedItem.Tag}" CurrentItem="{Binding StorageProvider.CurrentItem,Mode=TwoWay}" SelectedItems="{Binding StorageProvider.SelectedItems}" + SelectedItem="{Binding StorageProvider.SelectedItem}" + SelectionMode="{Binding StorageProvider.SelectionMode}" BackCommand="{Binding StorageProvider.BackCommand}" ItemDoubleClickedCommand="{Binding StorageProvider.OpenCommand}" OpenCommand="{Binding StorageProvider.OpenCommand}"/> diff --git a/Software/Visual_Studio/Tango.FileSystem/FileExplorerControl.cs b/Software/Visual_Studio/Tango.FileSystem/FileExplorerControl.cs index 60061780b..c624178a0 100644 --- a/Software/Visual_Studio/Tango.FileSystem/FileExplorerControl.cs +++ b/Software/Visual_Studio/Tango.FileSystem/FileExplorerControl.cs @@ -265,6 +265,15 @@ namespace Tango.FileSystem public static readonly DependencyProperty AllowDragProperty = DependencyProperty.Register("AllowDrag", typeof(bool), typeof(FileExplorerControl), new PropertyMetadata(null)); + public SelectionMode SelectionMode + { + get { return (SelectionMode)GetValue(SelectionModeProperty); } + set { SetValue(SelectionModeProperty, value); } + } + public static readonly DependencyProperty SelectionModeProperty = + DependencyProperty.Register("SelectionMode", typeof(SelectionMode), typeof(FileExplorerControl), new PropertyMetadata(SelectionMode.Extended)); + + static FileExplorerControl() { DefaultStyleKeyProperty.OverrideMetadata(typeof(FileExplorerControl), new FrameworkPropertyMetadata(typeof(FileExplorerControl))); @@ -492,39 +501,45 @@ namespace Tango.FileSystem private void _datagrid_SelectionChanged(object sender, SelectionChangedEventArgs e) { - if (!_preventSynchronization) + if (SelectionMode == SelectionMode.Extended) { - _preventSynchronization = true; + if (!_preventSynchronization) + { + _preventSynchronization = true; - _listBox.SelectedItems.Clear(); + _listBox.SelectedItems.Clear(); - foreach (var item in _datagrid.SelectedItems) - { - _listBox.SelectedItems.Add(item); - } + foreach (var item in _datagrid.SelectedItems) + { + _listBox.SelectedItems.Add(item); + } - SynchronizeSelectedItems(_listBox.SelectedItems); + SynchronizeSelectedItems(_listBox.SelectedItems); - _preventSynchronization = false; + _preventSynchronization = false; + } } } private void _listBox_SelectionChanged(object sender, SelectionChangedEventArgs e) { - if (!_preventSynchronization) + if (SelectionMode == SelectionMode.Extended) { - _preventSynchronization = true; + if (!_preventSynchronization) + { + _preventSynchronization = true; - _datagrid.SelectedItems.Clear(); + _datagrid.SelectedItems.Clear(); - foreach (var item in _listBox.SelectedItems) - { - _datagrid.SelectedItems.Add(item); - } + foreach (var item in _listBox.SelectedItems) + { + _datagrid.SelectedItems.Add(item); + } - SynchronizeSelectedItems(_listBox.SelectedItems); + SynchronizeSelectedItems(_listBox.SelectedItems); - _preventSynchronization = false; + _preventSynchronization = false; + } } } diff --git a/Software/Visual_Studio/Tango.FileSystem/FileSystemManager.cs b/Software/Visual_Studio/Tango.FileSystem/FileSystemManager.cs index c18df97c9..f72785f81 100644 --- a/Software/Visual_Studio/Tango.FileSystem/FileSystemManager.cs +++ b/Software/Visual_Studio/Tango.FileSystem/FileSystemManager.cs @@ -73,15 +73,18 @@ namespace Tango.FileSystem }); } - foreach (var file in Directory.GetFiles(request.Path)) + if (!request.FoldersOnly) { - items.Add(new FileSystemItemDTO() + foreach (var file in Directory.GetFiles(request.Path, request.Filter != null ? request.Filter : "*.*")) { - Path = file, - Type = FileSystemItemType.File, - DateModified = File.GetLastWriteTimeUtc(file), - Size = new FileInfo(file).Length - }); + items.Add(new FileSystemItemDTO() + { + Path = file, + Type = FileSystemItemType.File, + DateModified = File.GetLastWriteTimeUtc(file), + Size = new FileInfo(file).Length + }); + } } return new FileSystemItemDTO() @@ -92,24 +95,28 @@ namespace Tango.FileSystem }; } - public Task GetFolder(String path) + public Task GetFolder(String path, bool foldersOnly = false, String filter = "*.*") { return Task.Factory.StartNew(() => { return FileSystemItem.FromDTO(GetFolder(new GetFileSystemItemRequest() { Path = path, + FoldersOnly = foldersOnly, + Filter = filter, })); }); } - public Task GetFolder(Environment.SpecialFolder specialFolder) + public Task GetFolder(Environment.SpecialFolder specialFolder, bool foldersOnly = false, String filter = "*.*") { return Task.Factory.StartNew(() => { return FileSystemItem.FromDTO(GetFolder(new GetFileSystemItemRequest() { - SpecialFolder = specialFolder + SpecialFolder = specialFolder, + FoldersOnly = foldersOnly, + Filter = filter, })); }); } diff --git a/Software/Visual_Studio/Tango.FileSystem/Network/GetFileSystemItemRequest.cs b/Software/Visual_Studio/Tango.FileSystem/Network/GetFileSystemItemRequest.cs index 1ed6c19e4..3cf91e869 100644 --- a/Software/Visual_Studio/Tango.FileSystem/Network/GetFileSystemItemRequest.cs +++ b/Software/Visual_Studio/Tango.FileSystem/Network/GetFileSystemItemRequest.cs @@ -11,5 +11,7 @@ namespace Tango.FileSystem.Network { public String Path { get; set; } public SpecialFolder? SpecialFolder { get; set; } + public bool FoldersOnly { get; set; } + public String Filter { get; set; } = "*.*"; } } -- cgit v1.3.1 From 42c06402ff6648c356fba8315958283762ed2542 Mon Sep 17 00:00:00 2001 From: Roy Ben Shabat Date: Wed, 25 Mar 2020 00:26:47 +0200 Subject: Added Download menu implementation to file system. Added several stun and turn servers to web rtc. --- .../ViewModels/FileSystemViewVM.cs | 53 ++++++++++++++++++++++ .../Tango.FSE.PPCConsole/Views/FileSystemView.xaml | 17 ++++++- .../Controls/FileSystemControl.xaml | 11 ++++- .../FileSystem/IFileSystemProvider.cs | 1 + .../FileSystem/DefaultFileSystemProvider.cs | 11 +++++ .../Tango.FSE.UI/Storage/DefaultStorageProvider.cs | 2 +- .../FSE/Tango.FSE.UI/ViewModels/LoginViewVM.cs | 4 +- .../FileSystem/DefaultFileSystemService.cs | 14 ++++++ .../Visual_Studio/PPC/Tango.PPC.UI/app.manifest | 2 +- .../Tango.FileSystem/FileExplorerControl.cs | 53 ++++++++++++++++++++-- .../Tango.FileSystem/FileSystemManager.cs | 17 +++++++ .../Network/CreateFolderRequest.cs | 14 ++++++ .../Network/CreateFolderResponse.cs | 13 ++++++ .../Tango.FileSystem/Tango.FileSystem.csproj | 2 + .../Visual_Studio/Tango.WebRTC/WebRtcClient.cs | 17 +++++++ 15 files changed, 219 insertions(+), 12 deletions(-) create mode 100644 Software/Visual_Studio/Tango.FileSystem/Network/CreateFolderRequest.cs create mode 100644 Software/Visual_Studio/Tango.FileSystem/Network/CreateFolderResponse.cs (limited to 'Software/Visual_Studio/Tango.FileSystem/FileSystemManager.cs') 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 4e2ca1882..e10cc0ad1 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 @@ -58,6 +58,7 @@ namespace Tango.FSE.PPCConsole.ViewModels public RelayCommand> CutPasteCommand { get; set; } public RelayCommand> DownloadCommand { get; set; } public RelayCommand RenameCommand { get; set; } + public RelayCommand NewFolderCommand { get; set; } public FileSystemViewVM() @@ -78,6 +79,7 @@ namespace Tango.FSE.PPCConsole.ViewModels CutPasteCommand = new RelayCommand>((items) => PasteItems(items, true)); DownloadCommand = new RelayCommand>(DownloadSelectedItems); RenameCommand = new RelayCommand(RenameFileSystemItem); + NewFolderCommand = new RelayCommand(CreateNewFolder); } private async void NavigateBack() @@ -341,7 +343,24 @@ namespace Tango.FSE.PPCConsole.ViewModels var result = await StorageProvider.SelectFolder("Select download destination folder"); if (result) { + + String destination = result.SelectedItem; + Debug.WriteLine($"Download to {result.SelectedItem}"); + + foreach (var item in items.Where(x => x.Type != FileSystemItemType.Drive)) + { + if (File.Exists(Path.Combine(destination, item.Name)) || Directory.Exists(Path.Combine(destination, item.Name))) + { + if (!await NotificationProvider.ShowWarningQuestion($"'{item.Name}' already exists on '{Path.GetDirectoryName(destination)}'. Do you want to overwrite?")) + { + continue; + } + } + + var handler = await FileSystemProvider.Download(item, destination); + FileSystemHandlers.Insert(0, handler); + } } } @@ -376,6 +395,40 @@ namespace Tango.FSE.PPCConsole.ViewModels } } + private async void CreateNewFolder() + { + if (CurrentItem == null) return; + if (CurrentItem is FolderItem) + { + if ((CurrentItem as FolderItem).IsRoot) return; + } + + var result = await NotificationProvider.ShowInputBox( + "New Folder", + $"Please enter a folder name and press 'ENTER'.", + PackIconKind.FolderAdd, "untitled", + "folder name", + 100, + "CREATE"); + + if (result.Confirmed) + { + try + { + using (NotificationProvider.PushTaskItem("Creating new folder...")) + { + var folderItem = await FileSystemProvider.CreateFolder(CurrentItem, result.Input); + NavigateToCurrentPath(); //Instead of inserting folder item just refresh the current path... + } + } + catch (Exception ex) + { + LogManager.Log(ex, $"Error creating new folder '{Path.Combine(CurrentItem.Path,result.Input)}."); + await NotificationProvider.ShowError($"Error creating folder '{result.Input}'.\n{ex.FlattenMessage()}"); + } + } + } + private void OnCurrentItemChanged() { CurrentPath = CurrentItem.Path; 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 d1143ede4..67f1dc1c5 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 @@ -121,6 +121,7 @@ + RenameCommand="{Binding RenameCommand}" + NewFolderCommand="{Binding NewFolderCommand}"/> @@ -163,7 +165,18 @@ - + + + + + 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 eb1007609..5bc75ca54 100644 --- a/Software/Visual_Studio/FSE/Tango.FSE.Common/Controls/FileSystemControl.xaml +++ b/Software/Visual_Studio/FSE/Tango.FSE.Common/Controls/FileSystemControl.xaml @@ -49,6 +49,13 @@ + + + + + + + @@ -93,7 +100,7 @@ - + - +