aboutsummaryrefslogtreecommitdiffstats
path: root/Software
diff options
context:
space:
mode:
authorRoy Ben Shabat <Roy.mail.net@gmail.com>2020-03-22 00:04:44 +0200
committerRoy Ben Shabat <Roy.mail.net@gmail.com>2020-03-22 00:04:44 +0200
commitd48b2d23515d06a21ad241380986bf8f31773195 (patch)
treeebbb6b2bc874773ec58a4c999a1f6eb61a572592 /Software
parent8c094ceeaa538fdb5dc1d69b6ac73f8574cecb66 (diff)
downloadTango-d48b2d23515d06a21ad241380986bf8f31773195.tar.gz
Tango-d48b2d23515d06a21ad241380986bf8f31773195.zip
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.
Diffstat (limited to 'Software')
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/ViewModels/FileSystemViewVM.cs146
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/ViewModels/RemoteDesktopViewVM.cs6
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/Views/FileSystemView.xaml61
-rw-r--r--Software/Visual_Studio/FSE/Tango.FSE.Common/Connection/MachineConnectedEventArgs.cs1
-rw-r--r--Software/Visual_Studio/FSE/Tango.FSE.Common/Controls/FileSystemControl.xaml72
-rw-r--r--Software/Visual_Studio/FSE/Tango.FSE.Common/FileSystem/FileSystemHandler.cs28
-rw-r--r--Software/Visual_Studio/FSE/Tango.FSE.Common/FileSystem/IFileSystemProvider.cs6
-rw-r--r--Software/Visual_Studio/FSE/Tango.FSE.Common/Notifications/INotificationProvider.cs26
-rw-r--r--Software/Visual_Studio/FSE/Tango.FSE.Common/Notifications/InputBoxResult.cs14
-rw-r--r--Software/Visual_Studio/FSE/Tango.FSE.Common/Notifications/InputBoxVM.cs34
-rw-r--r--Software/Visual_Studio/FSE/Tango.FSE.Common/Tango.FSE.Common.csproj2
-rw-r--r--Software/Visual_Studio/FSE/Tango.FSE.UI/Connection/DefaultMachineProvider.cs5
-rw-r--r--Software/Visual_Studio/FSE/Tango.FSE.UI/FileSystem/DefaultFileSystemProvider.cs195
-rw-r--r--Software/Visual_Studio/FSE/Tango.FSE.UI/Navigation/DefaultNavigationManager.cs13
-rw-r--r--Software/Visual_Studio/FSE/Tango.FSE.UI/Notifications/DefaultNotificationProvider.cs65
-rw-r--r--Software/Visual_Studio/FSE/Tango.FSE.UI/RemoteDesktop/DefaultRemoteDesktopProvider.cs2
-rw-r--r--Software/Visual_Studio/FSE/Tango.FSE.UI/SystemInfo/DefaultSystemInfoProvider.cs2
-rw-r--r--Software/Visual_Studio/FSE/Tango.FSE.UI/ViewModels/LayoutViewVM.cs6
-rw-r--r--Software/Visual_Studio/FSE/Tango.FSE.UI/Views/LayoutView.xaml2
-rw-r--r--Software/Visual_Studio/FSE/Tango.FSE.UI/Views/MainView.xaml71
-rw-r--r--Software/Visual_Studio/FSE/Tango.FSE.UI/Views/MainView.xaml.cs45
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/FileSystem/DefaultFileSystemService.cs117
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/FileSystem/IFileSystemService.cs1
-rw-r--r--Software/Visual_Studio/SideChains/WebRtc.NET/src/conductor.cc2
-rw-r--r--Software/Visual_Studio/Tango.FileSystem/FileExplorerControl.cs380
-rw-r--r--Software/Visual_Studio/Tango.FileSystem/FileSystemItem.cs10
-rw-r--r--Software/Visual_Studio/Tango.FileSystem/FileSystemManager.cs46
-rw-r--r--Software/Visual_Studio/Tango.FileSystem/Network/CopyRequest.cs14
-rw-r--r--Software/Visual_Studio/Tango.FileSystem/Network/CopyResponse.cs12
-rw-r--r--Software/Visual_Studio/Tango.FileSystem/Network/DeleteRequest.cs13
-rw-r--r--Software/Visual_Studio/Tango.FileSystem/Network/DeleteResponse.cs12
-rw-r--r--Software/Visual_Studio/Tango.FileSystem/Network/InitWebRtcRequest.cs13
-rw-r--r--Software/Visual_Studio/Tango.FileSystem/Network/InitWebRtcResponse.cs12
-rw-r--r--Software/Visual_Studio/Tango.FileSystem/Network/MoveRequest.cs14
-rw-r--r--Software/Visual_Studio/Tango.FileSystem/Network/MoveResponse.cs12
-rw-r--r--Software/Visual_Studio/Tango.FileSystem/Tango.FileSystem.csproj8
-rw-r--r--Software/Visual_Studio/Tango.FileSystem/Themes/Generic.xaml17
-rw-r--r--Software/Visual_Studio/Tango.SharedUI/Controls/NavigationControl.cs6
-rw-r--r--Software/Visual_Studio/Tango.SystemInfo/WMIReader.cs8
-rw-r--r--Software/Visual_Studio/Tango.Transport/ITransporter.cs2
-rw-r--r--Software/Visual_Studio/Tango.Transport/TransporterBase.cs10
-rw-r--r--Software/Visual_Studio/Tango.WebRTC/Network/IceCandidateRequest.cs13
-rw-r--r--Software/Visual_Studio/Tango.WebRTC/Network/IceCandidateResponse.cs12
-rw-r--r--Software/Visual_Studio/Tango.WebRTC/Network/OfferRequest.cs13
-rw-r--r--Software/Visual_Studio/Tango.WebRTC/Network/OfferResponse.cs13
-rw-r--r--Software/Visual_Studio/Tango.WebRTC/Tango.WebRTC.csproj15
-rw-r--r--Software/Visual_Studio/Tango.WebRTC/WebRtcTransportAdapter.cs307
-rw-r--r--Software/Visual_Studio/Tango.WebRTC/WebRtcTransportAdapterDisconnectedException.cs16
-rw-r--r--Software/Visual_Studio/Tango.WebRTC/WebRtcTransportAdapterMode.cs14
49 files changed, 1846 insertions, 68 deletions
diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/ViewModels/FileSystemViewVM.cs b/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/ViewModels/FileSystemViewVM.cs
index 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<FileSystemHandler> DeleteFileSystemHandlerCommand { get; set; }
public RelayCommand<FileSystemHandler> OpenFileSystemHandlerDestinationCommand { get; set; }
public RelayCommand<FileSystemHandler> RetryFailedFileSystemHandlerCommand { get; set; }
+ public RelayCommand<List<FileSystemItem>> CopyPasteCommand { get; set; }
+ public RelayCommand<List<FileSystemItem>> CutPasteCommand { get; set; }
+ public RelayCommand<List<FileSystemItem>> DownloadCommand { get; set; }
+ public RelayCommand<FileSystemItem> RenameCommand { get; set; }
public FileSystemViewVM()
@@ -69,6 +74,10 @@ namespace Tango.FSE.PPCConsole.ViewModels
DeleteFileSystemHandlerCommand = new RelayCommand<FileSystemHandler>(DeleteFileSystemHandler);
OpenFileSystemHandlerDestinationCommand = new RelayCommand<FileSystemHandler>(OpenFileSystemHandlerDestination);
RetryFailedFileSystemHandlerCommand = new RelayCommand<FileSystemHandler>(RetryFailedFileSystemHandler);
+ CopyPasteCommand = new RelayCommand<List<FileSystemItem>>((items) => PasteItems(items, false));
+ CutPasteCommand = new RelayCommand<List<FileSystemItem>>((items) => PasteItems(items, true));
+ DownloadCommand = new RelayCommand<List<FileSystemItem>>(DownloadSelectedItems);
+ RenameCommand = new RelayCommand<FileSystemItem>(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<FileSystemItem> 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<FileSystemItem> 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 @@
<RowDefinition Height="1*" />
</Grid.RowDefinitions>
- <DockPanel>
+ <DockPanel IsEnabled="{Binding MachineProvider.IsConnected}">
<ListBox x:Name="listView" SelectedIndex="1" Background="{StaticResource FSE_PrimaryBackgroundLightBrush}" SelectionChanged="ListView_SelectionChanged" Style="{StaticResource MaterialDesignToolToggleListBox}" Margin="50 0 0 0" DockPanel.Dock="Right">
<ListBox.Resources>
<SolidColorBrush x:Key="MaterialDesignDivider" Color="{StaticResource FSE_PrimaryBackgroundDarkColor}"/>
@@ -92,7 +92,7 @@
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
- <Border Background="{StaticResource FSE_PrimaryBackgroundDarkBrush}" BorderBrush="{StaticResource FSE_PrimaryBackgroundLightBrush}" BorderThickness="3" CornerRadius="3" Padding="10 5">
+ <Border IsEnabled="{Binding MachineProvider.IsConnected}" Background="{StaticResource FSE_PrimaryBackgroundDarkBrush}" BorderBrush="{StaticResource FSE_PrimaryBackgroundLightBrush}" BorderThickness="3" CornerRadius="3" Padding="10 5">
<StackPanel>
<controls:TextIconButton HorizontalContentAlignment="Left" Padding="5" Icon="Computer" Background="Transparent" BorderThickness="0" FocusVisualStyle="{x:Null}">Devices</controls:TextIconButton>
<StackPanel Margin="30 0 0 0">
@@ -121,6 +121,7 @@
<RowDefinition Height="100" MinHeight="0" />
</Grid.RowDefinitions>
<controls:FileSystemControl
+ IsEnabled="{Binding MachineProvider.IsConnected}"
AllowDrag="True"
AllowDrop="True"
CurrentItem="{Binding CurrentItem}"
@@ -129,20 +130,31 @@
DeleteCommand="{Binding DeleteCommand}"
SelectedItems="{Binding SelectedItems}"
DragCommand="{Binding DragCommand}"
- DropCommand="{Binding DropCommand}"/>
+ DropCommand="{Binding DropCommand}"
+ CutPasteCommand="{Binding CutPasteCommand}"
+ CopyPasteCommand="{Binding CopyPasteCommand}"
+ DownloadCommand="{Binding DownloadCommand}"
+ BackCommand="{Binding BackCommand}"
+ RenameCommand="{Binding RenameCommand}"/>
<GridSplitter Grid.Row="1" Height="5" HorizontalAlignment="Stretch" VerticalAlignment="Center" />
<Grid Grid.Row="2">
<DockPanel>
- <StackPanel DockPanel.Dock="Top" Margin="5" Orientation="Horizontal">
- <material:PackIcon Kind="Download" />
- <material:PackIcon Margin="-2 0 0 0" Kind="Upload" />
- <TextBlock Margin="5 0 0 0" Foreground="{StaticResource FSE_PrimaryAccentBrush}" FontSize="{StaticResource FSE_SmallFontSize}">Transfer Queue</TextBlock>
- <TextBlock Margin="10 0 0 0" Foreground="{StaticResource FSE_GrayBrush}" FontSize="{StaticResource FSE_SmallFontSize}">
+ <Grid DockPanel.Dock="Top" >
+ <StackPanel Margin="5" Orientation="Horizontal" HorizontalAlignment="Left">
+ <material:PackIcon Kind="Download" />
+ <material:PackIcon Margin="-2 0 0 0" Kind="Upload" />
+ <TextBlock Margin="5 0 0 0" Foreground="{StaticResource FSE_PrimaryAccentBrush}" FontSize="{StaticResource FSE_SmallFontSize}">Transfer Queue</TextBlock>
+ <TextBlock Margin="10 0 0 0" Foreground="{StaticResource FSE_GrayBrush}" FontSize="{StaticResource FSE_SmallFontSize}">
<Run>(</Run><Run Text="{Binding FileSystemHandlers.Count,Mode=OneWay}"></Run><Run>)</Run>
- </TextBlock>
- </StackPanel>
+ </TextBlock>
+ </StackPanel>
+ <StackPanel VerticalAlignment="Center" HorizontalAlignment="Right" Margin="0 0 10 0" Orientation="Horizontal">
+ <material:PackIcon ToolTip="Fast communication channel is available" Visibility="{Binding FileSystemProvider.IsWebRtcAvailable,Converter={StaticResource BooleanToVisibilityConverter}}" Kind="LightningBoltCircle" HorizontalAlignment="Right" Width="18" Height="18" Foreground="{StaticResource FSE_GreenBrush}" />
+ <CheckBox Margin="5 0 0 0" IsEnabled="{Binding FileSystemProvider.IsWebRtcAvailable}" FontSize="{StaticResource FSE_SmallFontSize}" IsChecked="{Binding FileSystemProvider.EnableWebRTC}">Enable fast communication channel</CheckBox>
+ </StackPanel>
+ </Grid>
<Border CornerRadius="3" BorderThickness="3" BorderBrush="{StaticResource FSE_PrimaryBackgroundLightBrush}" Background="{StaticResource FSE_PrimaryBackgroundDarkBrush}">
<ScrollViewer HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
<ItemsControl ItemsSource="{Binding FileSystemHandlers}" HorizontalContentAlignment="Stretch" FontSize="10" Background="Transparent">
@@ -221,13 +233,13 @@
<controls:IconButton Icon="Restart" Cursor="Hand" Command="{Binding RelativeSource={RelativeSource AncestorType=UserControl},Path=DataContext.RetryFailedFileSystemHandlerCommand}" CommandParameter="{Binding}">
<controls:IconButton.Style>
<Style TargetType="controls:IconButton" BasedOn="{StaticResource {x:Type controls:IconButton}}">
- <Setter Property="Visibility" Value="Hidden"></Setter>
- <Style.Triggers>
- <DataTrigger Binding="{Binding Status}" Value="Failed">
- <Setter Property="Visibility" Value="Visible"></Setter>
- </DataTrigger>
- </Style.Triggers>
- </Style>
+ <Setter Property="Visibility" Value="Hidden"></Setter>
+ <Style.Triggers>
+ <DataTrigger Binding="{Binding Status}" Value="Failed">
+ <Setter Property="Visibility" Value="Visible"></Setter>
+ </DataTrigger>
+ </Style.Triggers>
+ </Style>
</controls:IconButton.Style>
</controls:IconButton>
</Grid>
@@ -239,11 +251,16 @@
<Grid Margin="10 0 0 0">
<DockPanel>
<TextBlock FontSize="{StaticResource FSE_SmallFontSize}" DockPanel.Dock="Top" HorizontalAlignment="Left" Text="{Binding FileSystemItem.Name}" TextTrimming="CharacterEllipsis"></TextBlock>
- <TextBlock Foreground="{StaticResource FSE_GrayBrush}" Margin="0 2 0 0" DockPanel.Dock="Top" HorizontalAlignment="Left" TextTrimming="CharacterEllipsis">
- <Run Text="{Binding Position,Mode=OneWay,Converter={StaticResource ByteArrayToFileSizeConverter}}"></Run>
- <Run>/</Run>
- <Run Text="{Binding Length,Mode=OneWay,Converter={StaticResource ByteArrayToFileSizeConverter}}"></Run>
- </TextBlock>
+ <DockPanel Width="140" TextElement.Foreground="{StaticResource FSE_GrayBrush}" Margin="0 2 0 0" DockPanel.Dock="Top" HorizontalAlignment="Left">
+ <TextBlock TextTrimming="CharacterEllipsis">
+ <Run Text="{Binding Position,Mode=OneWay,Converter={StaticResource ByteArrayToFileSizeConverter}}"></Run>
+ <Run>/</Run>
+ <Run Text="{Binding Length,Mode=OneWay,Converter={StaticResource ByteArrayToFileSizeConverter}}"></Run>
+ </TextBlock>
+ <TextBlock HorizontalAlignment="Right">
+ <Run Text="{Binding TransferRate,Mode=OneWay,Converter={StaticResource ByteArrayToFileSizeConverter}}"></Run><Run>/s</Run>
+ </TextBlock>
+ </DockPanel>
<Border VerticalAlignment="Bottom" Height="5" Background="{StaticResource FSE_PrimaryBackgroundBrush}">
<ProgressBar Height="Auto" Minimum="0" Maximum="{Binding Length}" Value="{Binding Position}">
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">
<converters:ByteArrayToFileSizeConverter x:Key="ByteArrayToFileSizeConverter" />
@@ -9,6 +11,11 @@
<Style TargetType="{x:Type local:FileSystemDataGridRow}" BasedOn="{StaticResource {x:Type DataGridRow}}">
<Setter Property="DoubleClickCommand" Value="{Binding RelativeSource={RelativeSource AncestorType=local:FileExplorerControl},Path=ItemDoubleClickedCommand}"></Setter>
+ <Style.Triggers>
+ <DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=Self},Path=(local:FileExplorerControl.IsCut)}" Value="True">
+ <Setter Property="Opacity" Value="0.3"></Setter>
+ </DataTrigger>
+ </Style.Triggers>
</Style>
<Style TargetType="{x:Type local:FileSystemDataGrid}" BasedOn="{StaticResource {x:Type DataGrid}}">
@@ -27,6 +34,7 @@
<Setter Property="Mode" Value="Details"></Setter>
<Setter Property="FocusVisualStyle" Value="{x:Null}"></Setter>
<Setter Property="Focusable" Value="True"></Setter>
+ <Setter Property="Background" Value="Transparent"></Setter>
<Setter Property="AllowDrop" Value="True"></Setter>
<Setter Property="Template">
<Setter.Value>
@@ -34,6 +42,55 @@
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
+ <Border.ContextMenu>
+ <ContextMenu Width="250" IsOpen="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},Path=IsContextMenuOpened,Mode=TwoWay}">
+ <MenuItem Header="Open" InputGestureText="Enter" Command="{TemplateBinding OpenCommand}">
+ <MenuItem.Icon>
+ <material:PackIcon Kind="SubdirectoryArrowRight" />
+ </MenuItem.Icon>
+ </MenuItem>
+ <MenuItem Header="Copy" InputGestureText="Ctrl+C" Command="{TemplateBinding CopyCommand}">
+ <MenuItem.Icon>
+ <material:PackIcon Kind="ContentCopy" />
+ </MenuItem.Icon>
+ </MenuItem>
+ <MenuItem Header="Cut" InputGestureText="Ctrl+X" Command="{TemplateBinding CutCommand}">
+ <MenuItem.Icon>
+ <material:PackIcon Kind="ContentCut" />
+ </MenuItem.Icon>
+ </MenuItem>
+ <Separator/>
+ <MenuItem Header="Paste" InputGestureText="Ctrl+V" Command="{TemplateBinding PasteCommandInternal}">
+ <MenuItem.Icon>
+ <material:PackIcon Kind="ContentPaste" />
+ </MenuItem.Icon>
+ </MenuItem>
+ <Separator/>
+ <MenuItem Header="Rename" InputGestureText="F2" Command="{TemplateBinding RenameCommandInternal}">
+ <MenuItem.Icon>
+ <material:PackIcon Kind="Rename" />
+ </MenuItem.Icon>
+ </MenuItem>
+ <Separator/>
+ <MenuItem Header="Delete" InputGestureText="DEL" Command="{TemplateBinding DeleteCommandInternal}">
+ <MenuItem.Icon>
+ <material:PackIcon Kind="DeleteForever" />
+ </MenuItem.Icon>
+ </MenuItem>
+ <Separator/>
+ <MenuItem Header="Download" InputGestureText="Ctrl+D" Command="{TemplateBinding DownloadCommandInternal}">
+ <MenuItem.Icon>
+ <material:PackIcon Kind="Download" />
+ </MenuItem.Icon>
+ </MenuItem>
+ <Separator/>
+ <MenuItem Header="Select All" InputGestureText="Ctrl+A" Command="{TemplateBinding SelectAllCommand}">
+ <MenuItem.Icon>
+ <material:PackIcon Kind="SelectAll" />
+ </MenuItem.Icon>
+ </MenuItem>
+ </ContextMenu>
+ </Border.ContextMenu>
<Grid Background="Transparent">
<ListBox x:Name="PART_listbox" Background="Transparent" SelectionMode="Extended" ScrollViewer.HorizontalScrollBarVisibility="Disabled" ScrollViewer.VerticalScrollBarVisibility="Auto" ItemsSource="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},Path=CurrentItem.Items}" SelectedItem="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},Path=SelectedItem,Mode=TwoWay}">
@@ -49,8 +106,15 @@
</ListBox.Style>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem" BasedOn="{StaticResource {x:Type ListBoxItem}}">
+ <Setter Property="Opacity" Value="1"></Setter>
<Setter Property="Padding" Value="5"></Setter>
<Setter Property="Margin" Value="0 10 20 10"></Setter>
+ <Setter Property="FocusVisualStyle" Value="{x:Null}"></Setter>
+ <Style.Triggers>
+ <DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=Self},Path=(local:FileExplorerControl.IsCut)}" Value="True">
+ <Setter Property="Opacity" Value="0.3"></Setter>
+ </DataTrigger>
+ </Style.Triggers>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemsPanel>
@@ -123,7 +187,7 @@
</DataGrid.Style>
<DataGrid.Columns>
- <DataGridTemplateColumn Header="NAME" Width="300*">
+ <DataGridTemplateColumn Header="NAME" Width="300*" SortMemberPath="Name">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<DockPanel Background="Transparent">
@@ -149,7 +213,7 @@
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
- <DataGridTemplateColumn Header="DATE MODIFIED" Width="170*">
+ <DataGridTemplateColumn Header="DATE MODIFIED" Width="170*" SortMemberPath="DateModified">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<DockPanel Background="Transparent">
@@ -158,7 +222,7 @@
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
- <DataGridTemplateColumn Header="TYPE" Width="140*">
+ <DataGridTemplateColumn Header="TYPE" Width="140*" SortMemberPath="Type">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<DockPanel Background="Transparent">
@@ -167,7 +231,7 @@
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
- <DataGridTemplateColumn Header="SIZE" Width="100*">
+ <DataGridTemplateColumn Header="SIZE" Width="100*" SortMemberPath="Size">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<DockPanel Background="Transparent">
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<IFileSystemContainer> GetFolder(String path);
Task<IFileSystemContainer> GetSpecialFolder(SpecialFolder specialFolder);
Task<IFileSystemContainer> GetThisPC();
Task<FileSystemHandler> Download(FileSystemItem item, String localTargetFolder);
Task<FileSystemHandler> 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;
@@ -73,6 +74,16 @@ namespace Tango.FSE.Common.Notifications
bool HasMessageBox { get; }
/// <summary>
+ /// Gets the current input box.
+ /// </summary>
+ InputBoxVM CurrentInputBox { get; }
+
+ /// <summary>
+ /// Gets a value indicating whether this instance has input box.
+ /// </summary>
+ bool HasInputBox { get; }
+
+ /// <summary>
/// Gets the current dialog.
/// </summary>
FrameworkElement CurrentDialog { get; }
@@ -112,6 +123,19 @@ namespace Tango.FSE.Common.Notifications
Task ShowSuccess(String message);
/// <summary>
+ /// Shows an input box.
+ /// </summary>
+ /// <param name="title">The title.</param>
+ /// <param name="message">The message.</param>
+ /// <param name="icon">The icon.</param>
+ /// <param name="defaultInput">The default input.</param>
+ /// <param name="inputHint">The input hint.</param>
+ /// <param name="okText">The ok text.</param>
+ /// <param name="cancelText">The cancel text.</param>
+ /// <returns></returns>
+ Task<InputBoxResult> ShowInputBox(String title, String message, PackIconKind icon = PackIconKind.InfoOutline, String defaultInput = null, String inputHint = null, int? maxChars = null, String okText = null, String cancelText = null);
+
+ /// <summary>
/// Shows a question message box.
/// </summary>
/// <param name="message">The message.</param>
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 @@
<Compile Include="Notifications\AppBarItem.cs" />
<Compile Include="Notifications\AppButton.cs" />
<Compile Include="Notifications\INotificationProvider.cs" />
+ <Compile Include="Notifications\InputBoxResult.cs" />
+ <Compile Include="Notifications\InputBoxVM.cs" />
<Compile Include="Notifications\ItemBase.cs" />
<Compile Include="Notifications\MessageBoxVM.cs" />
<Compile Include="Notifications\MessageType.cs" />
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<EventRegistration> _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<FileSystemHandler> _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<FileSystemHandler>();
+
+ 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<InitWebRtcRequest, InitWebRtcResponse>(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<IFileSystemContainer> 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<ChunkDownloadRequest, ChunkDownloadResponse>(
- 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<ChunkDownloadRequest, ChunkDownloadResponse>(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<ChunkDownloadRequest, ChunkDownloadResponse>(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<CopyRequest, CopyResponse>(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<MoveRequest, MoveResponse>(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<MoveRequest, MoveResponse>(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<DeleteRequest, DeleteResponse>(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;
+ /// <summary>
+ /// Gets the current input box.
+ /// </summary>
+ public InputBoxVM CurrentInputBox
+ {
+ get { return _currentInputBox; }
+ private set
+ {
+ _currentInputBox = value;
+ RaisePropertyChangedAuto();
+ RaisePropertyChanged(nameof(HasInputBox));
+ }
+ }
+
+ /// <summary>
+ /// Gets a value indicating whether this instance has input box.
+ /// </summary>
+ public bool HasInputBox
+ {
+ get
+ {
+ return CurrentInputBox != null;
+ }
+ }
+
private FrameworkElement _currentDialog;
/// <summary>
/// Gets the current dialog if any.
@@ -285,6 +311,45 @@ namespace Tango.FSE.UI.Notifications
return source.Task;
}
+ public Task<InputBoxResult> 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<InputBoxResult> source = new TaskCompletionSource<InputBoxResult>();
+
+ 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;
+ }
+
/// <summary>
/// Called when the message box has been closed.
/// </summary>
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<WebRtcIceCandidateRequest>(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<GetMachineInformationRequest, GetMachineInformationResponse>(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 @@
</StackPanel>
</Button>
- <Grid HorizontalAlignment="Right" Margin="0 0 25 0">
+ <Grid HorizontalAlignment="Right" Margin="0 0 25 0" ToolTip="{Binding MachineProvider.MachineOperator.Status,Converter={StaticResource EnumToDescriptionConverter}}">
<Ellipse Width="42" Height="42" Stroke="{StaticResource FSE_PrimaryBackgroundLightBrush}" StrokeThickness="4">
<Ellipse.Style>
<Style TargetType="Ellipse">
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 3961d303b..07f40d18c 100644
--- a/Software/Visual_Studio/FSE/Tango.FSE.UI/Views/MainView.xaml
+++ b/Software/Visual_Studio/FSE/Tango.FSE.UI/Views/MainView.xaml
@@ -274,6 +274,77 @@
</Grid>
<!--TASK ITEMS-->
+ <!--INPUT BOX-->
+ <Grid x:Name="gridInputBox" IsVisibleChanged="GridInputBox_IsVisibleChanged" Background="{StaticResource FSE_SemiTransparentBrush}" Visibility="{Binding NotificationProvider.HasInputBox,Converter={StaticResource BooleanToVisibilityConverter}}">
+ <Grid MinWidth="650" MaxWidth="800" MaxHeight="400" MinHeight="280" HorizontalAlignment="Center" VerticalAlignment="Center" RenderTransformOrigin="0.5,0.5" Grid.Column="1" Grid.Row="1">
+ <Grid.Style>
+ <Style TargetType="Grid">
+ <Setter Property="RenderTransform">
+ <Setter.Value>
+ <ScaleTransform ScaleX="0" ScaleY="0" />
+ </Setter.Value>
+ </Setter>
+ <Style.Triggers>
+ <DataTrigger Binding="{Binding NotificationProvider.HasInputBox}" Value="True">
+ <DataTrigger.EnterActions>
+ <BeginStoryboard>
+ <Storyboard>
+ <DoubleAnimation Storyboard.TargetProperty="RenderTransform.ScaleX" From="0" To="1" Duration="00:00:0.1" />
+ <DoubleAnimation Storyboard.TargetProperty="RenderTransform.ScaleY" From="0" To="1" Duration="00:00:0.1" />
+ </Storyboard>
+ </BeginStoryboard>
+ </DataTrigger.EnterActions>
+ <DataTrigger.ExitActions>
+ <BeginStoryboard>
+ <Storyboard>
+ <DoubleAnimation Storyboard.TargetProperty="RenderTransform.ScaleX" From="1" To="0" Duration="00:00:0.1" />
+ <DoubleAnimation Storyboard.TargetProperty="RenderTransform.ScaleY" From="1" To="0" Duration="00:00:0.1" />
+ </Storyboard>
+ </BeginStoryboard>
+ </DataTrigger.ExitActions>
+ </DataTrigger>
+ </Style.Triggers>
+ </Style>
+ </Grid.Style>
+
+ <Border DataContext="{Binding NotificationProvider.CurrentInputBox}" Background="{StaticResource FSE_PrimaryBackgroundBrush}" CornerRadius="5" Margin="10">
+ <Border.Effect>
+ <DropShadowEffect BlurRadius="10" />
+ </Border.Effect>
+ <Grid ClipToBounds="True">
+ <Grid>
+ <Grid.RowDefinitions>
+ <RowDefinition Height="48" />
+ <RowDefinition Height="80*" />
+ <RowDefinition Height="60" />
+ </Grid.RowDefinitions>
+ <Grid>
+ <Border CornerRadius="5 5 0 0" Background="{StaticResource FSE_PrimaryBackgroundLightBrush}"></Border>
+ <Border CornerRadius="5 5 0 0" BorderBrush="{StaticResource FSE_BorderBrush}" BorderThickness="0 0 0 1">
+ <StackPanel VerticalAlignment="Center" Margin="10 0 0 0" Orientation="Horizontal" HorizontalAlignment="Left">
+ <material:PackIcon Width="24" Height="24" Margin="0 0 10 0" Kind="{Binding Icon}" ></material:PackIcon>
+ <TextBlock VerticalAlignment="Center" Text="{Binding Title}" FontSize="{StaticResource FSE_MessageBoxTitleFontSize}"></TextBlock>
+ </StackPanel>
+ </Border>
+ <commonControls:IconButton Icon="Close" Padding="8" Margin="0 0 5 0" Command="{Binding CloseCommand}" CommandParameter="{Binding}" HorizontalAlignment="Right" Width="{Binding RelativeSource={RelativeSource Self},Path=ActualHeight}"/>
+ </Grid>
+
+ <StackPanel Grid.Row="1" Margin="40" VerticalAlignment="Top">
+ <TextBlock Foreground="{StaticResource FSE_PrimaryForegroundBrush}" Text="{Binding Message}" TextWrapping="Wrap"></TextBlock>
+ <TextBox x:Name="txtInput" KeyDown="TxtInput_KeyDown" Margin="0 20 0 0" Text="{Binding Input,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" MaxLength="{Binding MaxCharacters}" material:HintAssist.Hint="{Binding InputHint}" />
+ </StackPanel>
+
+ <StackPanel HorizontalAlignment="Right" Grid.Row="2" Margin="10" Orientation="Horizontal">
+ <Button MinWidth="150" x:Name="btnInputCancel" Height="Auto" Style="{StaticResource FSE_RaisedButton_Dark_Hover}" Margin="0 0 10 0" Command="{Binding CloseCommand}" Visibility="{Binding HasCancel,Converter={StaticResource BooleanToVisibilityConverter}}" Content="{Binding CancelText}"></Button>
+ <Button MinWidth="150" x:Name="btnInputOK" IsDefault="True" Style="{StaticResource FSE_RaisedButton_Dark_Hover}" Height="Auto" Command="{Binding OKCommand}" Content="{Binding OKText}"></Button>
+ </StackPanel>
+ </Grid>
+ </Grid>
+ </Border>
+ </Grid>
+ </Grid>
+ <!--INPUT BOX-->
+
<!--MESSAGE BOXES-->
<Grid x:Name="gridMessageBox" IsVisibleChanged="GridMessageBox_IsVisibleChanged" Background="{StaticResource FSE_SemiTransparentBrush}" Visibility="{Binding NotificationProvider.HasMessageBox,Converter={StaticResource BooleanToVisibilityConverter}}">
<Grid MinWidth="650" MaxWidth="800" MaxHeight="400" MinHeight="280" HorizontalAlignment="Center" VerticalAlignment="Center" RenderTransformOrigin="0.5,0.5" Grid.Column="1" Grid.Row="1">
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<String, FileSystemOperation> _operations;
+ private Dictionary<ExternalBridgeReceiver, BasicTransporter> _webRtcClients;
public bool Enabled { get; set; } = true;
+ public bool EnableWebRTC { get; set; } = true;
public DefaultFileSystemService(IPPCExternalBridgeService externalBridge)
{
+ _webRtcClients = new Dictionary<ExternalBridgeReceiver, BasicTransporter>();
_manager = new FileSystemManager();
_operations = new Dictionary<string, FileSystemOperation>();
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<ChunkDownloadRequest>(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<FileSystemItem> _selectedItemsBeforeDrag;
+ private List<FileSystemItem> _copyItems;
+ private bool _isCut;
+ private bool _isAfterContextMenu;
+
+ #region IsCut Attached Property
+
+ /// <summary>
+ /// Determines whether the draggable element is currently being dragged.
+ /// </summary>
+ public static readonly DependencyProperty IsCutProperty =
+ DependencyProperty.RegisterAttached("IsCut",
+ typeof(bool), typeof(FileExplorerControl),
+ new FrameworkPropertyMetadata(false));
+
+ /// <summary>
+ /// Sets the IsCut attached property.
+ /// </summary>
+ /// <param name="element">The element.</param>
+ /// <param name="value">if set to <c>true</c> [value].</param>
+ public static void SetIsCut(FrameworkElement element, bool value)
+ {
+ element.SetValue(IsCutProperty, value);
+ }
+
+ /// <summary>
+ /// Gets the is dragging attached property.
+ /// </summary>
+ /// <param name="element">The element.</param>
+ /// <returns></returns>
+ 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<FileSystemItem>();
_selectedItemsBeforeDrag = new List<FileSystemItem>();
+
+ 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))
{
- DeleteCommand?.Execute(SelectedItems);
+ OpenCommand?.Execute(null);
}
}
+ else if (e.Key == Key.C && Keyboard.IsKeyDown(Key.LeftCtrl))
+ {
+ if (CopyCommand != null && CopyCommand.CanExecute(null))
+ {
+ 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 @@
<Compile Include="Network\ChunkUploadResponse.cs" />
<Compile Include="Network\ChunkDownloadRequest.cs" />
<Compile Include="Network\ChunkDownloadResponse.cs" />
+ <Compile Include="Network\CopyRequest.cs" />
+ <Compile Include="Network\CopyResponse.cs" />
+ <Compile Include="Network\DeleteRequest.cs" />
+ <Compile Include="Network\DeleteResponse.cs" />
+ <Compile Include="Network\InitWebRtcRequest.cs" />
+ <Compile Include="Network\InitWebRtcResponse.cs" />
+ <Compile Include="Network\MoveRequest.cs" />
+ <Compile Include="Network\MoveResponse.cs" />
<Compile Include="VirtualFileDataObject.cs" />
<Page Include="Themes\Generic.xaml">
<Generator>MSBuild:Compile</Generator>
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 @@
<Setter Property="VerticalContentAlignment" Value="Center"></Setter>
</Style>
+ <ContextMenu x:Key="ItemContextMenu">
+ <MenuItem Header="Copy"/>
+ <MenuItem Header="Paste"/>
+ <MenuItem Header="Delete"/>
+ <MenuItem Header="Select All"/>
+ </ContextMenu>
+
<Style TargetType="{x:Type local:FileExplorerControl}">
<Setter Property="DriveIcon" Value="pack://application:,,,/Tango.FileSystem;component/Images/drive.png"></Setter>
<Setter Property="FolderIcon" Value="pack://application:,,,/Tango.FileSystem;component/Images/folder.png"></Setter>
@@ -34,7 +41,6 @@
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
-
<Grid Background="Transparent">
<ListBox x:Name="PART_listbox" Background="Transparent" SelectionMode="Extended" ScrollViewer.HorizontalScrollBarVisibility="Disabled" ScrollViewer.VerticalScrollBarVisibility="Auto" ItemsSource="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},Path=CurrentItem.Items}" SelectedItem="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},Path=SelectedItem,Mode=TwoWay}">
<ListBox.Style>
@@ -51,6 +57,7 @@
<Style TargetType="ListBoxItem" BasedOn="{StaticResource {x:Type ListBoxItem}}">
<Setter Property="Padding" Value="5"></Setter>
<Setter Property="Margin" Value="10"></Setter>
+ <Setter Property="ContextMenu" Value="{StaticResource ItemContextMenu}"></Setter>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemsPanel>
@@ -123,7 +130,7 @@
</DataGrid.Style>
<DataGrid.Columns>
- <DataGridTemplateColumn Header="Name" Width="300*">
+ <DataGridTemplateColumn Header="Name" Width="300*" SortMemberPath="Name">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<DockPanel Background="Transparent">
@@ -149,7 +156,7 @@
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
- <DataGridTemplateColumn Header="Date Modified" Width="170*">
+ <DataGridTemplateColumn Header="Date Modified" Width="170*" SortMemberPath="DateModified">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<DockPanel Background="Transparent">
@@ -158,7 +165,7 @@
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
- <DataGridTemplateColumn Header="Type" Width="140*">
+ <DataGridTemplateColumn Header="Type" Width="140*" SortMemberPath="Type">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<DockPanel Background="Transparent">
@@ -167,7 +174,7 @@
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
- <DataGridTemplateColumn Header="Size" Width="100*">
+ <DataGridTemplateColumn Header="Size" Width="100*" SortMemberPath="Size">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<DockPanel Background="Transparent">
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);
+ }
+
/// <remarks>
/// 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 request, String token);
+ public delegate void RequestHandlerCallbackDelegate<Request>(ITransporter transporter, Request request, String token);
/// <summary>
/// Represents a transportation engine which can send and receive <see cref="TangoMessage{T}"/> message using a <see cref="ITransportAdapter">Transport adapter</see>.
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<Object, String> Callback { get; set; }
+ public Action<ITransporter, Object, String> 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 @@
<Compile Include="DataMessageReceivedEventArgs.cs" />
<Compile Include="ErrorEventArgs.cs" />
<Compile Include="IceCandidate.cs" />
+ <Compile Include="Network\IceCandidateRequest.cs" />
+ <Compile Include="Network\IceCandidateResponse.cs" />
+ <Compile Include="Network\OfferRequest.cs" />
+ <Compile Include="Network\OfferResponse.cs" />
<Compile Include="NewIceCandidateEventArgs.cs" />
<Compile Include="Offer.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="VideoFrameReceivedEventArgs.cs" />
<Compile Include="WebRtcClient.cs" />
+ <Compile Include="WebRtcTransportAdapter.cs" />
+ <Compile Include="WebRtcTransportAdapterDisconnectedException.cs" />
+ <Compile Include="WebRtcTransportAdapterMode.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\SideChains\WebRtc.NET\WebRtc.NET.vcxproj">
@@ -63,6 +70,14 @@
<Project>{a34ee0f0-649d-41c8-8489-b6f1cc6924ee}</Project>
<Name>Tango.Core</Name>
</ProjectReference>
+ <ProjectReference Include="..\Tango.Logging\Tango.Logging.csproj">
+ <Project>{BC932DBD-7CDB-488C-99E4-F02CF441F55E}</Project>
+ <Name>Tango.Logging</Name>
+ </ProjectReference>
+ <ProjectReference Include="..\Tango.Transport\Tango.Transport.csproj">
+ <Project>{74e700b0-1156-4126-be40-ee450d3c3026}</Project>
+ <Name>Tango.Transport</Name>
+ </ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project> \ 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<IceCandidate> _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<IceCandidateRequest>(OnIceCandidateRequestReceived);
+ SignalingTransporter.RegisterRequestHandler<OfferRequest>(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<object> completionSource = new TaskCompletionSource<object>();
+ bool completed = false;
+
+ _queuedIceCandidates = new List<IceCandidate>();
+ _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<OfferRequest, OfferResponse>(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<IceCandidateRequest, IceCandidateResponse>(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<byte[]> 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<IceCandidateRequest, IceCandidateResponse>(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<IceCandidateRequest, IceCandidateResponse>(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<object> completionSource = new TaskCompletionSource<object>();
+
+ 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
+ }
+}