From 14d0b3383d04e102029f41661c192e77e22bfd98 Mon Sep 17 00:00:00 2001 From: Roy Ben Shabat Date: Wed, 18 Mar 2020 04:46:26 +0200 Subject: Added support for FSE mouse gestures via WebRTC. Added SendBinaryData via WebRtcClient. --- .../FSE/Modules/Tango.FSE.PPCConsole/ViewModels/RemoteDesktopViewVM.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/ViewModels/RemoteDesktopViewVM.cs') 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 f2ed84d8a..7fab5efd2 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 @@ -106,7 +106,7 @@ namespace Tango.FSE.PPCConsole.ViewModels public void OnMouseMove(System.Windows.Point point, System.Windows.Size size) { - //RemoteDesktopProvider.MouseMove(point, size); + RemoteDesktopProvider.MouseMove(point, size); } public void OnMouseDoubleClick(MouseButton changedButton, System.Windows.Point point, System.Windows.Size size) -- cgit v1.3.1 From 9ed4b017e5e033377be6ebffffc87c087a791b39 Mon Sep 17 00:00:00 2001 From: Roy Ben Shabat Date: Thu, 19 Mar 2020 02:15:56 +0200 Subject: Improved monitoring. --- .../ViewModels/MonitoringViewVM.cs | 2 +- .../ViewModels/RemoteDesktopViewVM.cs | 17 +++++++++-- .../Tango.FSE.PPCConsole/Views/MonitoringView.xaml | 2 +- .../Views/RemoteDesktopView.xaml | 6 ++-- .../Views/RemoteDesktopView.xaml.cs | 2 ++ .../RemoteDesktop/DefaultRemoteDesktopProvider.cs | 35 ++++++++++++++++++---- .../SystemInfo/DefaultSystemInfoProvider.cs | 12 +++++++- .../Network/RemoteDesktopCommand.cs | 14 +++++++++ .../Network/RemoteDesktopCommandRequest.cs | 13 ++++++++ .../Network/RemoteDesktopResponse.cs | 12 ++++++++ .../Tango.RemoteDesktop/Tango.RemoteDesktop.csproj | 3 ++ 11 files changed, 105 insertions(+), 13 deletions(-) create mode 100644 Software/Visual_Studio/Tango.RemoteDesktop/Network/RemoteDesktopCommand.cs create mode 100644 Software/Visual_Studio/Tango.RemoteDesktop/Network/RemoteDesktopCommandRequest.cs create mode 100644 Software/Visual_Studio/Tango.RemoteDesktop/Network/RemoteDesktopResponse.cs (limited to 'Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/ViewModels/RemoteDesktopViewVM.cs') diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/ViewModels/MonitoringViewVM.cs b/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/ViewModels/MonitoringViewVM.cs index 54282ebf2..eb59b2c9a 100644 --- a/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/ViewModels/MonitoringViewVM.cs +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/ViewModels/MonitoringViewVM.cs @@ -187,7 +187,7 @@ namespace Tango.FSE.PPCConsole.ViewModels private async void LoadSystemInformation() { - if (SystemInfo == null) + if (SystemInfo == null && !FetchingSystemInfo) { try { 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 7fab5efd2..7f3a95f47 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 @@ -30,10 +30,13 @@ namespace Tango.FSE.PPCConsole.ViewModels public RelayCommand StartCommand { get; set; } public RelayCommand StopCommand { get; set; } + public RelayCommand OpenTaskManagerCommand { get; set; } + public RemoteDesktopViewVM() { StartCommand = new RelayCommand(StartRemoteDesktop); StopCommand = new RelayCommand(StopRemoteDesktop); + OpenTaskManagerCommand = new RelayCommand(OpenTaskManager); } public override void OnApplicationStarted() @@ -116,12 +119,22 @@ namespace Tango.FSE.PPCConsole.ViewModels public void OnKeyboardDown(Key key, bool ctrlDown, bool shitDown, bool altDown) { - throw new NotImplementedException(); + RemoteDesktopProvider.KeyboardDown(key, ctrlDown, shitDown, altDown); } public void OnKeyboardUp(Key key, bool ctrlDown, bool shitDown, bool altDown) { - throw new NotImplementedException(); + RemoteDesktopProvider.KeyboardUp(key, ctrlDown, shitDown, altDown); + } + + #endregion + + #region Remote Actions + + private void OpenTaskManager() + { + RemoteDesktopProvider.KeyboardDown(Key.Escape, true, true, false); + RemoteDesktopProvider.KeyboardDown(Key.Escape, false, false, false); } #endregion diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/Views/MonitoringView.xaml b/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/Views/MonitoringView.xaml index 79077d0b9..1c3ee1067 100644 --- a/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/Views/MonitoringView.xaml +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/Views/MonitoringView.xaml @@ -175,7 +175,7 @@ - + : diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/Views/RemoteDesktopView.xaml b/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/Views/RemoteDesktopView.xaml index 644bfabba..a37b7b299 100644 --- a/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/Views/RemoteDesktopView.xaml +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/Views/RemoteDesktopView.xaml @@ -11,7 +11,7 @@ xmlns:console="clr-namespace:Tango.Console;assembly=Tango.Console" xmlns:material="http://materialdesigninxaml.net/winfx/xaml/themes" mc:Ignorable="d" - d:DesignHeight="720" d:DesignWidth="1280" d:DataContext="{d:DesignInstance Type=vm:RemoteDesktopViewVM, IsDesignTimeCreatable=False}" DataContext="{x:Static global:ViewModelLocator.RemoteDesktopViewVM}" Foreground="{StaticResource FSE_PrimaryForegroundBrush}"> + d:DesignHeight="720" d:DesignWidth="1280" d:DataContext="{d:DesignInstance Type=vm:RemoteDesktopViewVM, IsDesignTimeCreatable=False}" DataContext="{x:Static global:ViewModelLocator.RemoteDesktopViewVM}" Foreground="{StaticResource FSE_PrimaryForegroundBrush}" Focusable="False"> @@ -35,7 +35,7 @@ - + @@ -173,7 +173,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