aboutsummaryrefslogtreecommitdiffstats
path: root/Software/Visual_Studio
diff options
context:
space:
mode:
authorAvi Levkovich <avi@twine-s.com>2020-03-25 17:43:49 +0200
committerAvi Levkovich <avi@twine-s.com>2020-03-25 17:43:49 +0200
commitd29da53d6f71f45749c0ede5b4cd7281ed3a270e (patch)
treefd83afc7771c0f4f19c581e1cf407bcf7c14818b /Software/Visual_Studio
parent0208e9f1800c044ec3bd002b7aa7fd00621c81be (diff)
downloadTango-d29da53d6f71f45749c0ede5b4cd7281ed3a270e.tar.gz
Tango-d29da53d6f71f45749c0ede5b4cd7281ed3a270e.zip
merge
Diffstat (limited to 'Software/Visual_Studio')
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/Converters/DoubleToChartValuesConverter.cs23
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/ViewModels/FileSystemViewVM.cs243
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/ViewModels/MonitoringViewVM.cs211
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/Views/FileSystemView.xaml281
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/Views/FileSystemView.xaml.cs42
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/Views/MonitoringView.xaml205
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/Views/MonitoringView.xaml.cs33
-rw-r--r--Software/Visual_Studio/FSE/Tango.FSE.Common/Console/IConsoleService.cs16
-rw-r--r--Software/Visual_Studio/FSE/Tango.FSE.Common/Controls/FSEPanel.cs49
-rw-r--r--Software/Visual_Studio/FSE/Tango.FSE.Common/Controls/FSEPanel.xaml36
-rw-r--r--Software/Visual_Studio/FSE/Tango.FSE.Common/Controls/FSETabControl.cs106
-rw-r--r--Software/Visual_Studio/FSE/Tango.FSE.Common/Controls/FSETabControl.xaml106
-rw-r--r--Software/Visual_Studio/FSE/Tango.FSE.Common/Controls/FileSystemControl.cs26
-rw-r--r--Software/Visual_Studio/FSE/Tango.FSE.Common/Controls/FileSystemControl.xaml200
-rw-r--r--Software/Visual_Studio/FSE/Tango.FSE.Common/Controls/TextIconButton.cs34
-rw-r--r--Software/Visual_Studio/FSE/Tango.FSE.Common/Controls/TextIconButton.xaml32
-rw-r--r--Software/Visual_Studio/FSE/Tango.FSE.Common/Controls/ToggleIconButton.cs84
-rw-r--r--Software/Visual_Studio/FSE/Tango.FSE.Common/Controls/ToggleIconButton.xaml113
-rw-r--r--Software/Visual_Studio/FSE/Tango.FSE.Common/FileSystem/FileSystemHandler.cs119
-rw-r--r--Software/Visual_Studio/FSE/Tango.FSE.Common/FileSystem/FileSystemHandlerStatus.cs19
-rw-r--r--Software/Visual_Studio/FSE/Tango.FSE.Common/FileSystem/FileSystemHandlerType.cs17
-rw-r--r--Software/Visual_Studio/FSE/Tango.FSE.Common/FileSystem/IFileSystemProvider.cs19
-rw-r--r--Software/Visual_Studio/FSE/Tango.FSE.Common/Fonts/Caveat-Bold.ttfbin0 -> 335416 bytes
-rw-r--r--Software/Visual_Studio/FSE/Tango.FSE.Common/Fonts/Caveat-Regular.ttfbin0 -> 330800 bytes
-rw-r--r--Software/Visual_Studio/FSE/Tango.FSE.Common/Graphs/GraphHelper.cs37
-rw-r--r--Software/Visual_Studio/FSE/Tango.FSE.Common/Graphs/RealTimeGraph.cs101
-rw-r--r--Software/Visual_Studio/FSE/Tango.FSE.Common/Graphs/RealTimeGraph.xaml62
-rw-r--r--Software/Visual_Studio/FSE/Tango.FSE.Common/Images/screw.pngbin0 -> 937 bytes
-rw-r--r--Software/Visual_Studio/FSE/Tango.FSE.Common/Logging/ILoggingProvider.cs16
-rw-r--r--Software/Visual_Studio/FSE/Tango.FSE.Common/Performance/IPerformanceProvider.cs13
-rw-r--r--Software/Visual_Studio/FSE/Tango.FSE.Common/Performance/PerformancePackageEventArgs.cs14
-rw-r--r--Software/Visual_Studio/FSE/Tango.FSE.Common/Resolution/IResolutionService.cs20
-rw-r--r--Software/Visual_Studio/FSE/Tango.FSE.Common/Resolution/ResolutionHelper.cs227
-rw-r--r--Software/Visual_Studio/FSE/Tango.FSE.Common/Resolution/ResolutionMode.cs15
-rw-r--r--Software/Visual_Studio/FSE/Tango.FSE.Common/Resources/Graphs.xaml9
-rw-r--r--Software/Visual_Studio/FSE/Tango.FSE.Common/SystemInfo/ISystemInfoProvider.cs14
-rw-r--r--Software/Visual_Studio/FSE/Tango.FSE.Common/Themes/Generic.xaml7
-rw-r--r--Software/Visual_Studio/FSE/Tango.FSE.UI/Console/DefaultConsoleService.cs53
-rw-r--r--Software/Visual_Studio/FSE/Tango.FSE.UI/FileSystem/DefaultFileSystemProvider.cs216
-rw-r--r--Software/Visual_Studio/FSE/Tango.FSE.UI/Logging/DefaultLoggingProvider.cs39
-rw-r--r--Software/Visual_Studio/FSE/Tango.FSE.UI/Panes/LogViewerPane.xaml148
-rw-r--r--Software/Visual_Studio/FSE/Tango.FSE.UI/Panes/LogViewerPane.xaml.cs28
-rw-r--r--Software/Visual_Studio/FSE/Tango.FSE.UI/Panes/LogViewerPaneVM.cs179
-rw-r--r--Software/Visual_Studio/FSE/Tango.FSE.UI/Performance/DefaultPerformanceProvider.cs46
-rw-r--r--Software/Visual_Studio/FSE/Tango.FSE.UI/Resolution/DefaultResolutionService.cs121
-rw-r--r--Software/Visual_Studio/FSE/Tango.FSE.UI/SystemInfo/DefaultSystemInfoProvider.cs49
-rw-r--r--Software/Visual_Studio/Installers/VS Extensions/PropMan.vsixbin0 -> 45195 bytes
-rw-r--r--Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Statistics/Converters/JobLengthConverter.cs41
-rw-r--r--Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Statistics/Converters/NanoLiterToLiterFormatConverter.cs31
-rw-r--r--Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Statistics/Models/StatisticsValueCollection.cs319
-rw-r--r--Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Statistics/Models/TotalLiquidQuantityModel.cs15
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/FileSystem/DefaultFileSystemService.cs243
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/FileSystem/IFileSystemService.cs13
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/Performance/DefaultPerformanceService.cs227
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/Performance/IPerformanceService.cs14
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/SystemInfo/DefaultSystemInfoService.cs133
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/SystemInfo/ISystemInfoService.cs13
-rw-r--r--Software/Visual_Studio/SideChains/Priority Queue/FastPriorityQueue.cs593
-rw-r--r--Software/Visual_Studio/SideChains/Priority Queue/FastPriorityQueueNode.cs25
-rw-r--r--Software/Visual_Studio/SideChains/Priority Queue/GenericPriorityQueue.cs602
-rw-r--r--Software/Visual_Studio/SideChains/Priority Queue/GenericPriorityQueueNode.cs29
-rw-r--r--Software/Visual_Studio/SideChains/Priority Queue/IFixedSizePriorityQueue.cs31
-rw-r--r--Software/Visual_Studio/SideChains/Priority Queue/IPriorityQueue.cs55
-rw-r--r--Software/Visual_Studio/SideChains/Priority Queue/Priority Queue.csproj89
-rw-r--r--Software/Visual_Studio/SideChains/Priority Queue/Priority Queue.nuspec42
-rw-r--r--Software/Visual_Studio/SideChains/Priority Queue/Properties/AssemblyInfo.cs38
-rw-r--r--Software/Visual_Studio/SideChains/Priority Queue/SimplePriorityQueue.cs588
-rw-r--r--Software/Visual_Studio/SideChains/Priority Queue/StablePriorityQueue.cs587
-rw-r--r--Software/Visual_Studio/SideChains/Priority Queue/StablePriorityQueueNode.cs10
-rw-r--r--Software/Visual_Studio/SideChains/RealTimeGraphX-master/RealTimeGraphX/DataPoints/DateTimeDataPoint.cs178
-rw-r--r--Software/Visual_Studio/Tango.Console/Network/ConsoleCommandRequest.cs15
-rw-r--r--Software/Visual_Studio/Tango.Console/Network/ConsoleCommandResponse.cs20
-rw-r--r--Software/Visual_Studio/Tango.Console/Network/GetCurrentDirectoryRequest.cs13
-rw-r--r--Software/Visual_Studio/Tango.Console/Network/GetCurrentDirectoryResponse.cs19
-rw-r--r--Software/Visual_Studio/Tango.Core/ExtensionMethods/BooleanExtensions.cs14
-rw-r--r--Software/Visual_Studio/Tango.Core/PriorityProducerConsumerQueue.cs63
-rw-r--r--Software/Visual_Studio/Tango.FileSystem/DragItem.cs14
-rw-r--r--Software/Visual_Studio/Tango.FileSystem/DriveItem.cs38
-rw-r--r--Software/Visual_Studio/Tango.FileSystem/FileExplorerControl.cs423
-rw-r--r--Software/Visual_Studio/Tango.FileSystem/FileExplorerControlMode.cs14
-rw-r--r--Software/Visual_Studio/Tango.FileSystem/FileItem.cs132
-rw-r--r--Software/Visual_Studio/Tango.FileSystem/FileSystemDataGrid.cs30
-rw-r--r--Software/Visual_Studio/Tango.FileSystem/FileSystemDataGridRow.cs40
-rw-r--r--Software/Visual_Studio/Tango.FileSystem/FileSystemItem.cs79
-rw-r--r--Software/Visual_Studio/Tango.FileSystem/FileSystemItemType.cs15
-rw-r--r--Software/Visual_Studio/Tango.FileSystem/FileSystemManager.cs110
-rw-r--r--Software/Visual_Studio/Tango.FileSystem/FolderItem.cs34
-rw-r--r--Software/Visual_Studio/Tango.FileSystem/IFileSystemContainer.cs14
-rw-r--r--Software/Visual_Studio/Tango.FileSystem/Images/drive.pngbin0 -> 2166 bytes
-rw-r--r--Software/Visual_Studio/Tango.FileSystem/Images/folder.pngbin0 -> 317 bytes
-rw-r--r--Software/Visual_Studio/Tango.FileSystem/Network/AbortOperationRequest.cs13
-rw-r--r--Software/Visual_Studio/Tango.FileSystem/Network/AbortOperationResponse.cs12
-rw-r--r--Software/Visual_Studio/Tango.FileSystem/Network/ChunkDownloadRequest.cs15
-rw-r--r--Software/Visual_Studio/Tango.FileSystem/Network/ChunkDownloadResponse.cs13
-rw-r--r--Software/Visual_Studio/Tango.FileSystem/Network/ChunkUploadRequest.cs14
-rw-r--r--Software/Visual_Studio/Tango.FileSystem/Network/ChunkUploadResponse.cs13
-rw-r--r--Software/Visual_Studio/Tango.FileSystem/Network/FileDownloadRequest.cs13
-rw-r--r--Software/Visual_Studio/Tango.FileSystem/Network/FileDownloadResponse.cs14
-rw-r--r--Software/Visual_Studio/Tango.FileSystem/Network/FileSystemItemDTO.cs33
-rw-r--r--Software/Visual_Studio/Tango.FileSystem/Network/FileUploadRequest.cs13
-rw-r--r--Software/Visual_Studio/Tango.FileSystem/Network/FileUploadResponse.cs13
-rw-r--r--Software/Visual_Studio/Tango.FileSystem/Network/FolderDownloadRequest.cs13
-rw-r--r--Software/Visual_Studio/Tango.FileSystem/Network/FolderDownloadResponse.cs14
-rw-r--r--Software/Visual_Studio/Tango.FileSystem/Network/GetFileSystemItemRequest.cs15
-rw-r--r--Software/Visual_Studio/Tango.FileSystem/Network/GetFileSystemItemResponse.cs13
-rw-r--r--Software/Visual_Studio/Tango.FileSystem/Properties/AssemblyInfo.cs55
-rw-r--r--Software/Visual_Studio/Tango.FileSystem/Properties/Resources.Designer.cs62
-rw-r--r--Software/Visual_Studio/Tango.FileSystem/Properties/Resources.resx117
-rw-r--r--Software/Visual_Studio/Tango.FileSystem/Properties/Settings.Designer.cs30
-rw-r--r--Software/Visual_Studio/Tango.FileSystem/Properties/Settings.settings7
-rw-r--r--Software/Visual_Studio/Tango.FileSystem/Tango.FileSystem.csproj130
-rw-r--r--Software/Visual_Studio/Tango.FileSystem/Themes/Generic.xaml199
-rw-r--r--Software/Visual_Studio/Tango.FileSystem/VirtualFileDataObject.cs983
-rw-r--r--Software/Visual_Studio/Tango.Integration/ExternalBridge/Network/Information/GetMachineInformationRequest.cs12
-rw-r--r--Software/Visual_Studio/Tango.Integration/ExternalBridge/Network/Information/GetMachineInformationResponse.cs13
-rw-r--r--Software/Visual_Studio/Tango.Integration/ExternalBridge/Network/Information/InformationPackage.cs19
-rw-r--r--Software/Visual_Studio/Tango.Integration/ExternalBridge/Network/Performance/PerformancePackage.cs25
-rw-r--r--Software/Visual_Studio/Tango.Integration/ExternalBridge/Network/Performance/StartPerformanceUpdatesRequest.cs12
-rw-r--r--Software/Visual_Studio/Tango.Integration/ExternalBridge/Network/Performance/StartPerformanceUpdatesResponse.cs13
-rw-r--r--Software/Visual_Studio/Tango.RemoteDesktop/Network/RemoteDesktopCommand.cs14
-rw-r--r--Software/Visual_Studio/Tango.RemoteDesktop/Network/RemoteDesktopCommandRequest.cs13
-rw-r--r--Software/Visual_Studio/Tango.RemoteDesktop/Network/RemoteDesktopCommandResponse.cs13
-rw-r--r--Software/Visual_Studio/Tango.SharedUI/Binding/BindingEventArgs.cs39
-rw-r--r--Software/Visual_Studio/Tango.SharedUI/Binding/BindingEventContainer.cs95
-rw-r--r--Software/Visual_Studio/Tango.SharedUI/Binding/BindingProperty.cs38
-rw-r--r--Software/Visual_Studio/Tango.SharedUI/Converters/DateTimeUtcToLocalDateTime.cs30
-rw-r--r--Software/Visual_Studio/Tango.SharedUI/Converters/StringToTitleCaseConverter.cs30
-rw-r--r--Software/Visual_Studio/Tango.SystemInfo/Connection.cs83
-rw-r--r--Software/Visual_Studio/Tango.SystemInfo/IWMI.cs11
-rw-r--r--Software/Visual_Studio/Tango.SystemInfo/Properties/AssemblyInfo.cs36
-rw-r--r--Software/Visual_Studio/Tango.SystemInfo/SystemObject.cs31
-rw-r--r--Software/Visual_Studio/Tango.SystemInfo/SystemObjectProperty.cs12
-rw-r--r--Software/Visual_Studio/Tango.SystemInfo/SystemObjectsCollection.cs119
-rw-r--r--Software/Visual_Studio/Tango.SystemInfo/Tango.SystemInfo.csproj102
-rw-r--r--Software/Visual_Studio/Tango.SystemInfo/WMIReader.cs60
-rw-r--r--Software/Visual_Studio/Tango.SystemInfo/Win32_BIOS.cs25
-rw-r--r--Software/Visual_Studio/Tango.SystemInfo/Win32_BaseBoard.cs27
-rw-r--r--Software/Visual_Studio/Tango.SystemInfo/Win32_Battery.cs25
-rw-r--r--Software/Visual_Studio/Tango.SystemInfo/Win32_Bus.cs25
-rw-r--r--Software/Visual_Studio/Tango.SystemInfo/Win32_CDROMDrive.cs25
-rw-r--r--Software/Visual_Studio/Tango.SystemInfo/Win32_DMAChannel.cs25
-rw-r--r--Software/Visual_Studio/Tango.SystemInfo/Win32_DiskDrive.cs25
-rw-r--r--Software/Visual_Studio/Tango.SystemInfo/Win32_Fan.cs25
-rw-r--r--Software/Visual_Studio/Tango.SystemInfo/Win32_FloppyController.cs25
-rw-r--r--Software/Visual_Studio/Tango.SystemInfo/Win32_FloppyDrive.cs25
-rw-r--r--Software/Visual_Studio/Tango.SystemInfo/Win32_IDEController.cs25
-rw-r--r--Software/Visual_Studio/Tango.SystemInfo/Win32_IRQResource.cs25
-rw-r--r--Software/Visual_Studio/Tango.SystemInfo/Win32_Keyboard.cs25
-rw-r--r--Software/Visual_Studio/Tango.SystemInfo/Win32_MemoryDevice.cs25
-rw-r--r--Software/Visual_Studio/Tango.SystemInfo/Win32_NetworkAdapter.cs25
-rw-r--r--Software/Visual_Studio/Tango.SystemInfo/Win32_NetworkAdapterConfiguration.cs25
-rw-r--r--Software/Visual_Studio/Tango.SystemInfo/Win32_OnBoardDevice.cs25
-rw-r--r--Software/Visual_Studio/Tango.SystemInfo/Win32_OperatingSystem.cs21
-rw-r--r--Software/Visual_Studio/Tango.SystemInfo/Win32_PCMCIController.cs25
-rw-r--r--Software/Visual_Studio/Tango.SystemInfo/Win32_POTSModem.cs25
-rw-r--r--Software/Visual_Studio/Tango.SystemInfo/Win32_ParallelPort.cs25
-rw-r--r--Software/Visual_Studio/Tango.SystemInfo/Win32_PhysicalMedia.cs25
-rw-r--r--Software/Visual_Studio/Tango.SystemInfo/Win32_PhysicalMemory.cs25
-rw-r--r--Software/Visual_Studio/Tango.SystemInfo/Win32_PortConnector.cs25
-rw-r--r--Software/Visual_Studio/Tango.SystemInfo/Win32_PortResource.cs25
-rw-r--r--Software/Visual_Studio/Tango.SystemInfo/Win32_Processor.cs25
-rw-r--r--Software/Visual_Studio/Tango.SystemInfo/Win32_SCSIController.cs25
-rw-r--r--Software/Visual_Studio/Tango.SystemInfo/Win32_SerialPort.cs25
-rw-r--r--Software/Visual_Studio/Tango.SystemInfo/Win32_SerialPortConfiguration.cs25
-rw-r--r--Software/Visual_Studio/Tango.SystemInfo/Win32_SoundDevice.cs25
-rw-r--r--Software/Visual_Studio/Tango.SystemInfo/Win32_SystemEnclosure.cs25
-rw-r--r--Software/Visual_Studio/Tango.SystemInfo/Win32_TapeDrive.cs25
-rw-r--r--Software/Visual_Studio/Tango.SystemInfo/Win32_TemperatureProbe.cs25
-rw-r--r--Software/Visual_Studio/Tango.SystemInfo/Win32_USBController.cs25
-rw-r--r--Software/Visual_Studio/Tango.SystemInfo/Win32_USBHub.cs25
-rw-r--r--Software/Visual_Studio/Tango.SystemInfo/Win32_UninterruptiblePowerSupply.cs25
-rw-r--r--Software/Visual_Studio/Tango.SystemInfo/Win32_VideoController.cs25
-rw-r--r--Software/Visual_Studio/Tango.SystemInfo/Win32_VoltageProbe.cs25
-rw-r--r--Software/Visual_Studio/Tango.SystemInfo/XMLConfig.cs29
-rw-r--r--Software/Visual_Studio/Tango.SystemInfo/settings.xml1347
-rw-r--r--Software/Visual_Studio/Utilities/Tango.DispenserAnalyzer.UI/Analyzers/ReliabilityTestAnalyser.cs15
176 files changed, 13394 insertions, 0 deletions
diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/Converters/DoubleToChartValuesConverter.cs b/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/Converters/DoubleToChartValuesConverter.cs
new file mode 100644
index 000000000..1ac86b849
--- /dev/null
+++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/Converters/DoubleToChartValuesConverter.cs
@@ -0,0 +1,23 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Data;
+
+namespace Tango.FSE.PPCConsole.Converters
+{
+ public class DoubleToChartValuesConverter : IValueConverter
+ {
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ return new LiveCharts.ChartValues<double>() { (double)value };
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
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
new file mode 100644
index 000000000..cba25303e
--- /dev/null
+++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/ViewModels/FileSystemViewVM.cs
@@ -0,0 +1,243 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Input;
+using Tango.Core.Commands;
+using Tango.FileSystem;
+using Tango.FSE.Common;
+using Tango.FSE.Common.Connection;
+using Tango.FSE.Common.FileSystem;
+using static Tango.SharedUI.Controls.NavigationControl;
+
+namespace Tango.FSE.PPCConsole.ViewModels
+{
+ public class FileSystemViewVM : FSEViewModel, INavigationViewModel
+ {
+ private FileSystemItem _currentItem;
+ public FileSystemItem CurrentItem
+ {
+ get { return _currentItem; }
+ set { _currentItem = value; RaisePropertyChangedAuto(); OnCurrentItemChanged(); }
+ }
+
+ private String _currentPath;
+ public String CurrentPath
+ {
+ get { return _currentPath; }
+ set { _currentPath = value; RaisePropertyChangedAuto(); }
+ }
+
+ private List<DriveItem> _drives;
+ public List<DriveItem> Drives
+ {
+ get { return _drives; }
+ set { _drives = value; RaisePropertyChangedAuto(); }
+ }
+
+ public ObservableCollection<FileSystemItem> SelectedItems { get; set; }
+ public ObservableCollection<FileSystemHandler> FileSystemHandlers { get; set; }
+
+ public RelayCommand NavigateCommand { get; set; }
+ public RelayCommand<FileSystemItem> OpenItemCommand { get; set; }
+ public RelayCommand BackCommand { get; set; }
+ public RelayCommand<String> NavigateSpecialFolderCommand { get; set; }
+ public RelayCommand<String> NavigateToFolderCommand { get; set; }
+ public RelayCommand<IList<FileSystemItem>> DeleteCommand { get; set; }
+ public RelayCommand<List<FileSystemItem>> DropCommand { get; set; }
+ public RelayCommand<List<DragItem>> DragCommand { get; set; }
+ public RelayCommand<FileSystemHandler> DeleteFileSystemHandlerCommand { get; set; }
+ public RelayCommand<FileSystemHandler> OpenFileSystemHandlerDestinationCommand { get; set; }
+ public RelayCommand<FileSystemHandler> RetryFailedFileSystemHandlerCommand { get; set; }
+
+
+ public FileSystemViewVM()
+ {
+ SelectedItems = new ObservableCollection<FileSystemItem>();
+ FileSystemHandlers = new ObservableCollection<FileSystemHandler>();
+ NavigateCommand = new RelayCommand(NavigateToCurrentPath);
+ OpenItemCommand = new RelayCommand<FileSystemItem>(OpenFileSystemItem);
+ BackCommand = new RelayCommand(NavigateBack, () => !(CurrentItem is FolderItem) || !(CurrentItem as FolderItem).IsRoot);
+ NavigateSpecialFolderCommand = new RelayCommand<string>(NavigateToSpecialFolder);
+ NavigateToFolderCommand = new RelayCommand<string>(async (x) => await Navigate(x));
+ DeleteCommand = new RelayCommand<IList<FileSystemItem>>(DeleteSelectedItems);
+ DragCommand = new RelayCommand<List<DragItem>>(OnItemsDraggedOut);
+ DeleteFileSystemHandlerCommand = new RelayCommand<FileSystemHandler>(DeleteFileSystemHandler);
+ OpenFileSystemHandlerDestinationCommand = new RelayCommand<FileSystemHandler>(OpenFileSystemHandlerDestination);
+ RetryFailedFileSystemHandlerCommand = new RelayCommand<FileSystemHandler>(RetryFailedFileSystemHandler);
+ }
+
+ private async void NavigateBack()
+ {
+ if (CurrentItem.Path.Length == 3)
+ {
+ await Navigate(null);
+ }
+ else
+ {
+ String parent = Path.GetDirectoryName(CurrentItem.Path);
+ await Navigate(parent);
+ }
+ }
+
+ public override void OnApplicationStarted()
+ {
+ base.OnApplicationStarted();
+ MachineProvider.MachineConnected += MachineProvider_MachineConnected;
+ }
+
+ public override void OnApplicationReady()
+ {
+ base.OnApplicationReady();
+ }
+
+ private async void MachineProvider_MachineConnected(object sender, MachineConnectedEventArgs e)
+ {
+ await Navigate(null);
+ }
+
+ private async void NavigateToCurrentPath()
+ {
+ await Navigate(CurrentPath);
+ }
+
+ private async void OpenFileSystemItem(FileSystemItem item)
+ {
+ if (item == null) return;
+
+ if (item.Type == FileSystemItemType.Folder || item.Type == FileSystemItemType.Drive)
+ {
+ await Navigate(item.Path);
+ }
+ else if (item.Type == FileSystemItemType.File)
+ {
+ //TODO: Download/Open file?...
+ }
+ }
+
+ private async void DeleteSelectedItems(IList<FileSystemItem> items)
+ {
+ if (items != null && items.Count > 0)
+ {
+ if (await NotificationProvider.ShowWarningQuestion("Are you sure you want to delete the selected files/folders?", "DELETE"))
+ {
+ //TODO: Delete items
+ }
+ }
+ }
+
+ private async void OnItemsDraggedOut(List<DragItem> items)
+ {
+ foreach (var item in items.Where(x => x.FileSystemItem.Type != FileSystemItemType.Drive))
+ {
+ Debug.WriteLine($"Dropped out: {item.FileSystemItem.Name} => {item.Destination}");
+ var handler = await FileSystemProvider.Download(item.FileSystemItem, item.Destination);
+ FileSystemHandlers.Insert(0, handler);
+ }
+ }
+
+ private async void DeleteFileSystemHandler(FileSystemHandler handler)
+ {
+ if (handler.Status != FileSystemHandlerStatus.Completed && handler.Status != FileSystemHandlerStatus.Failed)
+ {
+ if (await NotificationProvider.ShowWarningQuestion($"This item is currently {handler.Status}. Do you wish to abort and delete this item?"))
+ {
+ try
+ {
+ handler.Abort();
+ }
+ catch { }
+
+ FileSystemHandlers.Remove(handler);
+ }
+ }
+ else
+ {
+ FileSystemHandlers.Remove(handler);
+ }
+ }
+
+ private void OpenFileSystemHandlerDestination(FileSystemHandler handler)
+ {
+ String destination = String.Empty;
+
+ if (File.Exists(handler.Destination) || Directory.Exists(handler.Destination))
+ {
+ destination = handler.Destination;
+ Process.Start("explorer.exe", string.Format("/select,\"{0}\"", destination));
+ }
+ else
+ {
+ destination = Path.GetDirectoryName(handler.Destination);
+ Process.Start("explorer.exe", destination);
+ }
+ }
+
+ private async void RetryFailedFileSystemHandler(FileSystemHandler handler)
+ {
+ if (handler.Status == FileSystemHandlerStatus.Failed)
+ {
+ var newHandler = await FileSystemProvider.Download(handler.FileSystemItem, Path.GetDirectoryName(handler.Destination));
+ FileSystemHandlers.Replace(handler, newHandler);
+ }
+ }
+
+ private async void NavigateToSpecialFolder(string folder)
+ {
+ Environment.SpecialFolder specialFolder = (Environment.SpecialFolder)Enum.Parse(typeof(Environment.SpecialFolder), folder);
+ await Navigate(null, specialFolder);
+ }
+
+ private async Task Navigate(String path, Environment.SpecialFolder? specialFolder = null)
+ {
+ try
+ {
+ IsFree = false;
+
+ Mouse.OverrideCursor = Cursors.AppStarting;
+
+ if (path != null)
+ {
+ CurrentItem = await FileSystemProvider.GetFolder(path) as FileSystemItem;
+ }
+ else if (specialFolder != null)
+ {
+ CurrentItem = await FileSystemProvider.GetSpecialFolder(specialFolder.Value) as FileSystemItem;
+ }
+ else
+ {
+ CurrentItem = await FileSystemProvider.GetThisPC() as FileSystemItem;
+ try
+ {
+ Drives = (CurrentItem as IFileSystemContainer).Items.Cast<DriveItem>().ToList();
+ }
+ catch (Exception ex)
+ {
+ LogManager.Log(ex, "Error setting file system drives menu.");
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ IsFree = true;
+ Mouse.OverrideCursor = null;
+ await NotificationProvider.ShowError($"Error navigating to the specified path.\n{ex.FlattenMessage()}");
+ }
+ finally
+ {
+ IsFree = true;
+ Mouse.OverrideCursor = null;
+ }
+ }
+
+ private void OnCurrentItemChanged()
+ {
+ CurrentPath = CurrentItem.Path;
+ InvalidateRelayCommands();
+ }
+ }
+}
diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/ViewModels/MonitoringViewVM.cs b/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/ViewModels/MonitoringViewVM.cs
new file mode 100644
index 000000000..eb59b2c9a
--- /dev/null
+++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/ViewModels/MonitoringViewVM.cs
@@ -0,0 +1,211 @@
+using LiveCharts;
+using RealTimeGraphX.DataPoints;
+using RealTimeGraphX.WPF;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Media;
+using Tango.FSE.Common;
+using Tango.FSE.Common.Graphs;
+using Tango.FSE.Common.Performance;
+using Tango.Integration.ExternalBridge.Network.Information;
+using Tango.SystemInfo;
+using static Tango.SharedUI.Controls.NavigationControl;
+
+namespace Tango.FSE.PPCConsole.ViewModels
+{
+ public class MonitoringViewVM : FSEViewModel, INavigationViewModel
+ {
+ public WpfGraphController<DateTimeDataPoint, DoubleDataPoint> CPUController { get; set; }
+
+ public WpfGraphController<DateTimeDataPoint, DoubleDataPoint> RAMController { get; set; }
+
+ public Func<ChartPoint, string> DiskSpacePointLabel { get; set; }
+
+ private InformationPackage _systemInfo;
+ public InformationPackage SystemInfo
+ {
+ get { return _systemInfo; }
+ set { _systemInfo = value; RaisePropertyChangedAuto(); }
+ }
+
+ private SystemObjectsCollection _selectedSystemObjectCollection;
+ public SystemObjectsCollection SelectedSystemObjectCollection
+ {
+ get { return _selectedSystemObjectCollection; }
+ set { _selectedSystemObjectCollection = value; RaisePropertyChangedAuto(); }
+ }
+
+ private bool _fetchingSystemInfo;
+ public bool FetchingSystemInfo
+ {
+ get { return _fetchingSystemInfo; }
+ set { _fetchingSystemInfo = value; RaisePropertyChangedAuto(); }
+ }
+
+ private double _usedDiskSpace;
+ public double UsedDiskSpace
+ {
+ get { return _usedDiskSpace; }
+ set
+ {
+ if (_usedDiskSpace != value)
+ {
+ _usedDiskSpace = value;
+ RaisePropertyChangedAuto();
+ }
+ }
+ }
+
+ private double _availableDiskSpace;
+ public double AvailableDiskSpace
+ {
+ get { return _availableDiskSpace; }
+ set
+ {
+ if (_availableDiskSpace != value)
+ {
+ _availableDiskSpace = value;
+ RaisePropertyChangedAuto();
+ }
+ }
+ }
+
+ public MonitoringViewVM()
+ {
+ CPUController = CreateController(CreateSeries("Total", GraphHelper.GraphColor.White), CreateSeries("Application", GraphHelper.GraphColor.Red));
+ RAMController = CreateController(CreateSeries("Total", GraphHelper.GraphColor.White), CreateSeries("Application", GraphHelper.GraphColor.Yellow));
+ UsedDiskSpace = 1000 * 40;
+ AvailableDiskSpace = 1000 * 60;
+ DiskSpacePointLabel = (point) =>
+ {
+ return $"{(point.Y / 1000d).ToString("0.0")} GB";
+ };
+ }
+
+ private WpfGraphController<DateTimeDataPoint, DoubleDataPoint> CreateController(params WpfGraphDataSeries[] seriesCollection)
+ {
+ var controller = new WpfGraphController<DateTimeDataPoint, DoubleDataPoint>();
+
+ foreach (var series in seriesCollection)
+ {
+ controller.DataSeriesCollection.Add(series);
+ }
+
+ controller.Range.AutoY = false;
+ controller.Range.MaximumY = 100;
+ controller.Range.MinimumY = 0;
+ controller.Range.MaximumX = new DateTime(0).AddMinutes(1);
+
+ controller.RefreshRate = TimeSpan.FromMilliseconds(100);
+
+ return controller;
+ }
+
+ private WpfGraphDataSeries CreateSeries(String name, GraphHelper.GraphColor fill)
+ {
+ WpfGraphDataSeries series = new WpfGraphDataSeries();
+ series.Name = name;
+ series.Fill = GraphHelper.GetGraphBrush(fill);
+ series.StrokeThickness = 1;
+ series.Stroke = GraphHelper.GetGraphStrokeColor();
+ return series;
+ }
+
+ public override void OnApplicationStarted()
+ {
+ base.OnApplicationStarted();
+ PerformanceProvider.PerformancePackageAvailable += PerformanceProvider_PerformancePackageAvailable;
+ MachineProvider.MachineDisconnected += MachineProvider_MachineDisconnected;
+ MachineProvider.MachineConnected += MachineProvider_MachineConnected;
+ }
+
+ private void MachineProvider_MachineConnected(object sender, Common.Connection.MachineConnectedEventArgs e)
+ {
+ CPUController.Clear();
+ RAMController.Clear();
+ AvailableDiskSpace = 0;
+ UsedDiskSpace = 0;
+ LoadSystemInformation();
+ }
+
+ private void MachineProvider_MachineDisconnected(object sender, Common.Connection.MachineDisconnectedEventArgs e)
+ {
+ SystemInfo = null;
+ }
+
+ private void PerformanceProvider_PerformancePackageAvailable(object sender, PerformancePackageEventArgs e)
+ {
+ List<DateTimeDataPoint> xx = new List<DateTimeDataPoint>()
+ {
+ DateTime.Now,
+ DateTime.Now
+ };
+
+ //CPU
+ CPUController.PushData(xx, new List<DoubleDataPoint>()
+ {
+ e.Package.CPU,
+ e.Package.ApplicationCPU
+ });
+
+ //App RAM
+ RAMController.Range.MaximumY = e.Package.MaxRAM;
+ RAMController.PushData(xx, new List<DoubleDataPoint>()
+ {
+ e.Package.RAM,
+ e.Package.ApplicationRAM
+ });
+
+ //Disk Space
+ var usedDiskSpace = e.Package.DiskCapacity - e.Package.AvailableDiskSpace;
+ var availableDiskSpace = e.Package.AvailableDiskSpace;
+
+ if (Math.Abs(UsedDiskSpace - usedDiskSpace) > 10)
+ {
+ UsedDiskSpace = usedDiskSpace;
+ }
+
+ if (Math.Abs(AvailableDiskSpace - availableDiskSpace) > 10)
+ {
+ AvailableDiskSpace = availableDiskSpace;
+ }
+ }
+
+ public override void OnNavigatedTo()
+ {
+ base.OnNavigatedTo();
+
+ if (MachineProvider.IsConnected)
+ {
+ LoadSystemInformation();
+ }
+ }
+
+ private async void LoadSystemInformation()
+ {
+ if (SystemInfo == null && !FetchingSystemInfo)
+ {
+ try
+ {
+ FetchingSystemInfo = true;
+ SystemInfo = await SystemInfoProvider.GetSystemInformationPackage();
+ SelectedSystemObjectCollection = SystemInfo.System.FirstOrDefault();
+ }
+ catch (Exception ex)
+ {
+ FetchingSystemInfo = false;
+ LogManager.Log(ex, "Error retrieving system information from remote machine.");
+ await NotificationProvider.ShowWarning("Error retrieving the remote machine tablet system information.");
+ }
+ finally
+ {
+ FetchingSystemInfo = false;
+ }
+ }
+ }
+ }
+}
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
new file mode 100644
index 000000000..8038ea905
--- /dev/null
+++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/Views/FileSystemView.xaml
@@ -0,0 +1,281 @@
+<UserControl x:Class="Tango.FSE.PPCConsole.Views.FileSystemView"
+ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ xmlns:global="clr-namespace:Tango.FSE.PPCConsole"
+ xmlns:vm="clr-namespace:Tango.FSE.PPCConsole.ViewModels"
+ xmlns:controls="clr-namespace:Tango.FSE.Common.Controls;assembly=Tango.FSE.Common"
+ xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+ xmlns:local="clr-namespace:Tango.FSE.PPCConsole.Views"
+ xmlns:material="http://materialdesigninxaml.net/winfx/xaml/themes"
+ mc:Ignorable="d"
+ d:DesignHeight="450" d:DesignWidth="800" d:DataContext="{d:DesignInstance Type=vm:FileSystemViewVM, IsDesignTimeCreatable=False}" DataContext="{x:Static global:ViewModelLocator.FileSystemViewVM}">
+ <Grid IsEnabled="{Binding IsFree}">
+ <Grid Margin="0 20 0 0">
+ <Grid.RowDefinitions>
+ <RowDefinition Height="30" />
+ <RowDefinition Height="1*" />
+ </Grid.RowDefinitions>
+
+ <DockPanel>
+ <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}"/>
+ </ListBox.Resources>
+ <ListBoxItem Width="60" HorizontalContentAlignment="Center" Tag="Large">
+ <material:PackIcon Kind="ViewList" Width="Auto" Height="20" />
+ </ListBoxItem>
+ <ListBoxItem Width="60" HorizontalContentAlignment="Center" Tag="Details">
+ <material:PackIcon Kind="FormatListBulleted" Width="Auto" Height="20" />
+ </ListBoxItem>
+ </ListBox>
+ <DockPanel>
+ <StackPanel DockPanel.Dock="Right" Orientation="Horizontal">
+ <Button Command="{Binding NavigateCommand}" material:ButtonAssist.CornerRadius="0 3 3 0" Style="{StaticResource FSE_RaisedButton_Dark_Hover}" Padding="0" Width="60" Height="Auto">
+ <material:PackIcon Kind="Refresh" Width="Auto" Height="20" />
+ </Button>
+ </StackPanel>
+ <StackPanel DockPanel.Dock="Left" Orientation="Horizontal">
+ <Button Command="{Binding BackCommand}" material:ButtonAssist.CornerRadius="3 0 0 3" Style="{StaticResource FSE_RaisedButton_Dark_Hover}" Padding="0" Width="60" Height="Auto">
+ <material:PackIcon Kind="ChevronLeft" Width="Auto" Height="20" />
+ </Button>
+ </StackPanel>
+ <Border>
+ <Border BorderThickness="3" BorderBrush="{StaticResource FSE_PrimaryBackgroundLightBrush}" Background="{StaticResource FSE_PrimaryBackgroundDarkBrush}">
+ <Border.Effect>
+ <DropShadowEffect BlurRadius="2" ShadowDepth="2" Opacity="0.3" Direction="270" />
+ </Border.Effect>
+ <Grid>
+ <Border IsHitTestVisible="False" Visibility="{Binding IsBusy,Converter={StaticResource BooleanToVisibilityConverter}}">
+ <Border.Background>
+ <LinearGradientBrush>
+ <GradientStop Offset="0" Color="Transparent" />
+ <GradientStop Offset="0.5" Color="#6303A9F4" />
+ <GradientStop Offset="1" Color="Transparent" />
+ </LinearGradientBrush>
+ </Border.Background>
+ <Border.Style>
+ <Style TargetType="Border">
+ <Style.Triggers>
+ <DataTrigger Binding="{Binding IsBusy}" Value="True">
+ <DataTrigger.EnterActions>
+ <BeginStoryboard Name="loadingStory">
+ <Storyboard>
+ <DoubleAnimation Storyboard.TargetProperty="Background.GradientStops[1].Offset" From="0" To="1" AutoReverse="True" RepeatBehavior="Forever" />
+ </Storyboard>
+ </BeginStoryboard>
+ </DataTrigger.EnterActions>
+ <DataTrigger.ExitActions>
+ <RemoveStoryboard BeginStoryboardName="loadingStory" />
+ </DataTrigger.ExitActions>
+ </DataTrigger>
+ </Style.Triggers>
+ </Style>
+ </Border.Style>
+ </Border>
+
+ <TextBox IsEnabled="{Binding IsFree}" Padding="5 0 0 0" Text="{Binding CurrentPath,UpdateSourceTrigger=PropertyChanged,Mode=TwoWay}" Foreground="{StaticResource FSE_PrimaryForegroundBrush}" CaretBrush="{StaticResource FSE_PrimaryForegroundBrush}" Background="Transparent" BorderThickness="0" VerticalContentAlignment="Center" Style="{x:Null}">
+ <TextBox.InputBindings>
+ <KeyBinding Key="Return" Command="{Binding NavigateCommand}" />
+ </TextBox.InputBindings>
+ </TextBox>
+ </Grid>
+ </Border>
+ </Border>
+ </DockPanel>
+ </DockPanel>
+
+ <Grid Grid.Row="1" Margin="0 10 0 0">
+ <Grid.ColumnDefinitions>
+ <ColumnDefinition Width="200" />
+ <ColumnDefinition Width="5" />
+ <ColumnDefinition Width="1*" />
+ </Grid.ColumnDefinitions>
+
+ <Border 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">
+ <ItemsControl ItemsSource="{Binding Drives}">
+ <ItemsControl.ItemTemplate>
+ <DataTemplate>
+ <controls:TextIconButton HorizontalContentAlignment="Left" Padding="5" Icon="Harddisk" Background="Transparent" BorderThickness="0" FocusVisualStyle="{x:Null}" Content="{Binding Label}" Command="{Binding RelativeSource={RelativeSource AncestorType=UserControl},Path=DataContext.NavigateToFolderCommand}" CommandParameter="{Binding Path}"></controls:TextIconButton>
+ </DataTemplate>
+ </ItemsControl.ItemTemplate>
+ </ItemsControl>
+ </StackPanel>
+
+ <controls:TextIconButton HorizontalContentAlignment="Left" Padding="5" Icon="Computer" Background="Transparent" BorderThickness="0" FocusVisualStyle="{x:Null}">Computer</controls:TextIconButton>
+ <StackPanel Margin="30 0 0 0">
+ <controls:TextIconButton HorizontalContentAlignment="Left" Padding="5" Icon="DesktopMac" Background="Transparent" BorderThickness="0" FocusVisualStyle="{x:Null}" Command="{Binding NavigateSpecialFolderCommand}" CommandParameter="DesktopDirectory">Desktop</controls:TextIconButton>
+ <controls:TextIconButton HorizontalContentAlignment="Left" Padding="5" Icon="FileDocument" Background="Transparent" BorderThickness="0" FocusVisualStyle="{x:Null}" Command="{Binding NavigateSpecialFolderCommand}" CommandParameter="MyDocuments">Documents</controls:TextIconButton>
+ <controls:TextIconButton HorizontalContentAlignment="Left" Padding="5" Icon="Application" Background="Transparent" BorderThickness="0" FocusVisualStyle="{x:Null}" Command="{Binding NavigateToFolderCommand}" CommandParameter='%appdata%\Twine\Tango'>Application Data</controls:TextIconButton>
+ </StackPanel>
+ </StackPanel>
+ </Border>
+
+ <Grid Margin="10 0 0 0" Grid.Column="2">
+ <Grid.RowDefinitions>
+ <RowDefinition Height="1*" MinHeight="100" />
+ <RowDefinition Height="5" />
+ <RowDefinition Height="100" MinHeight="0" />
+ </Grid.RowDefinitions>
+ <controls:FileSystemControl
+ AllowDrag="True"
+ AllowDrop="True"
+ CurrentItem="{Binding CurrentItem}"
+ Mode="{Binding ElementName=listView,Path=SelectedItem.Tag}"
+ ItemDoubleClickedCommand="{Binding OpenItemCommand}"
+ DeleteCommand="{Binding DeleteCommand}"
+ SelectedItems="{Binding SelectedItems}"
+ DragCommand="{Binding DragCommand}"
+ DropCommand="{Binding DropCommand}"/>
+
+ <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}">
+ <Run>(</Run><Run Text="{Binding FileSystemHandlers.Count,Mode=OneWay}"></Run><Run>)</Run>
+ </TextBlock>
+ </StackPanel>
+ <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">
+ <ItemsControl.ItemTemplate>
+ <DataTemplate>
+ <Border Margin="5 5 5 0" CornerRadius="3" BorderBrush="{StaticResource FSE_PrimaryBackgroundLightBrush}" BorderThickness="2" Padding="10">
+ <DockPanel Height="40">
+ <Grid DockPanel.Dock="Left" Width="40">
+ <Image HorizontalAlignment="Left" Source="{Binding FileSystemItem.Icon}" RenderOptions.BitmapScalingMode="Fant" Stretch="Uniform" />
+ <Ellipse Margin="-5 -5 0 0" Fill="{StaticResource FSE_PrimaryBackgroundDarkBrush}" HorizontalAlignment="Left" VerticalAlignment="Top" Width="20" Height="20" />
+ <material:PackIcon Margin="-3 -3 0 0" HorizontalAlignment="Left" VerticalAlignment="Top" Width="15" Height="15">
+ <material:PackIcon.Style>
+ <Style TargetType="material:PackIcon">
+ <Setter Property="Kind" Value="Hourglass"></Setter>
+ <Setter Property="Foreground" Value="{StaticResource FSE_GrayBrush}"></Setter>
+ <Style.Triggers>
+ <DataTrigger Binding="{Binding Status}" Value="Downloading">
+ <Setter Property="Kind" Value="Download"></Setter>
+ <Setter Property="Foreground" Value="{StaticResource FSE_PrimaryForegroundBrush}"></Setter>
+ </DataTrigger>
+ <DataTrigger Binding="{Binding Status}" Value="Uploading">
+ <Setter Property="Kind" Value="Upload"></Setter>
+ <Setter Property="Foreground" Value="{StaticResource FSE_PrimaryForegroundBrush}"></Setter>
+ </DataTrigger>
+ <DataTrigger Binding="{Binding Status}" Value="Paused">
+ <Setter Property="Kind" Value="Pause"></Setter>
+ <Setter Property="Foreground" Value="{StaticResource FSE_OrangeBrush}"></Setter>
+ </DataTrigger>
+ <DataTrigger Binding="{Binding Status}" Value="Failed">
+ <Setter Property="Kind" Value="Alert"></Setter>
+ <Setter Property="Foreground" Value="{StaticResource FSE_ErrorBrush}"></Setter>
+ </DataTrigger>
+ <DataTrigger Binding="{Binding Status}" Value="Aborted">
+ <Setter Property="Kind" Value="Alert"></Setter>
+ <Setter Property="Foreground" Value="{StaticResource FSE_WarningBrush}"></Setter>
+ </DataTrigger>
+ <DataTrigger Binding="{Binding Status}" Value="Completed">
+ <Setter Property="Kind" Value="Check"></Setter>
+ <Setter Property="Foreground" Value="{StaticResource FSE_SuccessBrush}"></Setter>
+ </DataTrigger>
+ </Style.Triggers>
+ </Style>
+ </material:PackIcon.Style>
+ </material:PackIcon>
+ </Grid>
+
+ <Grid DockPanel.Dock="Right" Width="140" Margin="20 0 0 0">
+ <UniformGrid Columns="3">
+ <Grid>
+ <controls:ToggleIconButton Width="24" Height="24" UncheckedForeground="{StaticResource FSE_OrangeBrush}" CheckedForeground="{StaticResource FSE_GreenBrush}" UncheckedIcon="Pause" CheckedIcon="Play" Cursor="Hand" IsChecked="{Binding IsPaused,Mode=TwoWay}">
+ <controls:ToggleIconButton.Style>
+ <Style TargetType="controls:ToggleIconButton" BasedOn="{StaticResource {x:Type controls:ToggleIconButton}}">
+ <Setter Property="IsEnabled" Value="False"></Setter>
+ <Setter Property="Visibility" Value="Visible"></Setter>
+ <Setter Property="Opacity" Value="0.2"></Setter>
+ <Style.Triggers>
+ <DataTrigger Binding="{Binding Status}" Value="Downloading">
+ <Setter Property="IsEnabled" Value="True"></Setter>
+ <Setter Property="Opacity" Value="1"></Setter>
+ </DataTrigger>
+ <DataTrigger Binding="{Binding Status}" Value="Uploading">
+ <Setter Property="IsEnabled" Value="True"></Setter>
+ <Setter Property="Opacity" Value="1"></Setter>
+ </DataTrigger>
+ <DataTrigger Binding="{Binding Status}" Value="Paused">
+ <Setter Property="IsEnabled" Value="True"></Setter>
+ <Setter Property="Opacity" Value="1"></Setter>
+ </DataTrigger>
+ <DataTrigger Binding="{Binding Status}" Value="Failed">
+ <Setter Property="Visibility" Value="Hidden"></Setter>
+ </DataTrigger>
+ </Style.Triggers>
+ </Style>
+ </controls:ToggleIconButton.Style>
+ </controls:ToggleIconButton>
+ <controls:IconButton Icon="Restart" Cursor="Hand" Command="{Binding RelativeSource={RelativeSource AncestorType=UserControl},Path=DataContext.RetryFailedFileSystemHandlerCommand}" CommandParameter="{Binding}">
+ <controls:IconButton.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>
+ </controls:IconButton.Style>
+ </controls:IconButton>
+ </Grid>
+ <controls:IconButton Icon="FolderOpen" Cursor="Hand" Command="{Binding RelativeSource={RelativeSource AncestorType=UserControl},Path=DataContext.OpenFileSystemHandlerDestinationCommand}" CommandParameter="{Binding}" />
+ <controls:IconButton Icon="DeleteForever" Foreground="{StaticResource FSE_RedBrush}" Cursor="Hand" Command="{Binding RelativeSource={RelativeSource AncestorType=UserControl},Path=DataContext.DeleteFileSystemHandlerCommand}" CommandParameter="{Binding}" />
+ </UniformGrid>
+ </Grid>
+
+ <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>
+
+ <Border VerticalAlignment="Bottom" Height="5" Background="{StaticResource FSE_PrimaryBackgroundBrush}">
+ <ProgressBar Height="Auto" Minimum="0" Maximum="{Binding Length}" Value="{Binding Position}">
+ <ProgressBar.Style>
+ <Style TargetType="ProgressBar" BasedOn="{StaticResource {x:Type ProgressBar}}">
+ <Setter Property="IsIndeterminate" Value="False"></Setter>
+ <Style.Triggers>
+ <DataTrigger Binding="{Binding Status}" Value="Pending">
+ <Setter Property="IsIndeterminate" Value="True"></Setter>
+ </DataTrigger>
+ </Style.Triggers>
+ </Style>
+ </ProgressBar.Style>
+ </ProgressBar>
+ </Border>
+
+
+ </DockPanel>
+
+ <TextBlock FontSize="{StaticResource FSE_SmallFontSize}" HorizontalAlignment="Right" VerticalAlignment="Top" Text="{Binding Status}"></TextBlock>
+ </Grid>
+ </DockPanel>
+ </Border>
+ </DataTemplate>
+ </ItemsControl.ItemTemplate>
+ </ItemsControl>
+ </ScrollViewer>
+ </Border>
+ </DockPanel>
+ </Grid>
+ </Grid>
+ </Grid>
+ </Grid>
+ </Grid>
+</UserControl>
diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/Views/FileSystemView.xaml.cs b/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/Views/FileSystemView.xaml.cs
new file mode 100644
index 000000000..87fc7e30c
--- /dev/null
+++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/Views/FileSystemView.xaml.cs
@@ -0,0 +1,42 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Navigation;
+using System.Windows.Shapes;
+
+namespace Tango.FSE.PPCConsole.Views
+{
+ /// <summary>
+ /// Interaction logic for FileSystemView.xaml
+ /// </summary>
+ public partial class FileSystemView : UserControl
+ {
+ private object _lastSelectedItem;
+
+ public FileSystemView()
+ {
+ InitializeComponent();
+ }
+
+ private void ListView_SelectionChanged(object sender, SelectionChangedEventArgs e)
+ {
+ if (listView.SelectedItem != null)
+ {
+ _lastSelectedItem = listView.SelectedItem;
+ }
+ else
+ {
+ listView.SelectedItem = _lastSelectedItem;
+ }
+ }
+ }
+}
diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/Views/MonitoringView.xaml b/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/Views/MonitoringView.xaml
new file mode 100644
index 000000000..22e2566b5
--- /dev/null
+++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/Views/MonitoringView.xaml
@@ -0,0 +1,205 @@
+<UserControl x:Class="Tango.FSE.PPCConsole.Views.MonitoringView"
+ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+ xmlns:graphs="clr-namespace:Tango.FSE.Common.Graphs;assembly=Tango.FSE.Common"
+ xmlns:global="clr-namespace:Tango.FSE.PPCConsole"
+ xmlns:lvc="clr-namespace:LiveCharts.Wpf;assembly=LiveCharts.Wpf"
+ xmlns:vm="clr-namespace:Tango.FSE.PPCConsole.ViewModels"
+ xmlns:converters="clr-namespace:Tango.FSE.PPCConsole.Converters"
+ xmlns:controls="clr-namespace:Tango.FSE.Common.Controls;assembly=Tango.FSE.Common"
+ xmlns:local="clr-namespace:Tango.FSE.PPCConsole.Views"
+ mc:Ignorable="d"
+ d:DesignHeight="720" d:DesignWidth="1280" d:DataContext="{d:DesignInstance Type=vm:MonitoringViewVM, IsDesignTimeCreatable=False}" DataContext="{x:Static global:ViewModelLocator.MonitoringViewVM}">
+
+ <UserControl.Resources>
+
+ <converters:DoubleToChartValuesConverter x:Key="DoubleToChartValuesConverter" />
+
+ <Style x:Key="DynamicResolutionGraph" TargetType="graphs:RealTimeGraph" BasedOn="{StaticResource {x:Type graphs:RealTimeGraph}}">
+ <Setter Property="VerticalTicks" Value="11"></Setter>
+ <Style.Triggers>
+ <DataTrigger Binding="{Binding ResolutionService.IsLowResolution}" Value="True">
+ <Setter Property="VerticalTicks" Value="5"></Setter>
+ </DataTrigger>
+ </Style.Triggers>
+ </Style>
+
+ <Style x:Key="ItemsPanel_Legends" TargetType="ItemsControl">
+ <Setter Property="Margin" Value="40"></Setter>
+ <Setter Property="HorizontalAlignment" Value="Right"></Setter>
+ <Setter Property="VerticalAlignment" Value="Top"></Setter>
+ <Setter Property="ItemsPanel">
+ <Setter.Value>
+ <ItemsPanelTemplate>
+ <StackPanel Orientation="Horizontal"></StackPanel>
+ </ItemsPanelTemplate>
+ </Setter.Value>
+ </Setter>
+ <Setter Property="ItemTemplate">
+ <Setter.Value>
+ <DataTemplate>
+ <StackPanel Orientation="Horizontal" Margin="0 0 20 0">
+ <Ellipse Width="10" Height="10" Fill="{Binding Fill}" />
+ <TextBlock Opacity="0.5" FontWeight="SemiBold" Margin="5 0 0 0" FontSize="{StaticResource FSE_SmallFontSize}" Text="{Binding Name}"></TextBlock>
+ </StackPanel>
+ </DataTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+ </UserControl.Resources>
+
+ <Grid>
+ <Grid Margin="20 20 20 0">
+ <Grid.RowDefinitions>
+ <RowDefinition Height="1*"/>
+ <RowDefinition Height="1*"/>
+ </Grid.RowDefinitions>
+ <Grid.ColumnDefinitions>
+ <ColumnDefinition Width="1*"/>
+ <ColumnDefinition Width="1*"/>
+ </Grid.ColumnDefinitions>
+
+ <Grid Margin="0 0 20 0">
+ <graphs:RealTimeGraph Style="{StaticResource DynamicResolutionGraph}" Controller="{Binding CPUController}" StringFormat="0\%"/>
+
+ <ItemsControl Style="{StaticResource ItemsPanel_Legends}" ItemsSource="{Binding CPUController.DataSeriesCollection}"/>
+
+ <Viewbox IsHitTestVisible="False" Margin="90 50 90 90" Stretch="Uniform">
+ <Grid Width="200" Height="200">
+ <Ellipse Stroke="#38FFFFFF" StrokeThickness="20" />
+
+ <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
+ <TextBlock Opacity="0.8" HorizontalAlignment="Center" VerticalAlignment="Center" FontWeight="SemiBold" FontSize="25" Text="{Binding CPUController.DataSeriesCollection[0].CurrentValue,StringFormat='\{0\}%'}"></TextBlock>
+ <Rectangle Width="120" Margin="0 5" VerticalAlignment="Center" Stroke="{StaticResource FSE_PrimaryForegroundBrush}" StrokeThickness="1" />
+ <TextBlock Opacity="0.8" HorizontalAlignment="Center" VerticalAlignment="Center" FontWeight="SemiBold" FontSize="25" Text="{Binding CPUController.DataSeriesCollection[1].CurrentValue,StringFormat='\{0\}%'}"></TextBlock>
+ </StackPanel>
+ </Grid>
+ </Viewbox>
+
+ <TextBlock Foreground="{StaticResource FSE_PrimaryAccentBrush}" Margin="100 30" HorizontalAlignment="Left" VerticalAlignment="Top" FontSize="{StaticResource FSE_LargeFontSize}" FontWeight="SemiBold">CPU</TextBlock>
+ </Grid>
+
+ <Grid Margin="20 0 0 0" Grid.Column="1">
+ <graphs:RealTimeGraph Style="{StaticResource DynamicResolutionGraph}" Controller="{Binding RAMController}" StringFormat="0 \MB" />
+
+ <ItemsControl Style="{StaticResource ItemsPanel_Legends}" ItemsSource="{Binding RAMController.DataSeriesCollection}"/>
+
+ <Viewbox IsHitTestVisible="False" Margin="90 50 90 90" Stretch="Uniform">
+ <Grid Width="200" Height="200">
+ <Ellipse Stroke="#38FFFFFF" StrokeThickness="20" />
+
+ <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
+ <TextBlock Opacity="0.8" HorizontalAlignment="Center" VerticalAlignment="Center" FontWeight="SemiBold" FontSize="25" Text="{Binding RAMController.DataSeriesCollection[0].CurrentValue,StringFormat='\{0\} MB'}"></TextBlock>
+ <Rectangle Width="120" Margin="0 5" VerticalAlignment="Center" Stroke="{StaticResource FSE_PrimaryForegroundBrush}" StrokeThickness="1" />
+ <TextBlock Opacity="0.8" HorizontalAlignment="Center" VerticalAlignment="Center" FontWeight="SemiBold" FontSize="25" Text="{Binding RAMController.DataSeriesCollection[1].CurrentValue,StringFormat='\{0\} MB'}"></TextBlock>
+ </StackPanel>
+ </Grid>
+ </Viewbox>
+
+ <TextBlock Foreground="{StaticResource FSE_PrimaryAccentBrush}" Margin="100 30" HorizontalAlignment="Left" VerticalAlignment="Top" FontSize="{StaticResource FSE_LargeFontSize}" FontWeight="SemiBold">RAM</TextBlock>
+ </Grid>
+
+ <Grid Grid.Row="1" Grid.ColumnSpan="2" Margin="0 40 0 0">
+ <Grid.ColumnDefinitions>
+ <ColumnDefinition Width="2*"/>
+ <ColumnDefinition Width="8*"/>
+ </Grid.ColumnDefinitions>
+
+ <Grid>
+ <DockPanel>
+
+ <TextBlock DockPanel.Dock="Top" Foreground="{StaticResource FSE_GrayBrush}" HorizontalAlignment="Left" VerticalAlignment="Top" FontSize="{StaticResource FSE_LargeFontSize}" FontWeight="SemiBold">AVAILABLE DISK SPACE</TextBlock>
+
+ <lvc:PieChart Opacity="0.7" DisableAnimations="True" LegendLocation="Bottom" Hoverable="False" DataTooltip="{x:Null}" HorizontalAlignment="Left" Width="250">
+ <lvc:PieChart.Series>
+ <lvc:PieSeries Foreground="{StaticResource FSE_PrimaryForegroundBrush}" Stroke="{StaticResource FSE_PrimaryBackgroundBrush}" Fill="#F47FD121" FontSize="16" Title="Free" Values="{Binding AvailableDiskSpace,Converter={StaticResource DoubleToChartValuesConverter}}" DataLabels="True"
+ LabelPoint="{Binding DiskSpacePointLabel}"/>
+ <lvc:PieSeries Foreground="{StaticResource FSE_PrimaryForegroundBrush}" Stroke="{StaticResource FSE_PrimaryBackgroundBrush}" Fill="#FF4F4F" FontSize="16" Title="Used" Values="{Binding UsedDiskSpace,Converter={StaticResource DoubleToChartValuesConverter}}" DataLabels="True"
+ LabelPoint="{Binding DiskSpacePointLabel}"/>
+ </lvc:PieChart.Series>
+ </lvc:PieChart>
+ </DockPanel>
+ </Grid>
+
+ <Grid Grid.Column="1">
+ <DockPanel>
+ <TextBlock DockPanel.Dock="Top" Foreground="{StaticResource FSE_GrayBrush}" HorizontalAlignment="Left" VerticalAlignment="Top" FontSize="{StaticResource FSE_LargeFontSize}" FontWeight="SemiBold">SYSTEM INFORMATION</TextBlock>
+
+ <controls:FSEPanel Margin="0 10 0 0" Padding="20" >
+ <Grid>
+ <Grid>
+ <Grid.ColumnDefinitions>
+ <ColumnDefinition Width="270"/>
+ <ColumnDefinition Width="1*"/>
+ </Grid.ColumnDefinitions>
+
+ <ListBox ItemsSource="{Binding SystemInfo.System}" SelectedItem="{Binding SelectedSystemObjectCollection}">
+ <ListBox.ItemsPanel>
+ <ItemsPanelTemplate>
+ <WrapPanel/>
+ </ItemsPanelTemplate>
+ </ListBox.ItemsPanel>
+ <ListBox.ItemTemplate>
+ <DataTemplate>
+ <TextBlock Width="115" Text="{Binding Name}"></TextBlock>
+ </DataTemplate>
+ </ListBox.ItemTemplate>
+ </ListBox>
+
+ <ScrollViewer Margin="40 0 0 0" Grid.Column="1" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled">
+ <ItemsControl ItemsSource="{Binding SelectedSystemObjectCollection.Objects}">
+ <ItemsControl.ItemsPanel>
+ <ItemsPanelTemplate>
+ <WrapPanel Orientation="Horizontal" IsItemsHost="True"></WrapPanel>
+ </ItemsPanelTemplate>
+ </ItemsControl.ItemsPanel>
+ <ItemsControl.ItemTemplate>
+ <DataTemplate>
+ <DockPanel Margin="10 0 10 10" Width="400">
+ <TextBlock TextTrimming="CharacterEllipsis" DockPanel.Dock="Top" Text="{Binding Name}" Foreground="{StaticResource FSE_GrayBrush}"></TextBlock>
+ <Border TextElement.FontSize="{StaticResource FSE_SmallFontSize}" Margin="0 5 0 0" Padding="3" BorderThickness="0" BorderBrush="{StaticResource FSE_BorderBrush}" CornerRadius="5">
+ <ItemsControl Opacity="0.7" ItemsSource="{Binding Properties}">
+ <ItemsControl.ItemsPanel>
+ <ItemsPanelTemplate>
+ <WrapPanel Orientation="Vertical" />
+ </ItemsPanelTemplate>
+ </ItemsControl.ItemsPanel>
+ <ItemsControl.ItemTemplate>
+ <DataTemplate>
+ <DockPanel>
+ <DockPanel.ToolTip>
+ <TextBlock>
+ <Run FontWeight="SemiBold" Text="{Binding Name}"></Run><Run>:</Run>
+ <Run Text="{Binding Value}"></Run>
+ </TextBlock>
+ </DockPanel.ToolTip>
+ <TextBlock TextTrimming="CharacterEllipsis" Width="170" FontWeight="SemiBold" HorizontalAlignment="Left">
+ <Run Text="{Binding Name}"></Run><Run>:</Run>
+ </TextBlock>
+ <TextBlock TextTrimming="CharacterEllipsis" Margin="20 0 0 0" HorizontalAlignment="Left" Text="{Binding Value}"></TextBlock>
+ </DockPanel>
+ </DataTemplate>
+ </ItemsControl.ItemTemplate>
+ </ItemsControl>
+ </Border>
+ </DockPanel>
+ </DataTemplate>
+ </ItemsControl.ItemTemplate>
+ </ItemsControl>
+ </ScrollViewer>
+ </Grid>
+
+ <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center" Visibility="{Binding FetchingSystemInfo,Converter={StaticResource BooleanToVisibilityConverter}}">
+ <ProgressBar HorizontalAlignment="Center" Style="{StaticResource FSE_CircularProgressBar}" IsIndeterminate="{Binding FetchingSystemInfo}" Width="80" Height="80" />
+ <TextBlock HorizontalAlignment="Center" Margin="0 10 0 0" Text="Fetching System Information..."></TextBlock>
+ </StackPanel>
+ </Grid>
+ </controls:FSEPanel>
+ </DockPanel>
+ </Grid>
+ </Grid>
+ </Grid>
+ </Grid>
+</UserControl>
diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/Views/MonitoringView.xaml.cs b/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/Views/MonitoringView.xaml.cs
new file mode 100644
index 000000000..84672dd50
--- /dev/null
+++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/Views/MonitoringView.xaml.cs
@@ -0,0 +1,33 @@
+using LiveCharts;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Navigation;
+using System.Windows.Shapes;
+
+namespace Tango.FSE.PPCConsole.Views
+{
+ /// <summary>
+ /// Interaction logic for MonitoringView.xaml
+ /// </summary>
+ public partial class MonitoringView : UserControl
+ {
+ public Func<ChartPoint, string> PointLabel { get; set; }
+
+ public MonitoringView()
+ {
+ PointLabel = chartPoint => string.Format("{0} ({1:P})", chartPoint.Y, chartPoint.Participation);
+
+ InitializeComponent();
+ }
+ }
+}
diff --git a/Software/Visual_Studio/FSE/Tango.FSE.Common/Console/IConsoleService.cs b/Software/Visual_Studio/FSE/Tango.FSE.Common/Console/IConsoleService.cs
new file mode 100644
index 000000000..321b183d8
--- /dev/null
+++ b/Software/Visual_Studio/FSE/Tango.FSE.Common/Console/IConsoleService.cs
@@ -0,0 +1,16 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Tango.Console.Network;
+
+namespace Tango.FSE.Common.Console
+{
+ public interface IConsoleService
+ {
+ event EventHandler<GetCurrentDirectoryResponse> Initialized;
+ String CurrentDirectory { get; }
+ Task<ConsoleCommandResponse> ExecuteCommand(ConsoleCommandRequest request, TimeSpan? timeout = null);
+ }
+}
diff --git a/Software/Visual_Studio/FSE/Tango.FSE.Common/Controls/FSEPanel.cs b/Software/Visual_Studio/FSE/Tango.FSE.Common/Controls/FSEPanel.cs
new file mode 100644
index 000000000..3eaabb595
--- /dev/null
+++ b/Software/Visual_Studio/FSE/Tango.FSE.Common/Controls/FSEPanel.cs
@@ -0,0 +1,49 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Navigation;
+using System.Windows.Shapes;
+
+namespace Tango.FSE.Common.Controls
+{
+ public class FSEPanel : ContentControl
+ {
+ public CornerRadius CornerRadius
+ {
+ get { return (CornerRadius)GetValue(CornerRadiusProperty); }
+ set { SetValue(CornerRadiusProperty, value); }
+ }
+ public static readonly DependencyProperty CornerRadiusProperty =
+ DependencyProperty.Register("CornerRadius", typeof(CornerRadius), typeof(FSEPanel), new PropertyMetadata(default(CornerRadius)));
+
+ public double ScrewSize
+ {
+ get { return (double)GetValue(ScrewSizeProperty); }
+ set { SetValue(ScrewSizeProperty, value); }
+ }
+ public static readonly DependencyProperty ScrewSizeProperty =
+ DependencyProperty.Register("ScrewSize", typeof(double), typeof(FSEPanel), new PropertyMetadata(10.0));
+
+ public Thickness ScrewMargin
+ {
+ get { return (Thickness)GetValue(ScrewMarginProperty); }
+ set { SetValue(ScrewMarginProperty, value); }
+ }
+ public static readonly DependencyProperty ScrewMarginProperty =
+ DependencyProperty.Register("ScrewMargin", typeof(Thickness), typeof(FSEPanel), new PropertyMetadata(default(Thickness)));
+
+ static FSEPanel()
+ {
+ DefaultStyleKeyProperty.OverrideMetadata(typeof(FSEPanel), new FrameworkPropertyMetadata(typeof(FSEPanel)));
+ }
+ }
+}
diff --git a/Software/Visual_Studio/FSE/Tango.FSE.Common/Controls/FSEPanel.xaml b/Software/Visual_Studio/FSE/Tango.FSE.Common/Controls/FSEPanel.xaml
new file mode 100644
index 000000000..986899490
--- /dev/null
+++ b/Software/Visual_Studio/FSE/Tango.FSE.Common/Controls/FSEPanel.xaml
@@ -0,0 +1,36 @@
+<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:local="clr-namespace:Tango.FSE.Common.Controls">
+
+ <Style TargetType="{x:Type local:FSEPanel}">
+ <Setter Property="BorderBrush" Value="{StaticResource FSE_Panel_BorderBrush}"></Setter>
+ <Setter Property="BorderThickness" Value="1"></Setter>
+ <Setter Property="Background" Value="{StaticResource FSE_Panel_BackgroundBrush}"></Setter>
+ <Setter Property="CornerRadius" Value="5"></Setter>
+ <Setter Property="ScrewSize" Value="10"></Setter>
+ <Setter Property="ScrewMargin" Value="8"></Setter>
+ <Setter Property="Padding" Value="15"></Setter>
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate TargetType="{x:Type local:FSEPanel}">
+ <Grid>
+ <Border Background="{TemplateBinding Background}"
+ BorderBrush="{TemplateBinding BorderBrush}"
+ BorderThickness="{TemplateBinding BorderThickness}"
+ Padding="{TemplateBinding Padding}"
+ CornerRadius="{TemplateBinding CornerRadius}">
+ <ContentPresenter Content="{TemplateBinding Content}"/>
+ </Border>
+
+ <Image HorizontalAlignment="Left" VerticalAlignment="Top" Margin="{TemplateBinding ScrewMargin}" Source="{StaticResource FSE_Screw}" RenderOptions.BitmapScalingMode="Fant" Width="{TemplateBinding ScrewSize}" Height="{TemplateBinding ScrewSize}" />
+ <Image HorizontalAlignment="Right" VerticalAlignment="Top" Margin="{TemplateBinding ScrewMargin}" Source="{StaticResource FSE_Screw}" RenderOptions.BitmapScalingMode="Fant" Width="{TemplateBinding ScrewSize}" Height="{TemplateBinding ScrewSize}" />
+
+ <Image HorizontalAlignment="Right" VerticalAlignment="Bottom" Margin="{TemplateBinding ScrewMargin}" Source="{StaticResource FSE_Screw}" RenderOptions.BitmapScalingMode="Fant" Width="{TemplateBinding ScrewSize}" Height="{TemplateBinding ScrewSize}" />
+ <Image HorizontalAlignment="Left" VerticalAlignment="Bottom" Margin="{TemplateBinding ScrewMargin}" Source="{StaticResource FSE_Screw}" RenderOptions.BitmapScalingMode="Fant" Width="{TemplateBinding ScrewSize}" Height="{TemplateBinding ScrewSize}" />
+ </Grid>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+
+</ResourceDictionary> \ No newline at end of file
diff --git a/Software/Visual_Studio/FSE/Tango.FSE.Common/Controls/FSETabControl.cs b/Software/Visual_Studio/FSE/Tango.FSE.Common/Controls/FSETabControl.cs
new file mode 100644
index 000000000..44cfd4872
--- /dev/null
+++ b/Software/Visual_Studio/FSE/Tango.FSE.Common/Controls/FSETabControl.cs
@@ -0,0 +1,106 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Markup;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Navigation;
+using System.Windows.Shapes;
+using Tango.SharedUI;
+using Tango.SharedUI.Controls;
+using static Tango.SharedUI.Controls.NavigationControl;
+
+namespace Tango.FSE.Common.Controls
+{
+ [ContentProperty(nameof(Elements))]
+ public class FSETabControl : Control
+ {
+ private NavigationControl _navigationControl;
+
+ public ObservableCollection<FrameworkElement> Elements
+ {
+ get { return (ObservableCollection<FrameworkElement>)GetValue(ElementsProperty); }
+ set { SetValue(ElementsProperty, value); }
+ }
+ public static readonly DependencyProperty ElementsProperty =
+ DependencyProperty.Register("Elements", typeof(ObservableCollection<FrameworkElement>), typeof(FSETabControl), new PropertyMetadata(null));
+
+ public FrameworkElement SelectedElement
+ {
+ get { return (FrameworkElement)GetValue(SelectedElementProperty); }
+ set { SetValue(SelectedElementProperty, value); }
+ }
+ public static readonly DependencyProperty SelectedElementProperty =
+ DependencyProperty.Register("SelectedElement", typeof(FrameworkElement), typeof(FSETabControl), new PropertyMetadata(null));
+
+ public TransitionTypes TransitionType
+ {
+ get { return (TransitionTypes)GetValue(TransitionTypeProperty); }
+ set { SetValue(TransitionTypeProperty, value); }
+ }
+ public static readonly DependencyProperty TransitionTypeProperty =
+ DependencyProperty.Register("TransitionType", typeof(TransitionTypes), typeof(FSETabControl), new PropertyMetadata(TransitionTypes.Slide));
+
+ public bool TransitionAlwaysFades
+ {
+ get { return (bool)GetValue(TransitionAlwaysFadesProperty); }
+ set { SetValue(TransitionAlwaysFadesProperty, value); }
+ }
+ public static readonly DependencyProperty TransitionAlwaysFadesProperty =
+ DependencyProperty.Register("TransitionAlwaysFades", typeof(bool), typeof(FSETabControl), new PropertyMetadata(false));
+
+ public Duration TransitionDuration
+ {
+ get { return (Duration)GetValue(TransitionDurationProperty); }
+ set { SetValue(TransitionDurationProperty, value); }
+ }
+ public static readonly DependencyProperty TransitionDurationProperty =
+ DependencyProperty.Register("TransitionDuration", typeof(Duration), typeof(FSETabControl), new PropertyMetadata(new Duration(TimeSpan.FromSeconds(0.2))));
+
+ public object SelectedObject
+ {
+ get { return (object)GetValue(SelectedObjectProperty); }
+ set { SetValue(SelectedObjectProperty, value); }
+ }
+ public static readonly DependencyProperty SelectedObjectProperty =
+ DependencyProperty.Register("SelectedObject", typeof(object), typeof(FSETabControl), new PropertyMetadata(null));
+
+ public double TabsWidth
+ {
+ get { return (double)GetValue(TabsWidthProperty); }
+ set { SetValue(TabsWidthProperty, value); }
+ }
+ public static readonly DependencyProperty TabsWidthProperty =
+ DependencyProperty.Register("TabsWidth", typeof(double), typeof(FSETabControl), new PropertyMetadata(double.NaN));
+
+ public FSETabControl()
+ {
+ Elements = new ObservableCollection<FrameworkElement>();
+ }
+
+ public override void OnApplyTemplate()
+ {
+ base.OnApplyTemplate();
+ _navigationControl = GetTemplateChild("navigation") as NavigationControl;
+ _navigationControl.SelectedElementChanged += (x, e) =>
+ {
+ SelectedElement = e;
+ };
+
+ _navigationControl.Elements = Elements;
+ }
+
+ static FSETabControl()
+ {
+ DefaultStyleKeyProperty.OverrideMetadata(typeof(FSETabControl), new FrameworkPropertyMetadata(typeof(FSETabControl)));
+ }
+ }
+}
diff --git a/Software/Visual_Studio/FSE/Tango.FSE.Common/Controls/FSETabControl.xaml b/Software/Visual_Studio/FSE/Tango.FSE.Common/Controls/FSETabControl.xaml
new file mode 100644
index 000000000..73aa05931
--- /dev/null
+++ b/Software/Visual_Studio/FSE/Tango.FSE.Common/Controls/FSETabControl.xaml
@@ -0,0 +1,106 @@
+<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:controls="clr-namespace:Tango.SharedUI.Controls;assembly=Tango.SharedUI"
+ xmlns:local="clr-namespace:Tango.FSE.Common.Controls">
+
+ <Style TargetType="{x:Type local:FSETabControl}">
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate TargetType="{x:Type local:FSETabControl}">
+ <Border Background="{TemplateBinding Background}"
+ BorderBrush="{TemplateBinding BorderBrush}"
+ BorderThickness="{TemplateBinding BorderThickness}">
+
+ <DockPanel>
+ <Grid DockPanel.Dock="Top" Height="50">
+ <Grid.ColumnDefinitions>
+ <ColumnDefinition Width="1*" />
+ <ColumnDefinition Width="Auto" />
+ <ColumnDefinition Width="1*" />
+ </Grid.ColumnDefinitions>
+ <Rectangle Grid.Column="0" VerticalAlignment="Bottom" StrokeThickness="2" Stroke="{StaticResource FSE_PrimaryAccentDarkBrush}" />
+ <ListBox x:Name="listTabs" Grid.Column="1" DisplayMemberPath="Tag" ItemsSource="{Binding ElementName=navigation,Path=Elements}" SelectedItem="{Binding ElementName=navigation,Path=SelectedElement,Mode=TwoWay}" SelectedIndex="0" Width="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},Path=TabsWidth}">
+ <ListBox.Style>
+ <Style TargetType="ListBox" BasedOn="{StaticResource {x:Type ListBox}}">
+ <Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Disabled"></Setter>
+ <Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Disabled"></Setter>
+ <Setter Property="ItemsPanel">
+ <Setter.Value>
+ <ItemsPanelTemplate>
+ <UniformGrid Margin="15 0 0 0" Columns="{Binding RelativeSource={RelativeSource AncestorType=ListBox},Path=Items.Count}" IsItemsHost="True"></UniformGrid>
+ </ItemsPanelTemplate>
+ </Setter.Value>
+ </Setter>
+ <Setter Property="ItemContainerStyle">
+ <Setter.Value>
+ <Style TargetType="ListBoxItem" BasedOn="{StaticResource {x:Type ListBoxItem}}">
+ <Setter Property="HorizontalContentAlignment" Value="Center"></Setter>
+ <Setter Property="VerticalContentAlignment" Value="Center"></Setter>
+ <Setter Property="Background" Value="{StaticResource FSE_PrimaryBackgroundBrush}"></Setter>
+ <Setter Property="FocusVisualStyle" Value="{x:Null}"></Setter>
+ <Setter Property="Foreground" Value="{StaticResource FSE_GrayBrush}"></Setter>
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate TargetType="ListBoxItem">
+ <Grid x:Name="grid" Margin="-15 0 0 0" Background="Transparent">
+ <Viewbox Stretch="Fill">
+ <Grid>
+ <Polygon Fill="{TemplateBinding Background}" Stretch="Fill" Points="0,30 15,0 85,0 100,30"></Polygon>
+ <Polygon Fill="White" Stretch="Fill" Points="0,30 15,0 85,0 100,30" IsHitTestVisible="False" Opacity="0.1">
+ <Polygon.Style>
+ <Style TargetType="Polygon">
+ <Setter Property="Visibility" Value="Hidden"></Setter>
+ <Style.Triggers>
+ <DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=ListBoxItem},Path=IsMouseOver}" Value="True">
+ <Setter Property="Visibility" Value="Visible"></Setter>
+ </DataTrigger>
+ </Style.Triggers>
+ </Style>
+ </Polygon.Style>
+ </Polygon>
+ <Polyline Stroke="{StaticResource FSE_PrimaryAccentDarkBrush}" StrokeThickness="1" Stretch="Fill" Points="0,30 15,0 85,0 100,30" />
+ <Rectangle VerticalAlignment="Bottom" Stroke="{StaticResource FSE_PrimaryAccentDarkBrush}" StrokeThickness="1" />
+ </Grid>
+ </Viewbox>
+ <ContentPresenter Content="{Binding Tag}" TextElement.Foreground="{TemplateBinding Foreground}" HorizontalAlignment="Center" VerticalAlignment="Center" />
+ </Grid>
+ <ControlTemplate.Triggers>
+ <Trigger Property="IsSelected" Value="True">
+
+ </Trigger>
+ </ControlTemplate.Triggers>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ <Style.Triggers>
+ <Trigger Property="IsSelected" Value="True">
+ <Setter Property="Background" Value="{StaticResource FSE_PrimaryAccentDarkBrush}"></Setter>
+ <Setter Property="Foreground" Value="{StaticResource FSE_PrimaryForegroundBrush}"></Setter>
+ <Setter Property="FontWeight" Value="SemiBold"></Setter>
+ <Setter Property="Panel.ZIndex" Value="200"></Setter>
+ </Trigger>
+ <Trigger Property="IsSelected" Value="False">
+ <Setter Property="Panel.ZIndex" Value="-1"></Setter>
+ </Trigger>
+ </Style.Triggers>
+ </Style>
+ </Setter.Value>
+ </Setter>
+ </Style>
+ </ListBox.Style>
+ </ListBox>
+ <Rectangle Grid.Column="2" VerticalAlignment="Bottom" StrokeThickness="2" Stroke="{StaticResource FSE_PrimaryAccentDarkBrush}" />
+ </Grid>
+
+ <Border Margin="0 -2 0 0" Padding="10">
+ <controls:NavigationControl x:Name="navigation" SelectedObject="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},Path=SelectedObject,Mode=TwoWay}" TransitionAlwaysFades="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},Path=TransitionAlwaysFades}" TransitionType="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},Path=TransitionType}" TransitionDuration="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},Path=TransitionDuration}"/>
+ </Border>
+ </DockPanel>
+
+ </Border>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+
+</ResourceDictionary> \ No newline at end of file
diff --git a/Software/Visual_Studio/FSE/Tango.FSE.Common/Controls/FileSystemControl.cs b/Software/Visual_Studio/FSE/Tango.FSE.Common/Controls/FileSystemControl.cs
new file mode 100644
index 000000000..dc1da7384
--- /dev/null
+++ b/Software/Visual_Studio/FSE/Tango.FSE.Common/Controls/FileSystemControl.cs
@@ -0,0 +1,26 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Navigation;
+using System.Windows.Shapes;
+using Tango.FileSystem;
+
+namespace Tango.FSE.Common.Controls
+{
+ public class FileSystemControl : FileExplorerControl
+ {
+ static FileSystemControl()
+ {
+ DefaultStyleKeyProperty.OverrideMetadata(typeof(FileSystemControl), new FrameworkPropertyMetadata(typeof(FileSystemControl)));
+ }
+ }
+}
diff --git a/Software/Visual_Studio/FSE/Tango.FSE.Common/Controls/FileSystemControl.xaml b/Software/Visual_Studio/FSE/Tango.FSE.Common/Controls/FileSystemControl.xaml
new file mode 100644
index 000000000..7230d97fb
--- /dev/null
+++ b/Software/Visual_Studio/FSE/Tango.FSE.Common/Controls/FileSystemControl.xaml
@@ -0,0 +1,200 @@
+<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+ 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:l="clr-namespace:Tango.FSE.Common.Controls">
+
+ <converters:ByteArrayToFileSizeConverter x:Key="ByteArrayToFileSizeConverter" />
+ <converters:DateTimeUtcToLocalDateTime x:Key="DateTimeUtcToLocalDateTime" />
+
+ <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>
+
+ <Style TargetType="{x:Type local:FileSystemDataGrid}" BasedOn="{StaticResource {x:Type DataGrid}}">
+
+ </Style>
+
+ <Style x:Key="FileSystemCellStyle" TargetType="DataGridCell" BasedOn="{StaticResource {x:Type DataGridCell}}">
+ <Setter Property="BorderThickness" Value="0"/>
+ <Setter Property="FocusVisualStyle" Value="{x:Null}"/>
+ <Setter Property="VerticalContentAlignment" Value="Center"></Setter>
+ </Style>
+
+ <Style TargetType="{x:Type l:FileSystemControl}">
+ <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>
+ <Setter Property="Mode" Value="Details"></Setter>
+ <Setter Property="FocusVisualStyle" Value="{x:Null}"></Setter>
+ <Setter Property="Focusable" Value="True"></Setter>
+ <Setter Property="AllowDrop" Value="True"></Setter>
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate TargetType="{x:Type l:FileSystemControl}">
+ <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>
+ <Style TargetType="ListBox" BasedOn="{StaticResource {x:Type ListBox}}">
+ <Setter Property="Visibility" Value="Collapsed"></Setter>
+ <Style.Triggers>
+ <DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},Path=Mode}" Value="Large">
+ <Setter Property="Visibility" Value="Visible"></Setter>
+ </DataTrigger>
+ </Style.Triggers>
+ </Style>
+ </ListBox.Style>
+ <ListBox.ItemContainerStyle>
+ <Style TargetType="ListBoxItem" BasedOn="{StaticResource {x:Type ListBoxItem}}">
+ <Setter Property="Padding" Value="5"></Setter>
+ <Setter Property="Margin" Value="0 10 20 10"></Setter>
+ </Style>
+ </ListBox.ItemContainerStyle>
+ <ListBox.ItemsPanel>
+ <ItemsPanelTemplate>
+ <WrapPanel Orientation="Horizontal" IsItemsHost="True" />
+ </ItemsPanelTemplate>
+ </ListBox.ItemsPanel>
+ <ListBox.ItemTemplate>
+ <DataTemplate>
+ <DockPanel Width="70" Height="115" Background="Transparent">
+ <DockPanel.InputBindings>
+ <MouseBinding MouseAction="LeftDoubleClick"
+ Command="{Binding RelativeSource={RelativeSource AncestorType=local:FileExplorerControl},Path=ItemDoubleClickedCommand}"
+ CommandParameter="{Binding}" />
+ </DockPanel.InputBindings>
+ <Image DockPanel.Dock="Top" RenderOptions.BitmapScalingMode="Fant" Stretch="Uniform" HorizontalAlignment="Center" VerticalAlignment="Top">
+ <Image.Style>
+ <Style TargetType="Image">
+ <Style.Triggers>
+ <DataTrigger Binding="{Binding Type}" Value="Folder">
+ <Setter Property="Source" Value="{Binding RelativeSource={RelativeSource AncestorType=local:FileExplorerControl},Path=FolderIcon}"></Setter>
+ </DataTrigger>
+ <DataTrigger Binding="{Binding Type}" Value="Drive">
+ <Setter Property="Source" Value="{Binding RelativeSource={RelativeSource AncestorType=local:FileExplorerControl},Path=DriveIcon}"></Setter>
+ </DataTrigger>
+ <DataTrigger Binding="{Binding Type}" Value="File">
+ <Setter Property="Source" Value="{Binding Icon}"></Setter>
+ </DataTrigger>
+ </Style.Triggers>
+ </Style>
+ </Image.Style>
+ </Image>
+ <TextBlock Text="{Binding Name}" TextWrapping="Wrap" TextTrimming="CharacterEllipsis" HorizontalAlignment="Center" TextAlignment="Center" Margin="0 10 0 0"></TextBlock>
+ </DockPanel>
+ </DataTemplate>
+ </ListBox.ItemTemplate>
+ </ListBox>
+
+ <Grid>
+ <Grid.Style>
+ <Style TargetType="Grid">
+ <Setter Property="Visibility" Value="Collapsed"></Setter>
+ <Style.Triggers>
+ <DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},Path=Mode}" Value="Details">
+ <Setter Property="Visibility" Value="Visible"></Setter>
+ </DataTrigger>
+ </Style.Triggers>
+ </Style>
+ </Grid.Style>
+ <local:FileSystemDataGrid x:Name="PART_datagrid" ItemsSource="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},Path=CurrentItem.Items}" CellStyle="{StaticResource FileSystemCellStyle}">
+ <DataGrid.Style>
+ <Style TargetType="DataGrid" BasedOn="{StaticResource {x:Type DataGrid}}">
+ <Setter Property="Background" Value="Transparent"></Setter>
+ <Setter Property="AutoGenerateColumns" Value="False"></Setter>
+ <Setter Property="CanUserAddRows" Value="False"></Setter>
+ <Setter Property="CanUserDeleteRows" Value="False"></Setter>
+ <Setter Property="CanUserReorderColumns" Value="False"></Setter>
+ <Setter Property="CanUserResizeColumns" Value="True"></Setter>
+ <Setter Property="CanUserResizeRows" Value="False"></Setter>
+ <Setter Property="CanUserSortColumns" Value="True"></Setter>
+ <Setter Property="IsReadOnly" Value="True"></Setter>
+ <Setter Property="SelectionMode" Value="Extended"></Setter>
+ <Setter Property="SelectionUnit" Value="FullRow"></Setter>
+ <Setter Property="RowHeight" Value="30"></Setter>
+ <Setter Property="HorizontalGridLinesBrush" Value="Transparent"></Setter>
+ <Setter Property="VerticalGridLinesBrush" Value="Transparent"></Setter>
+ <Setter Property="HorizontalScrollBarVisibility" Value="Disabled"></Setter>
+ <Setter Property="CellStyle" Value="{StaticResource FileSystemCellStyle}" />
+ </Style>
+ </DataGrid.Style>
+
+ <DataGrid.Columns>
+ <DataGridTemplateColumn Header="NAME" Width="300*">
+ <DataGridTemplateColumn.CellTemplate>
+ <DataTemplate>
+ <DockPanel Background="Transparent">
+ <Image Width="18" Height="18" RenderOptions.BitmapScalingMode="Fant" Stretch="Uniform" HorizontalAlignment="Center" VerticalAlignment="Center">
+ <Image.Style>
+ <Style TargetType="Image">
+ <Style.Triggers>
+ <DataTrigger Binding="{Binding Type}" Value="Drive">
+ <Setter Property="Source" Value="{Binding RelativeSource={RelativeSource AncestorType=local:FileExplorerControl},Path=DriveIcon}"></Setter>
+ </DataTrigger>
+ <DataTrigger Binding="{Binding Type}" Value="File">
+ <Setter Property="Source" Value="{Binding SmallIcon}"></Setter>
+ </DataTrigger>
+ <DataTrigger Binding="{Binding Type}" Value="Folder">
+ <Setter Property="Source" Value="{Binding RelativeSource={RelativeSource AncestorType=local:FileExplorerControl},Path=FolderIcon}"></Setter>
+ </DataTrigger>
+ </Style.Triggers>
+ </Style>
+ </Image.Style>
+ </Image>
+ <TextBlock Text="{Binding Name}" VerticalAlignment="Center" TextTrimming="CharacterEllipsis" Margin="5 0 0 0"></TextBlock>
+ </DockPanel>
+ </DataTemplate>
+ </DataGridTemplateColumn.CellTemplate>
+ </DataGridTemplateColumn>
+ <DataGridTemplateColumn Header="DATE MODIFIED" Width="170*">
+ <DataGridTemplateColumn.CellTemplate>
+ <DataTemplate>
+ <DockPanel Background="Transparent">
+ <TextBlock Text="{Binding DateModified,Converter={StaticResource DateTimeUtcToLocalDateTime}}" VerticalAlignment="Center"></TextBlock>
+ </DockPanel>
+ </DataTemplate>
+ </DataGridTemplateColumn.CellTemplate>
+ </DataGridTemplateColumn>
+ <DataGridTemplateColumn Header="TYPE" Width="140*">
+ <DataGridTemplateColumn.CellTemplate>
+ <DataTemplate>
+ <DockPanel Background="Transparent">
+ <TextBlock Text="{Binding Description}" VerticalAlignment="Center"></TextBlock>
+ </DockPanel>
+ </DataTemplate>
+ </DataGridTemplateColumn.CellTemplate>
+ </DataGridTemplateColumn>
+ <DataGridTemplateColumn Header="SIZE" Width="100*">
+ <DataGridTemplateColumn.CellTemplate>
+ <DataTemplate>
+ <DockPanel Background="Transparent">
+ <TextBlock Text="{Binding Size,Converter={StaticResource ByteArrayToFileSizeConverter}}" VerticalAlignment="Center">
+ <TextBlock.Style>
+ <Style TargetType="TextBlock">
+ <Setter Property="Visibility" Value="Hidden"></Setter>
+ <Style.Triggers>
+ <DataTrigger Binding="{Binding Type}" Value="File">
+ <Setter Property="Visibility" Value="Visible"></Setter>
+ </DataTrigger>
+ </Style.Triggers>
+ </Style>
+ </TextBlock.Style>
+ </TextBlock>
+ </DockPanel>
+ </DataTemplate>
+ </DataGridTemplateColumn.CellTemplate>
+ </DataGridTemplateColumn>
+ </DataGrid.Columns>
+ </local:FileSystemDataGrid>
+ </Grid>
+ </Grid>
+ </Border>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+
+</ResourceDictionary> \ No newline at end of file
diff --git a/Software/Visual_Studio/FSE/Tango.FSE.Common/Controls/TextIconButton.cs b/Software/Visual_Studio/FSE/Tango.FSE.Common/Controls/TextIconButton.cs
new file mode 100644
index 000000000..31bcee5cd
--- /dev/null
+++ b/Software/Visual_Studio/FSE/Tango.FSE.Common/Controls/TextIconButton.cs
@@ -0,0 +1,34 @@
+using MaterialDesignThemes.Wpf;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Navigation;
+using System.Windows.Shapes;
+
+namespace Tango.FSE.Common.Controls
+{
+ public class TextIconButton : Button
+ {
+ static TextIconButton()
+ {
+ DefaultStyleKeyProperty.OverrideMetadata(typeof(TextIconButton), new FrameworkPropertyMetadata(typeof(TextIconButton)));
+ }
+
+ public PackIconKind Icon
+ {
+ get { return (PackIconKind)GetValue(IconProperty); }
+ set { SetValue(IconProperty, value); }
+ }
+ public static readonly DependencyProperty IconProperty =
+ DependencyProperty.Register("Icon", typeof(PackIconKind), typeof(TextIconButton), new PropertyMetadata(PackIconKind.BorderNone));
+ }
+}
diff --git a/Software/Visual_Studio/FSE/Tango.FSE.Common/Controls/TextIconButton.xaml b/Software/Visual_Studio/FSE/Tango.FSE.Common/Controls/TextIconButton.xaml
new file mode 100644
index 000000000..eb8d823f7
--- /dev/null
+++ b/Software/Visual_Studio/FSE/Tango.FSE.Common/Controls/TextIconButton.xaml
@@ -0,0 +1,32 @@
+<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:material="http://materialdesigninxaml.net/winfx/xaml/themes"
+ xmlns:local="clr-namespace:Tango.FSE.Common.Controls">
+
+ <Style TargetType="{x:Type local:TextIconButton}" BasedOn="{StaticResource {x:Type Button}}">
+ <Setter Property="ContentTemplate">
+ <Setter.Value>
+ <DataTemplate>
+ <DockPanel>
+ <material:PackIcon Margin="-5 0 0 0" Width="Auto" Height="Auto" Kind="{Binding RelativeSource={RelativeSource AncestorType=local:TextIconButton},Path=Icon}" VerticalAlignment="Center" />
+ <Label VerticalAlignment="Center" Content="{Binding}"></Label>
+ </DockPanel>
+ </DataTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+
+ <Style x:Key="FSE_TextIconButton_Dark" TargetType="{x:Type local:TextIconButton}" BasedOn="{StaticResource FSE_RaisedButton_Dark_Hover}">
+ <Setter Property="ContentTemplate">
+ <Setter.Value>
+ <DataTemplate>
+ <DockPanel>
+ <material:PackIcon Margin="-5 0 0 0" Width="Auto" Height="Auto" Kind="{Binding RelativeSource={RelativeSource AncestorType=local:TextIconButton},Path=Icon}" VerticalAlignment="Center" />
+ <Label VerticalAlignment="Center" Content="{Binding}"></Label>
+ </DockPanel>
+ </DataTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+
+</ResourceDictionary> \ No newline at end of file
diff --git a/Software/Visual_Studio/FSE/Tango.FSE.Common/Controls/ToggleIconButton.cs b/Software/Visual_Studio/FSE/Tango.FSE.Common/Controls/ToggleIconButton.cs
new file mode 100644
index 000000000..a8d3f7f1a
--- /dev/null
+++ b/Software/Visual_Studio/FSE/Tango.FSE.Common/Controls/ToggleIconButton.cs
@@ -0,0 +1,84 @@
+using MaterialDesignThemes.Wpf;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Controls.Primitives;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Navigation;
+using System.Windows.Shapes;
+
+namespace Tango.FSE.Common.Controls
+{
+ public class ToggleIconButton : ToggleButton
+ {
+ public PackIconKind CheckedIcon
+ {
+ get { return (PackIconKind)GetValue(CheckedIconProperty); }
+ set { SetValue(CheckedIconProperty, value); }
+ }
+ public static readonly DependencyProperty CheckedIconProperty =
+ DependencyProperty.Register("CheckedIcon", typeof(PackIconKind), typeof(ToggleIconButton), new PropertyMetadata(PackIconKind.Circle));
+
+ public PackIconKind UncheckedIcon
+ {
+ get { return (PackIconKind)GetValue(UncheckedIconProperty); }
+ set { SetValue(UncheckedIconProperty, value); }
+ }
+ public static readonly DependencyProperty UncheckedIconProperty =
+ DependencyProperty.Register("UncheckedIcon", typeof(PackIconKind), typeof(ToggleIconButton), new PropertyMetadata(PackIconKind.CircleOutline));
+
+ public Brush CheckedForeground
+ {
+ get { return (Brush)GetValue(CheckedForegroundProperty); }
+ set { SetValue(CheckedForegroundProperty, value); }
+ }
+ public static readonly DependencyProperty CheckedForegroundProperty =
+ DependencyProperty.Register("CheckedForeground", typeof(Brush), typeof(ToggleIconButton), new PropertyMetadata(null));
+
+ public Brush UncheckedForeground
+ {
+ get { return (Brush)GetValue(UncheckedForegroundProperty); }
+ set { SetValue(UncheckedForegroundProperty, value); }
+ }
+ public static readonly DependencyProperty UncheckedForegroundProperty =
+ DependencyProperty.Register("UncheckedForeground", typeof(Brush), typeof(ToggleIconButton), new PropertyMetadata(null));
+
+ public String CheckedText
+ {
+ get { return (String)GetValue(CheckedTextProperty); }
+ set { SetValue(CheckedTextProperty, value); }
+ }
+ public static readonly DependencyProperty CheckedTextProperty =
+ DependencyProperty.Register("CheckedText", typeof(String), typeof(ToggleIconButton), new PropertyMetadata(null));
+
+ public String UncheckedText
+ {
+ get { return (String)GetValue(UncheckedTextProperty); }
+ set { SetValue(UncheckedTextProperty, value); }
+ }
+ public static readonly DependencyProperty UncheckedTextProperty =
+ DependencyProperty.Register("UncheckedText", typeof(String), typeof(ToggleIconButton), new PropertyMetadata(null));
+
+
+ public TextAlignment TextAlignment
+ {
+ get { return (TextAlignment)GetValue(TextAlignmentProperty); }
+ set { SetValue(TextAlignmentProperty, value); }
+ }
+ public static readonly DependencyProperty TextAlignmentProperty =
+ DependencyProperty.Register("TextAlignment", typeof(TextAlignment), typeof(ToggleIconButton), new PropertyMetadata(TextAlignment.Center));
+
+ static ToggleIconButton()
+ {
+ DefaultStyleKeyProperty.OverrideMetadata(typeof(ToggleIconButton), new FrameworkPropertyMetadata(typeof(ToggleIconButton)));
+ }
+ }
+}
diff --git a/Software/Visual_Studio/FSE/Tango.FSE.Common/Controls/ToggleIconButton.xaml b/Software/Visual_Studio/FSE/Tango.FSE.Common/Controls/ToggleIconButton.xaml
new file mode 100644
index 000000000..5eb3ef368
--- /dev/null
+++ b/Software/Visual_Studio/FSE/Tango.FSE.Common/Controls/ToggleIconButton.xaml
@@ -0,0 +1,113 @@
+<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:material="http://materialdesigninxaml.net/winfx/xaml/themes"
+ xmlns:local="clr-namespace:Tango.FSE.Common.Controls">
+
+
+ <Style TargetType="{x:Type local:ToggleIconButton}">
+ <Setter Property="CheckedForeground" Value="{StaticResource FSE_PrimaryForegroundBrush}"></Setter>
+ <Setter Property="UncheckedForeground" Value="{StaticResource FSE_PrimaryForegroundBrush}"></Setter>
+ <Setter Property="FocusVisualStyle" Value="{x:Null}"></Setter>
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate TargetType="{x:Type local:ToggleIconButton}">
+ <Grid Background="Transparent">
+ <StackPanel Orientation="Horizontal">
+ <TextBlock Margin="0 0 5 0" VerticalAlignment="Center">
+ <TextBlock.Style>
+ <Style TargetType="TextBlock">
+ <Setter Property="Text" Value="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},Path=UncheckedText}"></Setter>
+ <Setter Property="Visibility" Value="Collapsed"></Setter>
+ <Style.Triggers>
+ <DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},Path=TextAlignment}" Value="Left">
+ <Setter Property="Visibility" Value="Visible"></Setter>
+ </DataTrigger>
+ <DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},Path=IsChecked}" Value="True">
+ <Setter Property="Text" Value="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},Path=CheckedText}"></Setter>
+ </DataTrigger>
+ </Style.Triggers>
+ </Style>
+ </TextBlock.Style>
+ </TextBlock>
+ <material:PackIcon IsHitTestVisible="False" RenderTransformOrigin="0.5, 0.5" Width="Auto" Height="Auto">
+ <material:PackIcon.Style>
+ <Style TargetType="material:PackIcon">
+ <Setter Property="Foreground" Value="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},Path=UncheckedForeground}"></Setter>
+ <Setter Property="Kind" Value="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},Path=UncheckedIcon}"></Setter>
+ <Setter Property="RenderTransform">
+ <Setter.Value>
+ <ScaleTransform ScaleX="1" ScaleY="1" />
+ </Setter.Value>
+ </Setter>
+ <Style.Triggers>
+ <DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},Path=IsChecked}" Value="True">
+ <Setter Property="Foreground" Value="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},Path=CheckedForeground}"></Setter>
+ <Setter Property="Kind" Value="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},Path=CheckedIcon}"></Setter>
+ </DataTrigger>
+ <DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=local:ToggleIconButton},Path=IsMouseOver}" Value="True">
+ <Setter Property="RenderTransform">
+ <Setter.Value>
+ <ScaleTransform ScaleX="1.2" ScaleY="1.2" />
+ </Setter.Value>
+ </Setter>
+ </DataTrigger>
+ </Style.Triggers>
+ </Style>
+ </material:PackIcon.Style>
+ </material:PackIcon>
+
+ <TextBlock Margin="5 0 0 0" VerticalAlignment="Center">
+ <TextBlock.Style>
+ <Style TargetType="TextBlock">
+ <Setter Property="Text" Value="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},Path=UncheckedText}"></Setter>
+ <Setter Property="Visibility" Value="Collapsed"></Setter>
+ <Style.Triggers>
+ <DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},Path=TextAlignment}" Value="Right">
+ <Setter Property="Visibility" Value="Visible"></Setter>
+ </DataTrigger>
+ <DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},Path=IsChecked}" Value="True">
+ <Setter Property="Text" Value="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},Path=CheckedText}"></Setter>
+ </DataTrigger>
+ </Style.Triggers>
+ </Style>
+ </TextBlock.Style>
+ </TextBlock>
+ </StackPanel>
+ <Canvas IsHitTestVisible="False">
+ <Grid Width="{Binding RelativeSource={RelativeSource AncestorType=Canvas},Path=ActualWidth}" Height="{Binding RelativeSource={RelativeSource AncestorType=Canvas},Path=ActualWidth}">
+ <Ellipse x:Name="ellipse" Fill="White" RenderTransformOrigin="0.5,0.5">
+ <Ellipse.Style>
+ <Style TargetType="Ellipse">
+ <Setter Property="Opacity" Value="0.2"></Setter>
+ <Setter Property="RenderTransform">
+ <Setter.Value>
+ <ScaleTransform ScaleX="0" ScaleY="0" />
+ </Setter.Value>
+ </Setter>
+ <Setter Property="Visibility" Value="Hidden"></Setter>
+ <Style.Triggers>
+ <DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=local:ToggleIconButton},Path=IsMouseOver}" Value="True">
+ <Setter Property="Visibility" Value="Visible"></Setter>
+ <DataTrigger.EnterActions>
+ <BeginStoryboard>
+ <Storyboard>
+ <DoubleAnimation Storyboard.TargetProperty="RenderTransform.ScaleX" From="0" To="4" Duration="00:00:0.5" DecelerationRatio="0.8" />
+ <DoubleAnimation Storyboard.TargetProperty="RenderTransform.ScaleY" From="0" To="4" Duration="00:00:0.5" DecelerationRatio="0.8" />
+ <DoubleAnimation Storyboard.TargetProperty="Opacity" From="0.2" To="0.001" Duration="00:00:0.5" DecelerationRatio="0.8" />
+ </Storyboard>
+ </BeginStoryboard>
+ </DataTrigger.EnterActions>
+ </DataTrigger>
+ </Style.Triggers>
+ </Style>
+ </Ellipse.Style>
+ </Ellipse>
+ </Grid>
+ </Canvas>
+ </Grid>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+
+</ResourceDictionary> \ No newline at end of file
diff --git a/Software/Visual_Studio/FSE/Tango.FSE.Common/FileSystem/FileSystemHandler.cs b/Software/Visual_Studio/FSE/Tango.FSE.Common/FileSystem/FileSystemHandler.cs
new file mode 100644
index 000000000..48fb35c0f
--- /dev/null
+++ b/Software/Visual_Studio/FSE/Tango.FSE.Common/FileSystem/FileSystemHandler.cs
@@ -0,0 +1,119 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Tango.Core;
+using Tango.FileSystem;
+
+namespace Tango.FSE.Common.FileSystem
+{
+ public class FileSystemHandler : ExtendedObject
+ {
+ private Action _abortAction;
+ private FileSystemHandlerStatus _statusBeforePause;
+
+ public FileSystemHandlerType Type { get; set; }
+
+ private FileSystemHandlerStatus _status;
+ public FileSystemHandlerStatus Status
+ {
+ get { return _status; }
+ set
+ {
+ _status = value;
+ RaisePropertyChangedAuto();
+ }
+ }
+
+ private bool _isPaused;
+ public bool IsPaused
+ {
+ get { return _isPaused; }
+ set
+ {
+ if (_isPaused != value)
+ {
+ _isPaused = value;
+
+ if (_isPaused)
+ {
+ _statusBeforePause = Status;
+ Status = FileSystemHandlerStatus.Paused;
+ }
+ else
+ {
+ Status = _statusBeforePause;
+ }
+
+ RaisePropertyChangedAuto();
+ }
+ }
+ }
+
+ private double _position;
+ public double Position
+ {
+ get { return _position; }
+ set { _position = value; RaisePropertyChangedAuto(); }
+ }
+
+ private double _length;
+ public double Length
+ {
+ get { return _length; }
+ set { _length = value; RaisePropertyChangedAuto(); }
+ }
+
+ private Exception _failedException;
+ public Exception FailedException
+ {
+ get { return _failedException; }
+ set { _failedException = value; RaisePropertyChangedAuto(); }
+ }
+
+ public FileSystemItem FileSystemItem { get; set; }
+ public String Destination { get; set; }
+ public String OperationId { get; set; }
+
+ public FileSystemHandler(FileSystemItem fileSystemItem, String destination, Action abortAction)
+ {
+ FileSystemItem = fileSystemItem;
+ Destination = destination;
+ _abortAction = abortAction;
+ }
+
+ internal void InvalidateProgress(double position, double length)
+ {
+ Position = position;
+ Length = length;
+
+ if (!IsPaused)
+ {
+ Status = (Type == FileSystemHandlerType.FileDownload || Type == FileSystemHandlerType.FolderDownload) ? FileSystemHandlerStatus.Downloading : FileSystemHandlerStatus.Uploading;
+ }
+ }
+
+ internal void RaiseFailed(Exception exception)
+ {
+ Status = FileSystemHandlerStatus.Failed;
+ FailedException = exception;
+ }
+
+ internal void RaiseAborted()
+ {
+ Status = FileSystemHandlerStatus.Aborted;
+ }
+
+ internal void RaiseCompleted()
+ {
+ Status = FileSystemHandlerStatus.Completed;
+ }
+
+ public void Abort()
+ {
+ Status = FileSystemHandlerStatus.Aborted;
+ _abortAction?.Invoke();
+ }
+ }
+}
diff --git a/Software/Visual_Studio/FSE/Tango.FSE.Common/FileSystem/FileSystemHandlerStatus.cs b/Software/Visual_Studio/FSE/Tango.FSE.Common/FileSystem/FileSystemHandlerStatus.cs
new file mode 100644
index 000000000..c495b5dc0
--- /dev/null
+++ b/Software/Visual_Studio/FSE/Tango.FSE.Common/FileSystem/FileSystemHandlerStatus.cs
@@ -0,0 +1,19 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tango.FSE.Common.FileSystem
+{
+ public enum FileSystemHandlerStatus
+ {
+ Pending,
+ Downloading,
+ Uploading,
+ Paused,
+ Failed,
+ Aborted,
+ Completed
+ }
+}
diff --git a/Software/Visual_Studio/FSE/Tango.FSE.Common/FileSystem/FileSystemHandlerType.cs b/Software/Visual_Studio/FSE/Tango.FSE.Common/FileSystem/FileSystemHandlerType.cs
new file mode 100644
index 000000000..4c60cb828
--- /dev/null
+++ b/Software/Visual_Studio/FSE/Tango.FSE.Common/FileSystem/FileSystemHandlerType.cs
@@ -0,0 +1,17 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tango.FSE.Common.FileSystem
+{
+ public enum FileSystemHandlerType
+ {
+ FileDownload,
+ FileUpload,
+ FolderDownload,
+ FolderUpload
+ }
+}
diff --git a/Software/Visual_Studio/FSE/Tango.FSE.Common/FileSystem/IFileSystemProvider.cs b/Software/Visual_Studio/FSE/Tango.FSE.Common/FileSystem/IFileSystemProvider.cs
new file mode 100644
index 000000000..c7e00610a
--- /dev/null
+++ b/Software/Visual_Studio/FSE/Tango.FSE.Common/FileSystem/IFileSystemProvider.cs
@@ -0,0 +1,19 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Tango.FileSystem;
+using static System.Environment;
+
+namespace Tango.FSE.Common.FileSystem
+{
+ public interface IFileSystemProvider
+ {
+ Task<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);
+ }
+}
diff --git a/Software/Visual_Studio/FSE/Tango.FSE.Common/Fonts/Caveat-Bold.ttf b/Software/Visual_Studio/FSE/Tango.FSE.Common/Fonts/Caveat-Bold.ttf
new file mode 100644
index 000000000..e7e6a8377
--- /dev/null
+++ b/Software/Visual_Studio/FSE/Tango.FSE.Common/Fonts/Caveat-Bold.ttf
Binary files differ
diff --git a/Software/Visual_Studio/FSE/Tango.FSE.Common/Fonts/Caveat-Regular.ttf b/Software/Visual_Studio/FSE/Tango.FSE.Common/Fonts/Caveat-Regular.ttf
new file mode 100644
index 000000000..278a61c8f
--- /dev/null
+++ b/Software/Visual_Studio/FSE/Tango.FSE.Common/Fonts/Caveat-Regular.ttf
Binary files differ
diff --git a/Software/Visual_Studio/FSE/Tango.FSE.Common/Graphs/GraphHelper.cs b/Software/Visual_Studio/FSE/Tango.FSE.Common/Graphs/GraphHelper.cs
new file mode 100644
index 000000000..7227f5f0d
--- /dev/null
+++ b/Software/Visual_Studio/FSE/Tango.FSE.Common/Graphs/GraphHelper.cs
@@ -0,0 +1,37 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Media;
+
+namespace Tango.FSE.Common.Graphs
+{
+ public static class GraphHelper
+ {
+ public enum GraphColor
+ {
+ White,
+ Red,
+ Yellow,
+ Green,
+ Orange
+ }
+
+ public static Color GetGraphColor(GraphColor graphColor)
+ {
+ return (Color)Application.Current.Resources[$"FSE_RealTimeGraph_{graphColor.ToString()}"];
+ }
+
+ public static Brush GetGraphBrush(GraphColor graphColor)
+ {
+ return new SolidColorBrush(GetGraphColor(graphColor));
+ }
+
+ public static Color GetGraphStrokeColor()
+ {
+ return (Color)Application.Current.Resources["FSE_RealTimeGraph_ForegroundColor"];
+ }
+ }
+}
diff --git a/Software/Visual_Studio/FSE/Tango.FSE.Common/Graphs/RealTimeGraph.cs b/Software/Visual_Studio/FSE/Tango.FSE.Common/Graphs/RealTimeGraph.cs
new file mode 100644
index 000000000..5a2dd3eed
--- /dev/null
+++ b/Software/Visual_Studio/FSE/Tango.FSE.Common/Graphs/RealTimeGraph.cs
@@ -0,0 +1,101 @@
+using RealTimeGraphX;
+using System;
+using System.Windows;
+using System.Windows.Controls;
+
+namespace Tango.FSE.Common.Graphs
+{
+ public class RealTimeGraph : Control
+ {
+ /// <summary>
+ /// Gets or sets the graph controller.
+ /// </summary>
+ public IGraphController Controller
+ {
+ get { return (IGraphController)GetValue(ControllerProperty); }
+ set { SetValue(ControllerProperty, value); }
+ }
+ public static readonly DependencyProperty ControllerProperty =
+ DependencyProperty.Register("Controller", typeof(IGraphController), typeof(RealTimeGraph), new PropertyMetadata(null));
+
+
+ /// <summary>
+ /// Gets or sets the string format of the y-axis.
+ /// </summary>
+ public String StringFormat
+ {
+ get { return (String)GetValue(StringFormatProperty); }
+ set { SetValue(StringFormatProperty, value); }
+ }
+ public static readonly DependencyProperty StringFormatProperty =
+ DependencyProperty.Register("StringFormat", typeof(String), typeof(RealTimeGraph), new PropertyMetadata("0.0"));
+
+
+ /// <summary>
+ /// Gets or sets the display name.
+ /// </summary>
+ public String DisplayName
+ {
+ get { return (String)GetValue(DisplayNameProperty); }
+ set { SetValue(DisplayNameProperty, value); }
+ }
+ public static readonly DependencyProperty DisplayNameProperty =
+ DependencyProperty.Register("DisplayName", typeof(String), typeof(RealTimeGraph), new PropertyMetadata(null));
+
+
+ /// <summary>
+ /// Gets or sets the display units.
+ /// </summary>
+ public String DisplayUnits
+ {
+ get { return (String)GetValue(DisplayUnitsProperty); }
+ set { SetValue(DisplayUnitsProperty, value); }
+ }
+ public static readonly DependencyProperty DisplayUnitsProperty =
+ DependencyProperty.Register("DisplayUnits", typeof(String), typeof(RealTimeGraph), new PropertyMetadata(null));
+
+ /// <summary>
+ /// Gets or sets the graph label visibility.
+ /// </summary>
+ public Visibility GraphLabelVisibility
+ {
+ get { return (Visibility)GetValue(GraphLabelVisibilityProperty); }
+ set { SetValue(GraphLabelVisibilityProperty, value); }
+ }
+ public static readonly DependencyProperty GraphLabelVisibilityProperty =
+ DependencyProperty.Register("GraphLabelVisibility", typeof(Visibility), typeof(RealTimeGraph), new PropertyMetadata(Visibility.Visible));
+
+
+ /// <summary>
+ /// Gets or sets the vertical ticks.
+ /// </summary>
+ public int VerticalTicks
+ {
+ get { return (int)GetValue(VerticalTicksProperty); }
+ set { SetValue(VerticalTicksProperty, value); }
+ }
+ public static readonly DependencyProperty VerticalTicksProperty =
+ DependencyProperty.Register("VerticalTicks", typeof(int), typeof(RealTimeGraph), new PropertyMetadata(10));
+
+
+ /// <summary>
+ /// Gets or sets the horizontal ticks.
+ /// </summary>
+ public int HorizontalTicks
+ {
+ get { return (int)GetValue(HorizontalTicksProperty); }
+ set { SetValue(HorizontalTicksProperty, value); }
+ }
+ public static readonly DependencyProperty HorizontalTicksProperty =
+ DependencyProperty.Register("HorizontalTicks", typeof(int), typeof(RealTimeGraph), new PropertyMetadata(10));
+
+
+ /// <summary>
+ /// Initializes the <see cref="RealTimeGraph"/> class.
+ /// </summary>
+ static RealTimeGraph()
+ {
+ DefaultStyleKeyProperty.OverrideMetadata(typeof(RealTimeGraph), new FrameworkPropertyMetadata(typeof(RealTimeGraph)));
+ }
+ }
+}
diff --git a/Software/Visual_Studio/FSE/Tango.FSE.Common/Graphs/RealTimeGraph.xaml b/Software/Visual_Studio/FSE/Tango.FSE.Common/Graphs/RealTimeGraph.xaml
new file mode 100644
index 000000000..64f7e300e
--- /dev/null
+++ b/Software/Visual_Studio/FSE/Tango.FSE.Common/Graphs/RealTimeGraph.xaml
@@ -0,0 +1,62 @@
+<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:realTimeGraphX="clr-namespace:RealTimeGraphX.WPF;assembly=RealTimeGraphX.WPF"
+ xmlns:local="clr-namespace:Tango.FSE.Common.Graphs">
+
+
+ <Style TargetType="{x:Type local:RealTimeGraph}">
+ <Setter Property="BorderThickness" Value="1"></Setter>
+ <Setter Property="BorderBrush" Value="{StaticResource FSE_RealTimeGraph_OuterBorderBrush}"></Setter>
+ <Setter Property="Padding" Value="20 20 30 20"></Setter>
+ <Setter Property="FontSize" Value="11"></Setter>
+ <Setter Property="Foreground" Value="{StaticResource FSE_RealTimeGraph_ForegroundBrush}"></Setter>
+ <Setter Property="Background" Value="{StaticResource FSE_RealTimeGraph_BackgroundBrush}"></Setter>
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate TargetType="{x:Type local:RealTimeGraph}">
+ <Grid>
+ <Border Background="{TemplateBinding Background}"
+ BorderBrush="{TemplateBinding BorderBrush}"
+ BorderThickness="{TemplateBinding BorderThickness}"
+ CornerRadius="5"
+ Padding="{TemplateBinding Padding}">
+ <Grid>
+ <Grid.RowDefinitions>
+ <RowDefinition Height="1*"/>
+ <RowDefinition Height="35"/>
+ </Grid.RowDefinitions>
+ <Grid.ColumnDefinitions>
+ <ColumnDefinition Width="70"/>
+ <ColumnDefinition Width="1*"/>
+ </Grid.ColumnDefinitions>
+
+ <Grid Grid.Column="1">
+ <realTimeGraphX:WpfGraphGridLines Controller="{TemplateBinding Controller}" Foreground="{StaticResource FSE_RealTimeGraph_GridLinesBrush}" />
+ <realTimeGraphX:WpfGraphSurface x:Name="surface" Controller="{TemplateBinding Controller}" BorderThickness="1 0 0 1" BorderBrush="{StaticResource FSE_RealTimeGraph_InnerBorderBrush}" />
+
+ <!--<Grid IsHitTestVisible="False" Visibility="{TemplateBinding GraphLabelVisibility}">
+ <Label Style="{StaticResource graphLabel}">
+ <StackPanel Orientation="Horizontal">
+ <TextBlock Foreground="Gainsboro" FontSize="11" VerticalAlignment="Center" Text="{TemplateBinding DisplayName}"></TextBlock>
+ <TextBlock Foreground="Gray" Margin="10 0 0 0" FontSize="11" FontFamily="Sylfaen Regular" VerticalAlignment="Center" Text="{TemplateBinding DisplayUnits}"></TextBlock>
+ </StackPanel>
+ </Label>
+ </Grid>-->
+ </Grid>
+
+ <realTimeGraphX:WpfGraphAxisControl Orientation="Vertical" Controller="{TemplateBinding Controller}" StringFormat="{TemplateBinding StringFormat}" Ticks="{TemplateBinding VerticalTicks}" />
+ <realTimeGraphX:WpfGraphAxisControl Orientation="Horizontal" Controller="{TemplateBinding Controller}" Grid.Column="1" Grid.Row="1" Ticks="{TemplateBinding HorizontalTicks}" StringFormat="hh\:mm\:ss"/>
+ </Grid>
+ </Border>
+ <Image HorizontalAlignment="Left" VerticalAlignment="Top" Margin="8" Source="{StaticResource FSE_Screw}" RenderOptions.BitmapScalingMode="Fant" Width="10" Height="10" />
+ <Image HorizontalAlignment="Right" VerticalAlignment="Top" Margin="8" Source="{StaticResource FSE_Screw}" RenderOptions.BitmapScalingMode="Fant" Width="10" Height="10" />
+
+ <Image HorizontalAlignment="Right" VerticalAlignment="Bottom" Margin="8" Source="{StaticResource FSE_Screw}" RenderOptions.BitmapScalingMode="Fant" Width="10" Height="10" />
+ <Image HorizontalAlignment="Left" VerticalAlignment="Bottom" Margin="8" Source="{StaticResource FSE_Screw}" RenderOptions.BitmapScalingMode="Fant" Width="10" Height="10" />
+ </Grid>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+
+</ResourceDictionary> \ No newline at end of file
diff --git a/Software/Visual_Studio/FSE/Tango.FSE.Common/Images/screw.png b/Software/Visual_Studio/FSE/Tango.FSE.Common/Images/screw.png
new file mode 100644
index 000000000..46a8134fe
--- /dev/null
+++ b/Software/Visual_Studio/FSE/Tango.FSE.Common/Images/screw.png
Binary files differ
diff --git a/Software/Visual_Studio/FSE/Tango.FSE.Common/Logging/ILoggingProvider.cs b/Software/Visual_Studio/FSE/Tango.FSE.Common/Logging/ILoggingProvider.cs
new file mode 100644
index 000000000..917720233
--- /dev/null
+++ b/Software/Visual_Studio/FSE/Tango.FSE.Common/Logging/ILoggingProvider.cs
@@ -0,0 +1,16 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Tango.Integration.Logging;
+using Tango.Logging;
+
+namespace Tango.FSE.Common.Logging
+{
+ public interface ILoggingProvider
+ {
+ event EventHandler<LogItemBase> ApplicationLogAvailable;
+ event EventHandler<EmbeddedLogItem> EmbeddedLogAvailable;
+ }
+}
diff --git a/Software/Visual_Studio/FSE/Tango.FSE.Common/Performance/IPerformanceProvider.cs b/Software/Visual_Studio/FSE/Tango.FSE.Common/Performance/IPerformanceProvider.cs
new file mode 100644
index 000000000..c9c4f3a9d
--- /dev/null
+++ b/Software/Visual_Studio/FSE/Tango.FSE.Common/Performance/IPerformanceProvider.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tango.FSE.Common.Performance
+{
+ public interface IPerformanceProvider
+ {
+ event EventHandler<PerformancePackageEventArgs> PerformancePackageAvailable;
+ }
+}
diff --git a/Software/Visual_Studio/FSE/Tango.FSE.Common/Performance/PerformancePackageEventArgs.cs b/Software/Visual_Studio/FSE/Tango.FSE.Common/Performance/PerformancePackageEventArgs.cs
new file mode 100644
index 000000000..669890a29
--- /dev/null
+++ b/Software/Visual_Studio/FSE/Tango.FSE.Common/Performance/PerformancePackageEventArgs.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Tango.Integration.ExternalBridge.Network.Performance;
+
+namespace Tango.FSE.Common.Performance
+{
+ public class PerformancePackageEventArgs : EventArgs
+ {
+ public PerformancePackage Package { get; set; }
+ }
+}
diff --git a/Software/Visual_Studio/FSE/Tango.FSE.Common/Resolution/IResolutionService.cs b/Software/Visual_Studio/FSE/Tango.FSE.Common/Resolution/IResolutionService.cs
new file mode 100644
index 000000000..9b9f04bbc
--- /dev/null
+++ b/Software/Visual_Studio/FSE/Tango.FSE.Common/Resolution/IResolutionService.cs
@@ -0,0 +1,20 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+
+namespace Tango.FSE.Common.Resolution
+{
+ public interface IResolutionService
+ {
+ event EventHandler<ResolutionMode> ResolutionModeChanged;
+ event EventHandler<SizeChangedEventArgs> ResolutionChanged;
+ ResolutionMode Resolution { get; }
+ bool IsLowResolution { get; }
+ bool IsHighResolution { get; }
+ double ResolutionWidth { get; }
+ double ResolutionHeight { get; }
+ }
+}
diff --git a/Software/Visual_Studio/FSE/Tango.FSE.Common/Resolution/ResolutionHelper.cs b/Software/Visual_Studio/FSE/Tango.FSE.Common/Resolution/ResolutionHelper.cs
new file mode 100644
index 000000000..6caee944a
--- /dev/null
+++ b/Software/Visual_Studio/FSE/Tango.FSE.Common/Resolution/ResolutionHelper.cs
@@ -0,0 +1,227 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using Tango.Core.DI;
+
+namespace Tango.FSE.Common.Resolution
+{
+ public static class ResolutionHelper
+ {
+ private static List<FrameworkElement> _elements;
+ private static IResolutionService _resolutionService;
+
+ static ResolutionHelper()
+ {
+ _elements = new List<FrameworkElement>();
+ var resolutionService = TangoIOC.Default.GetInstance<IResolutionService>();
+ _resolutionService = resolutionService;
+
+ if (resolutionService != null)
+ {
+ resolutionService.ResolutionModeChanged += ResolutionService_ResolutionModeChanged;
+ resolutionService.ResolutionChanged += ResolutionService_ResolutionChanged;
+ }
+ }
+
+ private static void ResolutionService_ResolutionChanged(object sender, SizeChangedEventArgs e)
+ {
+ foreach (var element in _elements)
+ {
+ ApplyElementSetting(element);
+ }
+ }
+
+ private static void ResolutionService_ResolutionModeChanged(object sender, ResolutionMode mode)
+ {
+ foreach (var element in _elements)
+ {
+ ApplyElementSetting(element);
+ }
+ }
+
+ private static void ApplyElementSetting(FrameworkElement element)
+ {
+ if (_resolutionService != null)
+ {
+ var mode = GetMode(element);
+ if (mode != ResolutionMode.None)
+ {
+ if (mode == _resolutionService.Resolution)
+ {
+ element.Visibility = Visibility.Visible;
+ }
+ else
+ {
+ element.Visibility = Visibility.Collapsed;
+ }
+ }
+
+ var width = GetMinWidth(element);
+
+ if (!Double.IsNaN(width))
+ {
+ if (_resolutionService.ResolutionWidth >= width)
+ {
+ element.Visibility = Visibility.Visible;
+ }
+ else
+ {
+ element.Visibility = Visibility.Collapsed;
+ }
+ }
+
+ var height = GetMinHeight(element);
+
+ if (!Double.IsNaN(height))
+ {
+ if (_resolutionService.ResolutionHeight >= height)
+ {
+ element.Visibility = Visibility.Visible;
+ }
+ else
+ {
+ element.Visibility = Visibility.Collapsed;
+ }
+ }
+
+ if (!Double.IsNaN(width) && !Double.IsNaN(height))
+ {
+ if (_resolutionService.ResolutionWidth >= width && _resolutionService.ResolutionHeight >= height)
+ {
+ element.Visibility = Visibility.Visible;
+ }
+ else
+ {
+ element.Visibility = Visibility.Collapsed;
+ }
+ }
+ }
+ }
+
+ #region Mode
+
+ /// <summary>
+ /// Determined the resolution mode for the trigger.
+ /// </summary>
+ public static readonly DependencyProperty ModeProperty =
+ DependencyProperty.RegisterAttached("Mode",
+ typeof(ResolutionMode), typeof(ResolutionHelper),
+ new FrameworkPropertyMetadata(ResolutionMode.None, OnModeChanged));
+
+ private static void OnModeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ if (!_elements.Contains(d as FrameworkElement))
+ {
+ _elements.Add(d as FrameworkElement);
+ ApplyElementSetting(d as FrameworkElement);
+ }
+ }
+
+ /// <summary>
+ /// Sets the Mode attached property.
+ /// </summary>
+ /// <param name="element">The element.</param>
+ /// <param name="value">if set to <c>true</c> [value].</param>
+ public static void SetMode(FrameworkElement element, ResolutionMode value)
+ {
+ element.SetValue(ModeProperty, value);
+ }
+
+ /// <summary>
+ /// Gets the Mode attached property.
+ /// </summary>
+ /// <param name="element">The element.</param>
+ /// <returns></returns>
+ public static ResolutionMode GetMode(FrameworkElement element)
+ {
+ return (ResolutionMode)element.GetValue(ModeProperty);
+ }
+
+ #endregion
+
+ #region MinWidth
+
+ /// <summary>
+ /// Determined the resolution MinWidth for the trigger.
+ /// </summary>
+ public static readonly DependencyProperty MinWidthProperty =
+ DependencyProperty.RegisterAttached("MinWidth",
+ typeof(double), typeof(ResolutionHelper),
+ new FrameworkPropertyMetadata(double.NaN, OnMinWidthChanged));
+
+ private static void OnMinWidthChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ if (!_elements.Contains(d as FrameworkElement))
+ {
+ _elements.Add(d as FrameworkElement);
+ ApplyElementSetting(d as FrameworkElement);
+ }
+ }
+
+ /// <summary>
+ /// Sets the MinWidth attached property.
+ /// </summary>
+ /// <param name="element">The element.</param>
+ /// <param name="value">if set to <c>true</c> [value].</param>
+ public static void SetMinWidth(FrameworkElement element, double value)
+ {
+ element.SetValue(MinWidthProperty, value);
+ }
+
+ /// <summary>
+ /// Gets the MinWidth attached property.
+ /// </summary>
+ /// <param name="element">The element.</param>
+ /// <returns></returns>
+ public static double GetMinWidth(FrameworkElement element)
+ {
+ return (double)element.GetValue(MinWidthProperty);
+ }
+
+ #endregion
+
+ #region MinHeight
+
+ /// <summary>
+ /// Determined the resolution MinHeight for the trigger.
+ /// </summary>
+ public static readonly DependencyProperty MinHeightProperty =
+ DependencyProperty.RegisterAttached("MinHeight",
+ typeof(double), typeof(ResolutionHelper),
+ new FrameworkPropertyMetadata(double.NaN, OnMinHeightChanged));
+
+ private static void OnMinHeightChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ if (!_elements.Contains(d as FrameworkElement))
+ {
+ _elements.Add(d as FrameworkElement);
+ ApplyElementSetting(d as FrameworkElement);
+ }
+ }
+
+ /// <summary>
+ /// Sets the MinHeight attached property.
+ /// </summary>
+ /// <param name="element">The element.</param>
+ /// <param name="value">if set to <c>true</c> [value].</param>
+ public static void SetMinHeight(FrameworkElement element, double value)
+ {
+ element.SetValue(MinHeightProperty, value);
+ }
+
+ /// <summary>
+ /// Gets the MinHeight attached property.
+ /// </summary>
+ /// <param name="element">The element.</param>
+ /// <returns></returns>
+ public static double GetMinHeight(FrameworkElement element)
+ {
+ return (double)element.GetValue(MinHeightProperty);
+ }
+
+ #endregion
+ }
+}
diff --git a/Software/Visual_Studio/FSE/Tango.FSE.Common/Resolution/ResolutionMode.cs b/Software/Visual_Studio/FSE/Tango.FSE.Common/Resolution/ResolutionMode.cs
new file mode 100644
index 000000000..b770bbc71
--- /dev/null
+++ b/Software/Visual_Studio/FSE/Tango.FSE.Common/Resolution/ResolutionMode.cs
@@ -0,0 +1,15 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tango.FSE.Common.Resolution
+{
+ public enum ResolutionMode
+ {
+ None,
+ High,
+ Low
+ }
+}
diff --git a/Software/Visual_Studio/FSE/Tango.FSE.Common/Resources/Graphs.xaml b/Software/Visual_Studio/FSE/Tango.FSE.Common/Resources/Graphs.xaml
new file mode 100644
index 000000000..813fb9841
--- /dev/null
+++ b/Software/Visual_Studio/FSE/Tango.FSE.Common/Resources/Graphs.xaml
@@ -0,0 +1,9 @@
+<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:local="clr-namespace:Tango.FSE.Common.Resources">
+
+ <ResourceDictionary.MergedDictionaries>
+ <ResourceDictionary Source="pack://application:,,,/Tango.FSE.Common;component/Graphs/RealTimeGraph.xaml" />
+ </ResourceDictionary.MergedDictionaries>
+
+</ResourceDictionary> \ No newline at end of file
diff --git a/Software/Visual_Studio/FSE/Tango.FSE.Common/SystemInfo/ISystemInfoProvider.cs b/Software/Visual_Studio/FSE/Tango.FSE.Common/SystemInfo/ISystemInfoProvider.cs
new file mode 100644
index 000000000..d21224c4d
--- /dev/null
+++ b/Software/Visual_Studio/FSE/Tango.FSE.Common/SystemInfo/ISystemInfoProvider.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Tango.Integration.ExternalBridge.Network.Information;
+
+namespace Tango.FSE.Common.SystemInfo
+{
+ public interface ISystemInfoProvider
+ {
+ Task<InformationPackage> GetSystemInformationPackage();
+ }
+}
diff --git a/Software/Visual_Studio/FSE/Tango.FSE.Common/Themes/Generic.xaml b/Software/Visual_Studio/FSE/Tango.FSE.Common/Themes/Generic.xaml
new file mode 100644
index 000000000..9b1bd93cf
--- /dev/null
+++ b/Software/Visual_Studio/FSE/Tango.FSE.Common/Themes/Generic.xaml
@@ -0,0 +1,7 @@
+<ResourceDictionary
+ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:local="clr-namespace:Tango.FSE.Common">
+
+
+</ResourceDictionary>
diff --git a/Software/Visual_Studio/FSE/Tango.FSE.UI/Console/DefaultConsoleService.cs b/Software/Visual_Studio/FSE/Tango.FSE.UI/Console/DefaultConsoleService.cs
new file mode 100644
index 000000000..640b547a9
--- /dev/null
+++ b/Software/Visual_Studio/FSE/Tango.FSE.UI/Console/DefaultConsoleService.cs
@@ -0,0 +1,53 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Tango.Console.Network;
+using Tango.Core;
+using Tango.Core.DI;
+using Tango.FSE.Common.Connection;
+using Tango.FSE.Common.Console;
+
+namespace Tango.FSE.UI.Console
+{
+ [TangoCreateWhenRegistered]
+ public class DefaultConsoleService : ExtendedObject, IConsoleService
+ {
+ public event EventHandler<GetCurrentDirectoryResponse> Initialized;
+
+ public string CurrentDirectory { get; private set; }
+
+ private IMachineProvider MachineProvider { get; set; }
+
+ public DefaultConsoleService(IMachineProvider machineProvider)
+ {
+ MachineProvider = machineProvider;
+ MachineProvider.MachineConnected += MachineProvider_MachineConnected;
+ }
+
+ private async void MachineProvider_MachineConnected(object sender, MachineConnectedEventArgs e)
+ {
+ try
+ {
+ var response = await MachineProvider.MachineOperator.SendGenericRequest<GetCurrentDirectoryRequest, GetCurrentDirectoryResponse>(new GetCurrentDirectoryRequest(), new Transport.TransportRequestConfig() { Timeout = TimeSpan.FromSeconds(10) });
+ Initialized?.Invoke(this, response);
+ }
+ catch (Exception ex)
+ {
+ LogManager.Log(ex, "Error getting remote machine current console directory.");
+ }
+ }
+
+ public async Task<ConsoleCommandResponse> ExecuteCommand(ConsoleCommandRequest request, TimeSpan? timeout = null)
+ {
+ var response = await MachineProvider.MachineOperator.SendGenericRequest<ConsoleCommandRequest, ConsoleCommandResponse>(request, new Transport.TransportRequestConfig()
+ {
+ ShouldLog = true,
+ Timeout = timeout
+ });
+
+ return response;
+ }
+ }
+}
diff --git a/Software/Visual_Studio/FSE/Tango.FSE.UI/FileSystem/DefaultFileSystemProvider.cs b/Software/Visual_Studio/FSE/Tango.FSE.UI/FileSystem/DefaultFileSystemProvider.cs
new file mode 100644
index 000000000..bcc39d11d
--- /dev/null
+++ b/Software/Visual_Studio/FSE/Tango.FSE.UI/FileSystem/DefaultFileSystemProvider.cs
@@ -0,0 +1,216 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.IO.Compression;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using Tango.Core;
+using Tango.Core.Threading;
+using Tango.FileSystem;
+using Tango.FileSystem.Network;
+using Tango.FSE.Common.Connection;
+using Tango.FSE.Common.FileSystem;
+using Tango.Transport;
+
+namespace Tango.FSE.UI.FileSystem
+{
+ public class DefaultFileSystemProvider : ExtendedObject, IFileSystemProvider
+ {
+ private IMachineProvider _machineProvider;
+
+ public DefaultFileSystemProvider(IMachineProvider machineProvider)
+ {
+ _machineProvider = machineProvider;
+ }
+
+ public async Task<IFileSystemContainer> GetFolder(string path)
+ {
+ var response = await _machineProvider.MachineOperator.SendGenericRequest<GetFileSystemItemRequest, GetFileSystemItemResponse>(new GetFileSystemItemRequest()
+ {
+ Path = path
+ }, new TransportRequestConfig()
+ {
+ Timeout = TimeSpan.FromSeconds(30),
+ });
+
+ return FileSystemItem.FromDTO(response.FileSystemItem) as IFileSystemContainer;
+ }
+
+ public async Task<IFileSystemContainer> GetSpecialFolder(Environment.SpecialFolder specialFolder)
+ {
+ var response = await _machineProvider.MachineOperator.SendGenericRequest<GetFileSystemItemRequest, GetFileSystemItemResponse>(new GetFileSystemItemRequest()
+ {
+ SpecialFolder = specialFolder
+ }, new TransportRequestConfig()
+ {
+ Timeout = TimeSpan.FromSeconds(30),
+ });
+
+ return FileSystemItem.FromDTO(response.FileSystemItem) as IFileSystemContainer;
+ }
+
+ public async Task<IFileSystemContainer> GetThisPC()
+ {
+ var response = await _machineProvider.MachineOperator.SendGenericRequest<GetFileSystemItemRequest, GetFileSystemItemResponse>(new GetFileSystemItemRequest()
+ {
+ //No parameters at all
+ }, new TransportRequestConfig()
+ {
+ Timeout = TimeSpan.FromSeconds(30),
+ });
+
+ return FileSystemItem.FromDTO(response.FileSystemItem) as IFileSystemContainer;
+ }
+
+ public Task<FileSystemHandler> Download(FileSystemItem item, string localTargetFolder)
+ {
+ String operationId = String.Empty;
+ String destination = String.Empty;
+ long downloadLength = 0;
+ bool aborted = false;
+
+ FileSystemHandler handler = null;
+
+ destination = Path.Combine(localTargetFolder, item.Name);
+
+
+ handler = new FileSystemHandler(item, destination, async () =>
+ {
+ if (!aborted)
+ {
+ aborted = true;
+ try
+ {
+ var response = await _machineProvider.MachineOperator.SendGenericRequest<AbortOperationRequest, AbortOperationResponse>(
+ new AbortOperationRequest()
+ {
+ OperationId = operationId
+ }, new TransportRequestConfig() { Timeout = TimeSpan.FromSeconds(30) });
+ }
+ catch (Exception ex)
+ {
+ LogManager.Log(ex, "Error aborting the download operation.");
+ }
+ finally
+ {
+ handler.RaiseAborted();
+ }
+ }
+ });
+
+ ThreadFactory.StartNew(async () =>
+ {
+ try
+ {
+ if (item.Type == FileSystemItemType.File)
+ {
+ var response = await _machineProvider.MachineOperator.SendGenericRequest<FileDownloadRequest, FileDownloadResponse>(
+ new FileDownloadRequest()
+ {
+ Path = item.Path
+ }, new TransportRequestConfig() { Timeout = TimeSpan.FromSeconds(20) });
+
+ operationId = response.OperationId;
+ downloadLength = response.Length;
+ handler.OperationId = operationId;
+ }
+ else if (item.Type == FileSystemItemType.Folder)
+ {
+ var response = await _machineProvider.MachineOperator.SendGenericRequest<FolderDownloadRequest, FolderDownloadResponse>(
+ new FolderDownloadRequest()
+ {
+ Path = item.Path
+ }, new TransportRequestConfig() { Timeout = TimeSpan.FromSeconds(60) });
+
+ operationId = response.OperationId;
+ downloadLength = response.Length;
+ handler.OperationId = operationId;
+ }
+ else
+ {
+ throw new NotSupportedException("The requested file system item is not supported for downloading.");
+ }
+ }
+ catch (Exception ex)
+ {
+ handler.RaiseFailed(ex);
+ return;
+ }
+
+ long position = 0;
+
+ var tempFile = TemporaryManager.CreateFile();
+
+ while (position < downloadLength && !aborted)
+ {
+ if (handler.IsPaused)
+ {
+ Thread.Sleep(1000);
+ continue;
+ }
+
+ try
+ {
+ var response = await _machineProvider.MachineOperator.SendGenericRequest<ChunkDownloadRequest, ChunkDownloadResponse>(
+ new ChunkDownloadRequest()
+ {
+ MaxChunkSize = 1024 * 10,
+ OperationId = operationId,
+ Position = position,
+ }, new TransportRequestConfig() { Timeout = TimeSpan.FromSeconds(30), Priority = QueuePriority.Low });
+
+ using (FileStream fs = new FileStream(tempFile, FileMode.Append))
+ {
+ fs.Write(response.Data, 0, response.Data.Length);
+ }
+
+ position += response.Data.Length;
+ handler.InvalidateProgress(position, downloadLength);
+ }
+ catch (Exception ex)
+ {
+ tempFile.Delete();
+ handler.RaiseFailed(ex);
+ return;
+ }
+ }
+
+ if (!aborted)
+ {
+ try
+ {
+ if (item.Type == FileSystemItemType.File)
+ {
+ File.Copy(tempFile, destination, true);
+ tempFile.Delete();
+ }
+ else if (item.Type == FileSystemItemType.Folder)
+ {
+ ZipFile.ExtractToDirectory(tempFile, destination);
+ tempFile.Delete();
+ }
+
+ handler.RaiseCompleted();
+ }
+ catch (Exception ex)
+ {
+ handler.RaiseFailed(ex);
+ }
+ }
+ else
+ {
+ tempFile.Delete();
+ }
+ });
+
+ return Task.FromResult(handler);
+ }
+
+ public Task<FileSystemHandler> Upload(string sourcePath, string remoteTargetFolder)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
diff --git a/Software/Visual_Studio/FSE/Tango.FSE.UI/Logging/DefaultLoggingProvider.cs b/Software/Visual_Studio/FSE/Tango.FSE.UI/Logging/DefaultLoggingProvider.cs
new file mode 100644
index 000000000..56ec08cbd
--- /dev/null
+++ b/Software/Visual_Studio/FSE/Tango.FSE.UI/Logging/DefaultLoggingProvider.cs
@@ -0,0 +1,39 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Tango.FSE.Common.Connection;
+using Tango.FSE.Common.Logging;
+using Tango.Integration.ExternalBridge;
+using Tango.Integration.Logging;
+using Tango.Logging;
+
+namespace Tango.FSE.UI.Logging
+{
+ public class DefaultLoggingProvider : ILoggingProvider
+ {
+ public event EventHandler<LogItemBase> ApplicationLogAvailable;
+ public event EventHandler<EmbeddedLogItem> EmbeddedLogAvailable;
+
+ public DefaultLoggingProvider(IMachineProvider machineProvider)
+ {
+ if (machineProvider.MachineOperator is ExternalBridgeTcpClient)
+ {
+ (machineProvider.MachineOperator as ExternalBridgeTcpClient).ApplicationLogAvailable += DefaultLoggingProvider_ApplicationLogAvailable;
+ }
+
+ machineProvider.MachineOperator.DebugLogAvailable += MachineOperator_DebugLogAvailable;
+ }
+
+ private void DefaultLoggingProvider_ApplicationLogAvailable(object sender, LogItemBase log)
+ {
+ ApplicationLogAvailable?.Invoke(this, log);
+ }
+
+ private void MachineOperator_DebugLogAvailable(object sender, PMR.Debugging.StartDebugLogResponse log)
+ {
+ EmbeddedLogAvailable?.Invoke(this, new EmbeddedLogItem(log) { TimeStamp = DateTime.Now});
+ }
+ }
+}
diff --git a/Software/Visual_Studio/FSE/Tango.FSE.UI/Panes/LogViewerPane.xaml b/Software/Visual_Studio/FSE/Tango.FSE.UI/Panes/LogViewerPane.xaml
new file mode 100644
index 000000000..babc4f9fa
--- /dev/null
+++ b/Software/Visual_Studio/FSE/Tango.FSE.UI/Panes/LogViewerPane.xaml
@@ -0,0 +1,148 @@
+<UserControl x:Class="Tango.FSE.UI.Panes.LogViewerPane"
+ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ xmlns:controls="clr-namespace:Tango.FSE.Common.Controls;assembly=Tango.FSE.Common"
+ xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+ xmlns:material="http://materialdesigninxaml.net/winfx/xaml/themes"
+ xmlns:local="clr-namespace:Tango.FSE.UI.Panes"
+ mc:Ignorable="d"
+ d:DesignHeight="450" d:DesignWidth="800" d:DataContext="{d:DesignInstance Type=local:LogViewerPaneVM, IsDesignTimeCreatable=False}">
+
+ <UserControl.Resources>
+
+ <Style x:Key="LogsGridCellStyle" TargetType="DataGridCell" BasedOn="{StaticResource {x:Type DataGridCell}}">
+ <Setter Property="BorderThickness" Value="0"/>
+ <Setter Property="FocusVisualStyle" Value="{x:Null}"/>
+ <Setter Property="VerticalContentAlignment" Value="Center"></Setter>
+ </Style>
+
+ <Style x:Key="LogsGridStyle" TargetType="DataGrid" BasedOn="{StaticResource {x:Type DataGrid}}">
+ <Setter Property="Background" Value="Transparent"></Setter>
+ <Setter Property="Foreground" Value="{StaticResource FSE_GrayBrush}"></Setter>
+ <Setter Property="AutoGenerateColumns" Value="False"></Setter>
+ <Setter Property="CanUserAddRows" Value="False"></Setter>
+ <Setter Property="CanUserDeleteRows" Value="False"></Setter>
+ <Setter Property="CanUserReorderColumns" Value="False"></Setter>
+ <Setter Property="CanUserResizeColumns" Value="False"></Setter>
+ <Setter Property="CanUserResizeRows" Value="False"></Setter>
+ <Setter Property="CanUserSortColumns" Value="False"></Setter>
+ <Setter Property="IsReadOnly" Value="True"></Setter>
+ <Setter Property="SelectionMode" Value="Single"></Setter>
+ <Setter Property="SelectionUnit" Value="FullRow"></Setter>
+ <Setter Property="RowHeight" Value="35"></Setter>
+ <Setter Property="HorizontalGridLinesBrush" Value="{StaticResource FSE_PrimaryBackgroundBrush}"></Setter>
+ <Setter Property="HorizontalScrollBarVisibility" Value="Disabled"></Setter>
+ <Setter Property="CellStyle" Value="{StaticResource LogsGridCellStyle}" />
+ </Style>
+ </UserControl.Resources>
+
+ <Grid>
+ <Grid.ColumnDefinitions>
+ <ColumnDefinition Width="1*"/>
+ <ColumnDefinition Width="1*"/>
+ </Grid.ColumnDefinitions>
+
+ <DockPanel Margin="10">
+ <Grid DockPanel.Dock="Top" Margin="55 5 0 0" Panel.ZIndex="100">
+ <TextBlock Foreground="{StaticResource FSE_PrimaryAccentBrush}" FontWeight="SemiBold">APPLICATION LOGS</TextBlock>
+ <StackPanel Margin="0 0 0 -70" HorizontalAlignment="Right" >
+ <StackPanel Margin="8 0 0 0" HorizontalAlignment="Left" Orientation="Horizontal">
+ <controls:ToggleIconButton ToolTip="Pause/Resume application logs" CheckedIcon="Play" UncheckedIcon="Pause" Width="24" Height="24" IsChecked="{Binding ApplicationLogsPaused}" />
+ <controls:IconButton Icon="CircleArrows" ToolTip="Clear application logs" Width="24" Height="24" Command="{Binding ClearApplicationLogsCommand}" />
+ </StackPanel>
+ <StackPanel Margin="0 8 0 0" Orientation="Horizontal">
+ <ItemsControl VerticalAlignment="Center" ItemsSource="{Binding SelectedApplicationLogsCategories}" Visibility="{Binding ResolutionService.IsHighResolution,Converter={StaticResource BooleanToVisibilityConverter}}">
+ <ItemsControl.ItemsPanel>
+ <ItemsPanelTemplate>
+ <StackPanel Orientation="Horizontal" />
+ </ItemsPanelTemplate>
+ </ItemsControl.ItemsPanel>
+ <ItemsControl.ItemTemplate>
+ <DataTemplate>
+ <StackPanel Orientation="Horizontal" Margin="10 0 0 0">
+ <CheckBox FocusVisualStyle="{x:Null}" IsChecked="{Binding IsSelected,Delay=200}" VerticalAlignment="Center">
+ <CheckBox.Content>
+ <TextBlock Text="{Binding Data}" VerticalAlignment="Center"></TextBlock>
+ </CheckBox.Content>
+ </CheckBox>
+ </StackPanel>
+ </DataTemplate>
+ </ItemsControl.ItemTemplate>
+ </ItemsControl>
+
+ <DockPanel Margin="20 0 0 0" VerticalAlignment="Center">
+ <material:PackIcon Kind="Search" VerticalAlignment="Center" Width="18" Height="18" />
+ <TextBox Margin="5 0 0 0" Style="{StaticResource FSE_Rounded_Corners_TextBox}" Background="{StaticResource FSE_PrimaryBackgroundBrush}" FontSize="{StaticResource FSE_SmallFontSize}" material:HintAssist.Hint="filter" material:TextFieldAssist.RippleOnFocusEnabled="True" Text="{Binding ApplicationLogsFilter,UpdateSourceTrigger=PropertyChanged,Delay=1000}" Width="200"/>
+ </DockPanel>
+ </StackPanel>
+ </StackPanel>
+ </Grid>
+
+ <DataGrid Margin="0 5 0 0" Style="{StaticResource LogsGridStyle}" ItemsSource="{Binding ApplicationLogs}" CellStyle="{StaticResource LogsGridCellStyle}">
+ <DataGrid.Columns>
+ <DataGridTemplateColumn Header="#" Width="40" CellTemplate="{StaticResource FSE_LogIcon_Cell}"/>
+ <DataGridTextColumn Header="DATE TIME" Binding="{Binding TimeStamp,StringFormat='HH:mm:ss.ff'}" Width="100" />
+ <DataGridTemplateColumn Header="MESSAGE" Width="1*" >
+ <DataGridTemplateColumn.CellTemplate>
+ <DataTemplate>
+ <TextBlock Text="{Binding Message,Converter={StaticResource StringToOneLineConverter},ConverterParameter='120'}"></TextBlock>
+ </DataTemplate>
+ </DataGridTemplateColumn.CellTemplate>
+ </DataGridTemplateColumn>
+ </DataGrid.Columns>
+ </DataGrid>
+ </DockPanel>
+
+ <DockPanel Grid.Column="1" Margin="10">
+ <Grid DockPanel.Dock="Top" Margin="55 5 0 0" Panel.ZIndex="100">
+ <TextBlock Foreground="{StaticResource FSE_PrimaryAccentBrush}" FontWeight="SemiBold">MACHINE LOGS</TextBlock>
+ <StackPanel Margin="0 0 0 -70" HorizontalAlignment="Right" >
+ <StackPanel Margin="8 0 0 0" HorizontalAlignment="Left" Orientation="Horizontal">
+ <controls:ToggleIconButton ToolTip="Pause/Resume machine logs" CheckedIcon="Play" UncheckedIcon="Pause" Width="24" Height="24" IsChecked="{Binding MachineLogsPaused}" />
+ <controls:IconButton Icon="CircleArrows" ToolTip="Clear machine logs" Width="24" Height="24" Command="{Binding ClearMachineLogsCommand}" />
+ </StackPanel>
+ <StackPanel Margin="0 8 0 0" Orientation="Horizontal">
+ <ItemsControl VerticalAlignment="Center" ItemsSource="{Binding SelectedMachineLogsCategories}" Visibility="{Binding ResolutionService.IsHighResolution,Converter={StaticResource BooleanToVisibilityConverter}}">
+ <ItemsControl.ItemsPanel>
+ <ItemsPanelTemplate>
+ <StackPanel Orientation="Horizontal" />
+ </ItemsPanelTemplate>
+ </ItemsControl.ItemsPanel>
+ <ItemsControl.ItemTemplate>
+ <DataTemplate>
+ <StackPanel Orientation="Horizontal" Margin="10 0 0 0">
+ <CheckBox FocusVisualStyle="{x:Null}" IsChecked="{Binding IsSelected,Delay=200}" VerticalAlignment="Center">
+ <CheckBox.Content>
+ <TextBlock Text="{Binding Data}" VerticalAlignment="Center"/>
+ </CheckBox.Content>
+ </CheckBox>
+ </StackPanel>
+ </DataTemplate>
+ </ItemsControl.ItemTemplate>
+ </ItemsControl>
+
+ <DockPanel Margin="20 0 0 0" VerticalAlignment="Center">
+ <material:PackIcon Kind="Search" VerticalAlignment="Center" Width="18" Height="18" />
+ <TextBox Margin="5 0 0 0" Style="{StaticResource FSE_Rounded_Corners_TextBox}" Background="{StaticResource FSE_PrimaryBackgroundBrush}" FontSize="{StaticResource FSE_SmallFontSize}" material:HintAssist.Hint="filter" material:TextFieldAssist.RippleOnFocusEnabled="True" Text="{Binding MachineLogsFilter,UpdateSourceTrigger=PropertyChanged,Delay=1000}" Width="200"/>
+ </DockPanel>
+ </StackPanel>
+ </StackPanel>
+ </Grid>
+
+ <DataGrid Margin="0 5 0 0" Style="{StaticResource LogsGridStyle}" ItemsSource="{Binding MachineLogs}" CellStyle="{StaticResource LogsGridCellStyle}">
+ <DataGrid.Columns>
+ <DataGridTemplateColumn Header="#" Width="40" CellTemplate="{StaticResource FSE_LogIcon_Cell}"/>
+ <DataGridTextColumn Header="DATE TIME" Binding="{Binding TimeStamp,StringFormat='HH:mm:ss.ff'}" Width="100" />
+ <DataGridTemplateColumn Header="MESSAGE" Width="1*" >
+ <DataGridTemplateColumn.CellTemplate>
+ <DataTemplate>
+ <TextBlock Text="{Binding Message,Converter={StaticResource StringToOneLineConverter},ConverterParameter='120'}"></TextBlock>
+ </DataTemplate>
+ </DataGridTemplateColumn.CellTemplate>
+ </DataGridTemplateColumn>
+ </DataGrid.Columns>
+ </DataGrid>
+ </DockPanel>
+ </Grid>
+</UserControl>
diff --git a/Software/Visual_Studio/FSE/Tango.FSE.UI/Panes/LogViewerPane.xaml.cs b/Software/Visual_Studio/FSE/Tango.FSE.UI/Panes/LogViewerPane.xaml.cs
new file mode 100644
index 000000000..3c09421ca
--- /dev/null
+++ b/Software/Visual_Studio/FSE/Tango.FSE.UI/Panes/LogViewerPane.xaml.cs
@@ -0,0 +1,28 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Navigation;
+using System.Windows.Shapes;
+
+namespace Tango.FSE.UI.Panes
+{
+ /// <summary>
+ /// Interaction logic for LogViewerPane.xaml
+ /// </summary>
+ public partial class LogViewerPane : UserControl
+ {
+ public LogViewerPane()
+ {
+ InitializeComponent();
+ }
+ }
+}
diff --git a/Software/Visual_Studio/FSE/Tango.FSE.UI/Panes/LogViewerPaneVM.cs b/Software/Visual_Studio/FSE/Tango.FSE.UI/Panes/LogViewerPaneVM.cs
new file mode 100644
index 000000000..64e73babf
--- /dev/null
+++ b/Software/Visual_Studio/FSE/Tango.FSE.UI/Panes/LogViewerPaneVM.cs
@@ -0,0 +1,179 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.ComponentModel;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Data;
+using Tango.Core.Commands;
+using Tango.Core.DI;
+using Tango.FSE.Common;
+using Tango.Integration.Logging;
+using Tango.Logging;
+using Tango.SharedUI.Components;
+
+namespace Tango.FSE.UI.Panes
+{
+ public class LogViewerPaneVM : FSEViewModel
+ {
+ private List<LogItemBase> _pausedApplicationLogs;
+ private List<EmbeddedLogItem> _pausedMachineLogs;
+
+ public ObservableCollection<LogItemBase> ApplicationLogs { get; set; }
+ public ObservableCollection<EmbeddedLogItem> MachineLogs { get; set; }
+ public ICollectionView ApplicationLogsView { get; set; }
+ public ICollectionView MachineLogsView { get; set; }
+ public SelectedObjectCollection<LogCategory> SelectedApplicationLogsCategories { get; set; }
+ public SelectedObjectCollection<LogCategory> SelectedMachineLogsCategories { get; set; }
+ public bool ApplicationLogsPaused { get; set; }
+ public bool MachineLogsPaused { get; set; }
+
+ private String _applicationLogsFilter;
+ public String ApplicationLogsFilter
+ {
+ get { return _applicationLogsFilter; }
+ set { _applicationLogsFilter = value; RaisePropertyChangedAuto(); ApplicationLogsView.Refresh(); }
+ }
+
+ private String _machineLogsFilter;
+ public String MachineLogsFilter
+ {
+ get { return _machineLogsFilter; }
+ set { _machineLogsFilter = value; RaisePropertyChangedAuto(); MachineLogsView.Refresh(); }
+ }
+
+
+ public RelayCommand ClearApplicationLogsCommand { get; set; }
+ public RelayCommand ClearMachineLogsCommand { get; set; }
+
+ public LogViewerPaneVM()
+ {
+ _pausedApplicationLogs = new List<LogItemBase>();
+ _pausedMachineLogs = new List<EmbeddedLogItem>();
+
+ ApplicationLogs = new ObservableCollection<LogItemBase>();
+ MachineLogs = new ObservableCollection<EmbeddedLogItem>();
+ ApplicationLogsView = CollectionViewSource.GetDefaultView(ApplicationLogs);
+ MachineLogsView = CollectionViewSource.GetDefaultView(MachineLogs);
+
+ ApplicationLogsView.Filter = FilterApplicationLogs;
+ MachineLogsView.Filter = FilterMachineLogs;
+
+ SelectedApplicationLogsCategories = new SelectedObjectCollection<LogCategory>(new ObservableCollection<LogCategory>()
+ {
+ LogCategory.Info,
+ LogCategory.Warning,
+ LogCategory.Error,
+ LogCategory.Critical,
+ }, new ObservableCollection<LogCategory>()
+ {
+ LogCategory.Info,
+ LogCategory.Warning,
+ LogCategory.Error,
+ LogCategory.Critical,
+ });
+
+ SelectedMachineLogsCategories = new SelectedObjectCollection<LogCategory>(new ObservableCollection<LogCategory>()
+ {
+ LogCategory.Info,
+ LogCategory.Warning,
+ LogCategory.Error,
+ LogCategory.Critical,
+ LogCategory.Debug,
+ }, new ObservableCollection<LogCategory>()
+ {
+ LogCategory.Info,
+ LogCategory.Warning,
+ LogCategory.Error,
+ LogCategory.Critical,
+ LogCategory.Debug,
+ });
+
+ SelectedApplicationLogsCategories.SynchedSource.CollectionChanged += (_, __) => ApplicationLogsView.Refresh();
+ SelectedMachineLogsCategories.SynchedSource.CollectionChanged += (_, __) => MachineLogsView.Refresh();
+
+ ClearApplicationLogsCommand = new RelayCommand(ClearApplicationLogs);
+ ClearMachineLogsCommand = new RelayCommand(ClearMachineLogs);
+
+ TangoIOC.Default.Inject(this);
+
+ LoggingProvider.ApplicationLogAvailable += LoggingProvider_ApplicationLogAvailable;
+ LoggingProvider.EmbeddedLogAvailable += LoggingProvider_EmbeddedLogAvailable;
+ }
+
+ private bool FilterApplicationLogs(object obj)
+ {
+ var log = obj as LogItemBase;
+ return SelectedApplicationLogsCategories.SynchedSource.Contains(log.Category) && (String.IsNullOrWhiteSpace(ApplicationLogsFilter) || log.Message.ToLower().Contains(ApplicationLogsFilter.ToLower()));
+ }
+
+ private bool FilterMachineLogs(object obj)
+ {
+ var log = obj as EmbeddedLogItem;
+ return SelectedMachineLogsCategories.SynchedSource.Contains(log.Category) && (String.IsNullOrWhiteSpace(MachineLogsFilter) || log.Message.ToLower().Contains(MachineLogsFilter.ToLower()));
+ }
+
+ private void ClearApplicationLogs()
+ {
+ _pausedApplicationLogs.Clear();
+ ApplicationLogs.Clear();
+ }
+
+ private void ClearMachineLogs()
+ {
+ _pausedMachineLogs.Clear();
+ MachineLogs.Clear();
+ }
+
+ private void LoggingProvider_ApplicationLogAvailable(object sender, LogItemBase logItem)
+ {
+ if (ApplicationLogsPaused)
+ {
+ _pausedApplicationLogs.Add(logItem);
+ }
+ else
+ {
+ InvokeUI(() =>
+ {
+ if (_pausedApplicationLogs.Count > 0)
+ {
+ foreach (var pausedItem in _pausedApplicationLogs)
+ {
+ ApplicationLogs.Insert(0, pausedItem);
+ }
+
+ _pausedApplicationLogs.Clear();
+ }
+
+ ApplicationLogs.Insert(0, logItem);
+ });
+ }
+ }
+
+ private void LoggingProvider_EmbeddedLogAvailable(object sender, EmbeddedLogItem logItem)
+ {
+ if (MachineLogsPaused)
+ {
+ _pausedMachineLogs.Add(logItem);
+ }
+ else
+ {
+ InvokeUI(() =>
+ {
+ if (_pausedMachineLogs.Count > 0)
+ {
+ foreach (var pausedItem in _pausedMachineLogs)
+ {
+ MachineLogs.Insert(0, pausedItem);
+ }
+
+ _pausedMachineLogs.Clear();
+ }
+
+ MachineLogs.Insert(0, logItem);
+ });
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Software/Visual_Studio/FSE/Tango.FSE.UI/Performance/DefaultPerformanceProvider.cs b/Software/Visual_Studio/FSE/Tango.FSE.UI/Performance/DefaultPerformanceProvider.cs
new file mode 100644
index 000000000..c9b5d8bbb
--- /dev/null
+++ b/Software/Visual_Studio/FSE/Tango.FSE.UI/Performance/DefaultPerformanceProvider.cs
@@ -0,0 +1,46 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Tango.Core;
+using Tango.Core.DI;
+using Tango.FSE.Common.Connection;
+using Tango.FSE.Common.Performance;
+using Tango.Integration.ExternalBridge.Network.Performance;
+
+namespace Tango.FSE.UI.Performance
+{
+ [TangoCreateWhenRegistered]
+ public class DefaultPerformanceProvider : ExtendedObject, IPerformanceProvider
+ {
+ public event EventHandler<PerformancePackageEventArgs> PerformancePackageAvailable;
+
+ private IMachineProvider MachineProvider { get; set; }
+
+ public DefaultPerformanceProvider(IMachineProvider machineProvider)
+ {
+ MachineProvider = machineProvider;
+ MachineProvider.MachineConnected += MachineProvider_MachineConnected;
+ }
+
+ private void MachineProvider_MachineConnected(object sender, MachineConnectedEventArgs e)
+ {
+ MachineProvider.MachineOperator.SendGenericContinuousRequest<StartPerformanceUpdatesRequest, StartPerformanceUpdatesResponse>(new StartPerformanceUpdatesRequest()).Subscribe((response) =>
+ {
+ OnPerformancePackageAvailable(response.Package);
+ }, (ex) =>
+ {
+ LogManager.Log(ex, "Error starting performance updates.");
+ }, () =>
+ {
+ //Do nothing.
+ });
+ }
+
+ protected virtual void OnPerformancePackageAvailable(PerformancePackage package)
+ {
+ PerformancePackageAvailable?.Invoke(this, new PerformancePackageEventArgs() { Package = package });
+ }
+ }
+}
diff --git a/Software/Visual_Studio/FSE/Tango.FSE.UI/Resolution/DefaultResolutionService.cs b/Software/Visual_Studio/FSE/Tango.FSE.UI/Resolution/DefaultResolutionService.cs
new file mode 100644
index 000000000..de3d2c356
--- /dev/null
+++ b/Software/Visual_Studio/FSE/Tango.FSE.UI/Resolution/DefaultResolutionService.cs
@@ -0,0 +1,121 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using Tango.Core;
+using Tango.Core.DI;
+using Tango.FSE.Common.Resolution;
+
+namespace Tango.FSE.UI.Resolution
+{
+ [TangoCreateWhenRegistered]
+ public class DefaultResolutionService : ExtendedObject, IResolutionService
+ {
+ public event EventHandler<ResolutionMode> ResolutionModeChanged;
+ public event EventHandler<SizeChangedEventArgs> ResolutionChanged;
+
+ private ResolutionMode resolution;
+ public ResolutionMode Resolution
+ {
+ get
+ {
+ return resolution;
+ }
+ private set
+ {
+ resolution = value;
+ RaisePropertyChangedAuto();
+ }
+ }
+
+ private bool isLowResolution;
+ public bool IsLowResolution
+ {
+ get
+ {
+ return isLowResolution;
+ }
+ private set
+ {
+ isLowResolution = value;
+ RaisePropertyChangedAuto();
+ }
+ }
+
+
+ private bool isHighResolution;
+ public bool IsHighResolution
+ {
+ get
+ {
+ return isHighResolution;
+ }
+ private set
+ {
+ isHighResolution = value;
+ RaisePropertyChangedAuto();
+ }
+ }
+
+ private double resolutionWidth;
+ public double ResolutionWidth
+ {
+ get
+ {
+ return resolutionWidth;
+ }
+ private set
+ {
+ resolutionWidth = value;
+ RaisePropertyChangedAuto();
+ }
+ }
+
+ private double resolutionHeight;
+ public double ResolutionHeight
+ {
+ get
+ {
+ return resolutionHeight;
+ }
+ private set
+ {
+ resolutionHeight = value;
+ RaisePropertyChangedAuto();
+ }
+ }
+
+ public DefaultResolutionService()
+ {
+ var window = Application.Current.MainWindow;
+ if (window != null)
+ {
+ window.SizeChanged += Window_SizeChanged;
+ }
+ }
+
+ private void Window_SizeChanged(object sender, SizeChangedEventArgs e)
+ {
+ var size = e.NewSize;
+
+ ResolutionWidth = size.Width;
+ ResolutionHeight = size.Height;
+
+ IsHighResolution = ResolutionWidth > 1280 && ResolutionHeight > 800;
+ IsLowResolution = !isHighResolution;
+
+ ResolutionMode previousMode = Resolution;
+
+ Resolution = IsHighResolution ? ResolutionMode.High : ResolutionMode.Low;
+
+ if (Resolution != previousMode)
+ {
+ ResolutionModeChanged?.Invoke(this, Resolution);
+ }
+
+ ResolutionChanged?.Invoke(this, e);
+ }
+ }
+}
diff --git a/Software/Visual_Studio/FSE/Tango.FSE.UI/SystemInfo/DefaultSystemInfoProvider.cs b/Software/Visual_Studio/FSE/Tango.FSE.UI/SystemInfo/DefaultSystemInfoProvider.cs
new file mode 100644
index 000000000..9b72b308a
--- /dev/null
+++ b/Software/Visual_Studio/FSE/Tango.FSE.UI/SystemInfo/DefaultSystemInfoProvider.cs
@@ -0,0 +1,49 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Tango.Core;
+using Tango.Core.DI;
+using Tango.FSE.Common.Connection;
+using Tango.FSE.Common.SystemInfo;
+using Tango.Integration.ExternalBridge.Network.Information;
+
+namespace Tango.FSE.UI.SystemInfo
+{
+ public class DefaultSystemInfoProvider : ExtendedObject, ISystemInfoProvider
+ {
+ private InformationPackage _package;
+
+ private IMachineProvider MachineProvider { get; set; }
+
+ public DefaultSystemInfoProvider(IMachineProvider machineProvider)
+ {
+ MachineProvider = machineProvider;
+ MachineProvider.MachineDisconnected += MachineProvider_MachineDisconnected;
+ }
+
+ private void MachineProvider_MachineDisconnected(object sender, MachineDisconnectedEventArgs e)
+ {
+ _package = null;
+ }
+
+ public async Task<InformationPackage> GetSystemInformationPackage()
+ {
+ if (_package == null)
+ {
+ var response = await MachineProvider.MachineOperator.SendGenericRequest<GetMachineInformationRequest, GetMachineInformationResponse>(new GetMachineInformationRequest(), new Transport.TransportRequestConfig()
+ {
+ Timeout = TimeSpan.FromSeconds(30)
+ });
+
+ _package = response.Package;
+ return _package;
+ }
+ else
+ {
+ return _package;
+ }
+ }
+ }
+}
diff --git a/Software/Visual_Studio/Installers/VS Extensions/PropMan.vsix b/Software/Visual_Studio/Installers/VS Extensions/PropMan.vsix
new file mode 100644
index 000000000..23b380931
--- /dev/null
+++ b/Software/Visual_Studio/Installers/VS Extensions/PropMan.vsix
Binary files differ
diff --git a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Statistics/Converters/JobLengthConverter.cs b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Statistics/Converters/JobLengthConverter.cs
new file mode 100644
index 000000000..cd928d9c9
--- /dev/null
+++ b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Statistics/Converters/JobLengthConverter.cs
@@ -0,0 +1,41 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Data;
+
+namespace Tango.MachineStudio.Statistics.Converters
+{
+ public class JobLengthConverter : IMultiValueConverter
+ {
+ public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
+ {
+ try
+ {
+ if (values != null && values.Count() == 3)
+ {
+ double length = (double)values[0];
+ double endPoint = (double)values[1];
+ double width = (double)values[2];
+ var v = Math.Round((endPoint / length) * width, MidpointRounding.AwayFromZero);
+
+ if (double.IsInfinity(v))
+ {
+ return 0d;
+ }
+ return v;
+ }
+ }
+ catch { }
+
+ return 0d;
+ }
+
+ public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
diff --git a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Statistics/Converters/NanoLiterToLiterFormatConverter.cs b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Statistics/Converters/NanoLiterToLiterFormatConverter.cs
new file mode 100644
index 000000000..32050cdb1
--- /dev/null
+++ b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Statistics/Converters/NanoLiterToLiterFormatConverter.cs
@@ -0,0 +1,31 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Data;
+
+namespace Tango.MachineStudio.Statistics.Converters
+{
+ public class NanoLiterToLiterFormatConverter : IValueConverter
+ {
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ try
+ {
+ var longValue = System.Convert.ToUInt64(value.ToString());
+ double val = (longValue / 1000000000d);
+ return val.ToString("0.0");
+ }
+ catch { }
+
+ return "";
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
diff --git a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Statistics/Models/StatisticsValueCollection.cs b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Statistics/Models/StatisticsValueCollection.cs
new file mode 100644
index 000000000..b5615e4d1
--- /dev/null
+++ b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Statistics/Models/StatisticsValueCollection.cs
@@ -0,0 +1,319 @@
+using LiveCharts;
+using LiveCharts.Wpf;
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Data;
+using System.Windows.Media;
+using Tango.BL.Enumerations;
+using Tango.BL.ValueObjects;
+using Tango.Core;
+using System.Windows.Media;
+using System.Windows;
+
+namespace Tango.MachineStudio.Statistics.Models
+{
+ public class StatisticsValue
+ {
+ public string Name { get; set; }
+
+ public object Value { get; set; }
+
+ public string Unit { get; set; }
+ }
+ public class MoreValue
+ {
+ public string Text { get; set; }
+ }
+
+ public class StatisticsValueCollection : ExtendedObject
+ {
+ private List<Color> _pieColors;
+
+ #region Properties
+
+ private ObservableCollection<StatisticsValue> _statisticsCollection;
+
+ /// <summary>
+ /// Gets or sets the statistics collection of StatisticsValue object.
+ /// </summary>
+ public ObservableCollection<StatisticsValue> StatisticsCollection
+ {
+ get { return _statisticsCollection; }
+ set
+ {
+ _statisticsCollection = value;
+ RaisePropertyChangedAuto();
+ }
+ }
+
+ private List<StatisticsValue> _threadConsumptionPerThread;
+ /// <summary>
+ /// Gets or sets the thread consumption per thread list.
+ /// </summary>
+ public List<StatisticsValue> ThreadConsumptionPerThread
+ {
+ get { return _threadConsumptionPerThread; }
+ set { _threadConsumptionPerThread = value; RaisePropertyChangedAuto(); }
+ }
+
+ /// <summary>
+ /// Gets or sets the thread consumption per thread collection.
+ /// </summary>
+ public CompositeCollection ThreadConsumptionPerThreadCollection { get; set; }
+
+ private LabeledSeriesCollection _pieJobSource;
+
+ /// <summary>
+ /// Gets or sets the pie job source.
+ /// </summary>
+ public LabeledSeriesCollection PieJobSource
+ {
+ get { return _pieJobSource; }
+ set { _pieJobSource = value; RaisePropertyChangedAuto(); }
+ }
+
+ private LabeledSeriesCollection _pieJobRunStatus;
+
+ /// <summary>
+ /// Gets or sets the pie job run status.
+ /// </summary>
+ public LabeledSeriesCollection PieJobRunStatus
+ {
+ get { return _pieJobRunStatus; }
+ set { _pieJobRunStatus = value; RaisePropertyChangedAuto(); }
+ }
+
+ private LabeledSeriesCollection _pieGradientSolid;
+
+ /// <summary>
+ /// Gets or sets the pie gradient solid.
+ /// </summary>
+ public LabeledSeriesCollection PieGradientSolid
+ {
+ get { return _pieGradientSolid; }
+ set { _pieGradientSolid = value; RaisePropertyChangedAuto(); }
+ }
+
+ private List<TotalLiquidQuantityModel> _liquidQuantities;
+
+ /// <summary>
+ /// Gets or sets the liquid quantities.
+ /// </summary>
+ public List<TotalLiquidQuantityModel> LiquidQuantities
+ {
+ get
+ {
+ if (_liquidQuantities == null)
+ {
+ _liquidQuantities = new List<TotalLiquidQuantityModel>();
+ }
+ return _liquidQuantities;
+ }
+ set
+ { _liquidQuantities = value; RaisePropertyChangedAuto(); }
+ }
+
+ private ulong _totalLiquidQuantities;
+
+ /// <summary>
+ /// Gets or sets the total liquid quantities.
+ /// </summary>
+ public ulong TotalLiquidQuantities
+ {
+ get { return _totalLiquidQuantities; }
+ set { _totalLiquidQuantities = value; RaisePropertyChangedAuto(); }
+ }
+
+ #endregion
+
+ public StatisticsValueCollection()
+ {
+ StatisticsCollection = new ObservableCollection<StatisticsValue>();
+ ThreadConsumptionPerThread = new List<StatisticsValue>();
+ ThreadConsumptionPerThreadCollection = new CompositeCollection();
+
+ _pieColors = new List<Color>();
+ _pieColors.Add(((SolidColorBrush)Application.Current.Resources["RedBrush500"]).Color);
+ _pieColors.Add(((SolidColorBrush)Application.Current.Resources["OrangeBrush"]).Color);
+ _pieColors.Add(((SolidColorBrush)Application.Current.Resources["GreenBrush"]).Color);
+ _pieColors.Add(((SolidColorBrush)Application.Current.Resources["BlueBrush100"]).Color);
+ _pieColors.Add(Color.FromRgb(255, 216, 76));
+
+
+ PieJobSource = new LabeledSeriesCollection()
+ {
+ Title = "PPC/MS",
+ SeriesColors = _pieColors,
+ };
+ PieJobRunStatus = new LabeledSeriesCollection()
+ {
+ Title = "Failed/Aborted/Completed",
+ SeriesColors = _pieColors,
+ };
+ PieGradientSolid = new LabeledSeriesCollection()
+ {
+ Title = "Gradient/Solid",
+ SeriesColors = _pieColors,
+ };
+ }
+
+ /// <summary>
+ /// Cleans all values.
+ /// </summary>
+ public void Clean()
+ {
+ StatisticsCollection.Clear();
+ ThreadConsumptionPerThreadCollection.Clear();
+ ThreadConsumptionPerThread.Clear();
+ PieJobSource.SeriesCollection.Clear();
+ PieJobRunStatus.SeriesCollection.Clear();
+ PieGradientSolid.SeriesCollection.Clear();
+ }
+
+ /// <summary>
+ /// Adds the statistics value.
+ /// </summary>
+ public void AddStatisticsValue(string name, object value, string unit)
+ {
+ StatisticsCollection.Add(new StatisticsValue() { Name = name, Value = value, Unit = unit });
+ RaisePropertyChanged("StatisticsCollection");
+ }
+
+ /// <summary>
+ /// Creates the thread consumption per thread.
+ /// </summary>
+ public void CreateThreadConsumptionPerThread(List<StatisticsValue> threads)
+ {
+ ThreadConsumptionPerThreadCollection.Add(new CollectionContainer() { Collection = threads.Take(threads.Count() > 2 ? 2 : threads.Count()) });
+ if (threads.Count() > 2)
+ {
+ ThreadConsumptionPerThreadCollection.Add(new CollectionContainer() { Collection = new List<MoreValue>() { new MoreValue() { Text = "More threads ..." } } });
+ ThreadConsumptionPerThread = threads.Skip(2).ToList();
+ }
+
+ RaisePropertyChanged("ThreadConsumptionPerThreadCollection");
+ }
+
+ #region GeneratePieChart
+ Func<ChartPoint, string> labelPoint = chartPoint =>
+ string.Format("{0} ({1:P})", chartPoint.Y, chartPoint.Participation);
+
+ public void GeneratePieJobSource(int PPCCount, int MSCount)
+ {
+ var series = new PieSeries()
+ {
+ Title = "PPC",
+ Values = new ChartValues<int>() { PPCCount },
+ Fill = new SolidColorBrush(_pieColors[4]),
+ DataLabels = true,
+ ToolTip = "",
+ LabelPoint = labelPoint
+
+ };
+
+ PieJobSource.SeriesCollection.Add(series);
+
+ series = new PieSeries()
+ {
+ Title = "MS",
+ Values = new ChartValues<int>() { MSCount },
+ Fill = new SolidColorBrush(_pieColors[3]),
+ DataLabels = true,
+ ToolTip = "",
+ LabelPoint = labelPoint
+ };
+ PieJobSource.SeriesCollection.Add(series);
+ RaisePropertyChanged("PieJobSource");
+ }
+
+ public void GeneratePieJobRunStatus(int failedCount, int abortedCount, int completedCount)
+ {
+ var series = new PieSeries()
+ {
+ Title = "Failed",
+ Values = new ChartValues<int>() { failedCount },
+ Fill = new SolidColorBrush(_pieColors[0]),
+ DataLabels = true,
+ ToolTip = "",
+ LabelPoint = labelPoint
+ };
+
+ PieJobRunStatus.SeriesCollection.Add(series);
+
+ series = new PieSeries()
+ {
+ Title = "Aborted",
+ Values = new ChartValues<int>() { abortedCount },
+ Fill = new SolidColorBrush(_pieColors[1]),
+ DataLabels = true,
+ ToolTip = "",
+ LabelPoint = labelPoint,
+
+ };
+ PieJobRunStatus.SeriesCollection.Add(series);
+
+ series = new PieSeries()
+ {
+ Title = "Completed",
+ Values = new ChartValues<int>() { completedCount },
+ Fill = new SolidColorBrush(_pieColors[2]),
+ DataLabels = true,
+ ToolTip = "",
+ LabelPoint = labelPoint
+ };
+ PieJobRunStatus.SeriesCollection.Add(series);
+
+ RaisePropertyChanged("PieJobRunStatus");
+ }
+
+ public void GeneratePieGradientSolid(int gradientCount, int solidCount)
+ {
+ var series = new PieSeries()
+ {
+ Title = "Solid",
+ Values = new ChartValues<int>() { solidCount },
+ Fill = new SolidColorBrush(_pieColors[4]),
+ DataLabels = true,
+ ToolTip = "",
+ LabelPoint = labelPoint
+ };
+ PieGradientSolid.SeriesCollection.Add(series);
+
+ series = new PieSeries()
+ {
+ Title = "Gradient",
+ Values = new ChartValues<int>() { gradientCount },
+ Fill = new SolidColorBrush(_pieColors[3]),
+ DataLabels = true,
+ ToolTip = "",
+ LabelPoint = labelPoint
+
+ };
+
+ PieGradientSolid.SeriesCollection.Add(series);
+
+
+ RaisePropertyChanged("PieGradientSolid");
+ }
+ #endregion
+
+ /// <summary>
+ /// Generates the statistics liquid quantity and TotalLiquidQuantities.
+ /// </summary>
+ public void GenerateStatisticsLiquidQuantity(List<TotalLiquidQuantityModel> liquidQuantities)
+ {
+ LiquidQuantities = liquidQuantities;
+ TotalLiquidQuantities = 0;
+
+ foreach (var item in liquidQuantities)
+ {
+ TotalLiquidQuantities += (ulong)item.Quantity;
+ }
+ }
+
+ }
+}
diff --git a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Statistics/Models/TotalLiquidQuantityModel.cs b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Statistics/Models/TotalLiquidQuantityModel.cs
new file mode 100644
index 000000000..b5e2e9fb7
--- /dev/null
+++ b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Statistics/Models/TotalLiquidQuantityModel.cs
@@ -0,0 +1,15 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Tango.BL.Enumerations;
+
+namespace Tango.MachineStudio.Statistics.Models
+{
+ public class TotalLiquidQuantityModel
+ {
+ public LiquidTypes LiquidType { get; set; }
+ public ulong Quantity { get; set; }
+ }
+}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/FileSystem/DefaultFileSystemService.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/FileSystem/DefaultFileSystemService.cs
new file mode 100644
index 000000000..cf7a21e13
--- /dev/null
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/FileSystem/DefaultFileSystemService.cs
@@ -0,0 +1,243 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.IO.Compression;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Tango.Core;
+using Tango.Core.DI;
+using Tango.Core.IO;
+using Tango.FileSystem;
+using Tango.FileSystem.Network;
+using Tango.Integration.ExternalBridge;
+using Tango.PPC.Common.ExternalBridge;
+using Tango.Transport;
+
+namespace Tango.PPC.Common.FileSystem
+{
+ [TangoCreateWhenRegistered]
+ public class DefaultFileSystemService : ExtendedObject, IFileSystemService, IExternalBridgeRequestHandler
+ {
+ private enum FileSystemOperationMode
+ {
+ Upload,
+ Download
+ }
+
+ private class FileSystemOperation
+ {
+ public FileSystemOperationMode Mode { get; set; }
+ public String Id { get; set; }
+ public String Path { get; set; }
+ public bool IsPathTempZip { get; set; }
+
+ public FileSystemOperation(FileSystemOperationMode mode, String path)
+ {
+ Mode = mode;
+ Id = Guid.NewGuid().ToString();
+ Path = path;
+ }
+ }
+
+ private FileSystemManager _manager;
+ private Dictionary<String, FileSystemOperation> _operations;
+
+ public bool Enabled { get; set; } = true;
+
+ public DefaultFileSystemService(IPPCExternalBridgeService externalBridge)
+ {
+ _manager = new FileSystemManager();
+ _operations = new Dictionary<string, FileSystemOperation>();
+ externalBridge.RegisterRequestHandler(this);
+ }
+
+ [ExternalBridgeRequestHandlerMethod(typeof(GetFileSystemItemRequest))]
+ public async void OnGetFileSystemItemRequest(GetFileSystemItemRequest request, String token, ExternalBridgeReceiver receiver)
+ {
+ try
+ {
+ FileSystemItemDTO dto = _manager.GetFolder(request);
+ await receiver.SendGenericResponse(new GetFileSystemItemResponse() { FileSystemItem = dto }, token);
+ }
+ catch (Exception ex)
+ {
+ await receiver.SendErrorResponse(ex, token);
+ }
+ }
+
+ [ExternalBridgeRequestHandlerMethod(typeof(FileUploadRequest))]
+ public async void OnFileUploadRequest(FileUploadRequest request, String token, ExternalBridgeReceiver receiver)
+ {
+ try
+ {
+ using (var stream = new FileStream(request.Path, FileMode.Create)) { }
+
+ FileSystemOperation operation = new FileSystemOperation(FileSystemOperationMode.Upload, request.Path);
+ _operations.Add(operation.Id, operation);
+
+ await receiver.SendGenericResponse(new FileUploadResponse() { OperationId = operation.Id }, token);
+ }
+ catch (Exception ex)
+ {
+ await receiver.SendErrorResponse(ex, token);
+ }
+ }
+
+ [ExternalBridgeRequestHandlerMethod(typeof(FileDownloadRequest))]
+ public async void OnFileDownloadRequest(FileDownloadRequest request, String token, ExternalBridgeReceiver receiver)
+ {
+ try
+ {
+ if (!File.Exists(request.Path))
+ {
+ await receiver.SendErrorResponse(new FileNotFoundException("Could not find the specified file."), token);
+ return;
+ }
+
+ FileSystemOperation operation = new FileSystemOperation(FileSystemOperationMode.Download, request.Path);
+
+ _operations.Add(operation.Id, operation);
+
+ await receiver.SendGenericResponse(new FileDownloadResponse()
+ {
+ OperationId = operation.Id,
+ Length = new FileInfo(request.Path).Length
+ }, token);
+ }
+ catch (Exception ex)
+ {
+ await receiver.SendErrorResponse(ex, token);
+ }
+ }
+
+ [ExternalBridgeRequestHandlerMethod(typeof(FolderDownloadRequest))]
+ public async void OnFolderDownloadRequest(FolderDownloadRequest request, String token, ExternalBridgeReceiver receiver)
+ {
+ try
+ {
+ if (!Directory.Exists(request.Path))
+ {
+ await receiver.SendErrorResponse(new FileNotFoundException("Could not find the specified directory."), token);
+ return;
+ }
+
+ var tempFile = TemporaryManager.CreateImaginaryFile();
+
+ ZipFile.CreateFromDirectory(request.Path, tempFile);
+
+ FileSystemOperation operation = new FileSystemOperation(FileSystemOperationMode.Download, tempFile);
+ operation.IsPathTempZip = true;
+
+ _operations.Add(operation.Id, operation);
+
+ await receiver.SendGenericResponse(new FolderDownloadResponse()
+ {
+ OperationId = operation.Id,
+ Length = new FileInfo(tempFile).Length
+ }, token);
+ }
+ catch (Exception ex)
+ {
+ await receiver.SendErrorResponse(ex, token);
+ }
+ }
+
+ [ExternalBridgeRequestHandlerMethod(typeof(ChunkUploadRequest))]
+ public async void OnChunkUploadRequest(ChunkUploadRequest request, String token, ExternalBridgeReceiver receiver)
+ {
+ try
+ {
+ FileSystemOperation operation;
+ _operations.TryGetValue(request.OperationId, out operation);
+
+ if (operation == null)
+ {
+ await receiver.SendErrorResponse(new ArgumentException("Invalid operation id."), token);
+ return;
+ }
+
+ using (var stream = new FileStream(operation.Path, FileMode.Append))
+ {
+ stream.Write(request.Data, 0, request.Data.Length);
+ }
+
+ await receiver.SendGenericResponse(new ChunkUploadResponse(), token, new TransportResponseConfig() { Priority = QueuePriority.Low });
+ }
+ catch (Exception ex)
+ {
+ await receiver.SendErrorResponse(ex, token);
+ }
+ }
+
+ [ExternalBridgeRequestHandlerMethod(typeof(ChunkDownloadRequest))]
+ public async void OnChunkDownloadRequest(ChunkDownloadRequest request, String token, ExternalBridgeReceiver receiver)
+ {
+ FileSystemOperation operation;
+ _operations.TryGetValue(request.OperationId, out operation);
+
+ if (operation == null)
+ {
+ await receiver.SendErrorResponse(new ArgumentException("Invalid operation id."), token);
+ return;
+ }
+
+ using (FileStream stream = new FileStream(operation.Path, FileMode.Open))
+ {
+ stream.Position = request.Position;
+ byte[] data = new byte[Math.Min(request.MaxChunkSize, stream.Length - stream.Position)];
+ await stream.ReadAsync(data, 0, data.Length);
+ await receiver.SendGenericResponse(new ChunkDownloadResponse()
+ {
+ Data = data
+ }, token, new TransportResponseConfig() { Priority = QueuePriority.Low });
+ }
+ }
+
+ [ExternalBridgeRequestHandlerMethod(typeof(AbortOperationRequest))]
+ public async void OnAbortOperationRequest(AbortOperationRequest request, String token, ExternalBridgeReceiver receiver)
+ {
+ FileSystemOperation operation;
+ _operations.TryGetValue(request.OperationId, out operation);
+
+ if (operation == null)
+ {
+ await receiver.SendErrorResponse(new ArgumentException("Invalid operation id."), token);
+ return;
+ }
+
+ try
+ {
+ if (operation.Mode == FileSystemOperationMode.Upload)
+ {
+ if (File.Exists(operation.Path))
+ {
+ File.Delete(operation.Path);
+ }
+ else if (Directory.Exists(operation.Path))
+ {
+ Directory.Delete(operation.Path, true);
+ }
+ }
+ else if (operation.IsPathTempZip)
+ {
+ if (File.Exists(operation.Path))
+ {
+ File.Delete(operation.Path);
+ }
+ }
+
+ await receiver.SendGenericResponse(new AbortOperationResponse(), token);
+ }
+ catch (Exception ex)
+ {
+ await receiver.SendErrorResponse(ex, token);
+ }
+ }
+
+ public void OnReceiverDisconnected(ExternalBridgeReceiver receiver)
+ {
+
+ }
+ }
+}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/FileSystem/IFileSystemService.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/FileSystem/IFileSystemService.cs
new file mode 100644
index 000000000..050bb1cd6
--- /dev/null
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/FileSystem/IFileSystemService.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tango.PPC.Common.FileSystem
+{
+ public interface IFileSystemService
+ {
+ bool Enabled { get; set; }
+ }
+}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/Performance/DefaultPerformanceService.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/Performance/DefaultPerformanceService.cs
new file mode 100644
index 000000000..2279d204c
--- /dev/null
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/Performance/DefaultPerformanceService.cs
@@ -0,0 +1,227 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using Tango.Core;
+using Tango.Core.DI;
+using Tango.Integration.ExternalBridge;
+using Tango.Integration.ExternalBridge.Network.Performance;
+using Tango.PPC.Common.ExternalBridge;
+
+namespace Tango.PPC.Common.Performance
+{
+ [TangoCreateWhenRegistered]
+ public class DefaultPerformanceService : ExtendedObject, IPerformanceService
+ {
+ #region Nested Classes
+
+ public static class PerformanceInfo
+ {
+ [DllImport("psapi.dll", SetLastError = true)]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ public static extern bool GetPerformanceInfo([Out] out PerformanceInformation PerformanceInformation, [In] int Size);
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct PerformanceInformation
+ {
+ public int Size;
+ public IntPtr CommitTotal;
+ public IntPtr CommitLimit;
+ public IntPtr CommitPeak;
+ public IntPtr PhysicalTotal;
+ public IntPtr PhysicalAvailable;
+ public IntPtr SystemCache;
+ public IntPtr KernelTotal;
+ public IntPtr KernelPaged;
+ public IntPtr KernelNonPaged;
+ public IntPtr PageSize;
+ public int HandlesCount;
+ public int ProcessCount;
+ public int ThreadCount;
+ }
+
+ public static Int64 GetPhysicalAvailableMemoryInMiB()
+ {
+ PerformanceInformation pi = new PerformanceInformation();
+ if (GetPerformanceInfo(out pi, Marshal.SizeOf(pi)))
+ {
+ return Convert.ToInt64((pi.PhysicalAvailable.ToInt64() * pi.PageSize.ToInt64() / 1048576));
+ }
+ else
+ {
+ return -1;
+ }
+
+ }
+
+ public static Int64 GetTotalMemoryInMiB()
+ {
+ PerformanceInformation pi = new PerformanceInformation();
+ if (GetPerformanceInfo(out pi, Marshal.SizeOf(pi)))
+ {
+ return Convert.ToInt64((pi.PhysicalTotal.ToInt64() * pi.PageSize.ToInt64() / 1048576));
+ }
+ else
+ {
+ return -1;
+ }
+
+ }
+ }
+
+ #endregion
+
+ private class PerformanceClient
+ {
+ public ExternalBridgeReceiver Receiver { get; set; }
+ public String Token { get; set; }
+ }
+
+ private List<PerformanceClient> _clients;
+ private PerformancePackage _package;
+ private bool _isStarted;
+ private Thread _performanceThread;
+
+ public bool Enabled { get; set; } = true;
+
+ public DefaultPerformanceService(IPPCExternalBridgeService externalBridge)
+ {
+ _package = new PerformancePackage();
+ _clients = new List<PerformanceClient>();
+ externalBridge.RegisterRequestHandler(this);
+ }
+
+ [ExternalBridgeRequestHandlerMethod(typeof(StartPerformanceUpdatesRequest))]
+ public async void OnStartPerformanceUpdatesRequest(StartPerformanceUpdatesRequest request, String token, ExternalBridgeReceiver receiver)
+ {
+ if (Enabled)
+ {
+ try
+ {
+ if (!_clients.Exists(x => x.Receiver == receiver))
+ {
+ _clients.Add(new PerformanceClient() { Receiver = receiver, Token = token });
+ OnReceiversChanged();
+ }
+
+ await receiver.SendGenericResponse(new StartPerformanceUpdatesResponse() { Package = _package }, token);
+ }
+ catch (Exception ex)
+ {
+ LogManager.Log(ex, "Error sending performance package.");
+ }
+ }
+ }
+
+ public void OnReceiverDisconnected(ExternalBridgeReceiver receiver)
+ {
+ _clients.RemoveAll(x => x.Receiver == receiver);
+ OnReceiversChanged();
+ }
+
+ private void OnReceiversChanged()
+ {
+ if (_clients.Count > 0 && !_isStarted)
+ {
+ _isStarted = true;
+ _performanceThread = new Thread(PerformanceThreadMethod);
+ _performanceThread.IsBackground = true;
+ _performanceThread.Start();
+ }
+ else if (_clients.Count == 0 && _isStarted)
+ {
+ _isStarted = false;
+ }
+ }
+
+ private async void PerformanceThreadMethod()
+ {
+ while (_isStarted)
+ {
+ try
+ {
+ _package.ApplicationCPU = (int)GetAppCPU();
+ _package.CPU = (int)GetTotalCPU();
+ _package.ApplicationRAM = (int)BytesToMegaBytes(GetAppRam());
+ _package.MaxRAM = (int)BytesToMegaBytes((long)new Microsoft.VisualBasic.Devices.ComputerInfo().TotalPhysicalMemory);
+ _package.RAM = _package.MaxRAM - (int)PerformanceInfo.GetPhysicalAvailableMemoryInMiB();
+
+ DriveInfo info = new DriveInfo("C");
+ _package.DiskCapacity = (int)BytesToMegaBytes(info.TotalSize);
+ _package.AvailableDiskSpace = (int)BytesToMegaBytes(info.AvailableFreeSpace);
+ _package.DateTime = DateTime.Now;
+
+ foreach (var client in _clients.ToList())
+ {
+ try
+ {
+ await client.Receiver.SendGenericResponse(new StartPerformanceUpdatesResponse() { Package = _package }, client.Token);
+ }
+ catch (Exception ex)
+ {
+ LogManager.Log(ex, "Error sending performance package.");
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ LogManager.Log(ex, "Error creating performance package.");
+ }
+
+ Thread.Sleep(200);
+ }
+ }
+
+ #region Helpers
+
+ private float BytesToMegaBytes(long bytes)
+ {
+ return bytes / 1024f / 1024f;
+ }
+
+ public float GetAppCPU()
+ {
+ PerformanceCounter cpuCounter = new PerformanceCounter();
+ cpuCounter.CategoryName = "Process";
+ cpuCounter.CounterName = "% Processor Time";
+ cpuCounter.InstanceName = Process.GetCurrentProcess().ProcessName;
+
+ // will always start at 0
+ float firstValue = cpuCounter.NextValue();
+ System.Threading.Thread.Sleep(1000);
+ // now matches task manager reading
+ float secondValue = cpuCounter.NextValue();
+
+ return secondValue / Environment.ProcessorCount;
+ }
+
+ public float GetTotalCPU()
+ {
+ PerformanceCounter cpuCounter = new PerformanceCounter();
+ cpuCounter.CategoryName = "Processor";
+ cpuCounter.CounterName = "% Processor Time";
+ cpuCounter.InstanceName = "_Total";
+
+ // will always start at 0
+ float firstValue = cpuCounter.NextValue();
+ System.Threading.Thread.Sleep(1000);
+ // now matches task manager reading
+ float secondValue = cpuCounter.NextValue();
+
+ return secondValue;
+ }
+
+ public long GetAppRam()
+ {
+ Process proc = Process.GetCurrentProcess();
+ return proc.PrivateMemorySize64;
+ }
+
+ #endregion
+ }
+}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/Performance/IPerformanceService.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/Performance/IPerformanceService.cs
new file mode 100644
index 000000000..c3bfd1543
--- /dev/null
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/Performance/IPerformanceService.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Tango.Integration.ExternalBridge;
+
+namespace Tango.PPC.Common.Performance
+{
+ public interface IPerformanceService : IExternalBridgeRequestHandler
+ {
+ bool Enabled { get; set; }
+ }
+}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/SystemInfo/DefaultSystemInfoService.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/SystemInfo/DefaultSystemInfoService.cs
new file mode 100644
index 000000000..205013e0c
--- /dev/null
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/SystemInfo/DefaultSystemInfoService.cs
@@ -0,0 +1,133 @@
+using Microsoft.WindowsAPICodePack.Net;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Tango.Core;
+using Tango.Core.DI;
+using Tango.Integration.ExternalBridge;
+using Tango.Integration.ExternalBridge.Network.Information;
+using Tango.PPC.Common.Application;
+using Tango.PPC.Common.Connectivity;
+using Tango.PPC.Common.ExternalBridge;
+using Tango.Settings;
+using Tango.SystemInfo;
+
+namespace Tango.PPC.Common.SystemInfo
+{
+ [TangoCreateWhenRegistered]
+ public class DefaultSystemInfoService : ExtendedObject, ISystemInfoService, IExternalBridgeRequestHandler
+ {
+ public bool Enabled { get; set; } = true;
+
+ private GetMachineInformationResponse response;
+ private IPPCApplicationManager _applicationManager;
+ private IConnectivityProvider _connectivityProvider;
+
+ public DefaultSystemInfoService(IPPCExternalBridgeService externalBridge, IPPCApplicationManager applicationManager, IConnectivityProvider connectivityProvider)
+ {
+ _applicationManager = applicationManager;
+ _connectivityProvider = connectivityProvider;
+ externalBridge.RegisterRequestHandler(this);
+ }
+
+ [ExternalBridgeRequestHandlerMethod(typeof(GetMachineInformationRequest))]
+ public async void OnGetMachineInformationRequest(GetMachineInformationRequest request, String token, ExternalBridgeReceiver receiver)
+ {
+ try
+ {
+ if (response == null)
+ {
+ //Get the networks that are currently connected to
+ var connectedNetwork = NetworkListManager.GetNetworks(NetworkConnectivityLevels.Connected).FirstOrDefault();
+
+ var settings = SettingsManager.Default.GetOrCreate<PPCSettings>();
+
+ var system = SystemObjectsCollection.Create();
+
+ //Add custom information..
+ system.Insert(0, new SystemObjectsCollection()
+ {
+ Name = "Application",
+ Objects = new List<SystemObject>()
+ {
+ new SystemObject()
+ {
+ Name = "Tango PPC",
+ Properties = new List<SystemObjectProperty>()
+ {
+ new SystemObjectProperty() { Name = "Version", Value = _applicationManager.Version.ToString(3) },
+ new SystemObjectProperty() { Name = "Build Date", Value = _applicationManager.BuildDate.ToString() },
+ new SystemObjectProperty() { Name = "Previous Version", Value = settings.PreviousApplicationVersion.ToString() },
+ new SystemObjectProperty() { Name = "Firmware Version", Value = _applicationManager.FirmwareVersion.ToString() },
+ new SystemObjectProperty() { Name = "Technician Mode", Value = _applicationManager.IsInTechnicianMode.ToStringYesNo() },
+ new SystemObjectProperty() { Name = "After Update", Value = _applicationManager.IsAfterUpdate.ToStringYesNo() },
+ new SystemObjectProperty() { Name = "Startup Date", Value = _applicationManager.StartUpDate.ToString() },
+ },
+ },
+ new SystemObject()
+ {
+ Name = "Network",
+ Properties = new List<SystemObjectProperty>()
+ {
+ new SystemObjectProperty() { Name = "Network Name", Value = connectedNetwork.Name },
+ new SystemObjectProperty() { Name = "Category", Value = connectedNetwork.Category.ToString() },
+ new SystemObjectProperty() { Name = "Type", Value = connectedNetwork.Connectivity.ToString() },
+ new SystemObjectProperty() { Name = "Domain", Value = connectedNetwork.DomainType.ToString() },
+ new SystemObjectProperty() { Name = "Connected Time", Value = connectedNetwork.ConnectedTime.ToString() },
+ new SystemObjectProperty() { Name = "Internet Connection", Value = connectedNetwork.IsConnectedToInternet.ToStringYesNo() },
+ },
+ },
+ new SystemObject()
+ {
+ Name = "Settings",
+ Properties = new List<SystemObjectProperty>()
+ {
+ new SystemObjectProperty() { Name = "Application State", Value = settings.ApplicationState.ToString() },
+ new SystemObjectProperty() { Name = "Auto Update Check", Value = settings.AutoCheckForUpdates.ToStringYesNo() },
+ new SystemObjectProperty() { Name = "Auto Update Interval", Value = settings.AutoUpdateCheckInterval.ToString() },
+ new SystemObjectProperty() { Name = "Automatic Thread Loading", Value = settings.EnableAutomaticThreadLoading.ToStringYesNo() },
+ new SystemObjectProperty() { Name = "Firmware Logs Enabled", Value = settings.EnableEmbeddedDebugLogs.ToStringYesNo() },
+ new SystemObjectProperty() { Name = "Emergency Switch Enabled", Value = settings.EnableEmergencyNotifications.ToStringYesNo() },
+ new SystemObjectProperty() { Name = "Liquid Quantity Validation Enabled", Value = settings.EnableJobLiquidQuantityValidation.ToStringYesNo() },
+ new SystemObjectProperty() { Name = "Remote Assistance Enabled", Value = settings.EnableRemoteAssistance.ToStringYesNo() },
+ new SystemObjectProperty() { Name = "Remote Desktop Enabled", Value = settings.EnableRemoteDesktop.ToStringYesNo() },
+ new SystemObjectProperty() { Name = "Start in Technician Mode", Value = settings.EnableTechnicianModeByDefault.ToStringYesNo() },
+ new SystemObjectProperty() { Name = "Watchdog Enabled", Value = settings.EnableWatchDog.ToStringYesNo() },
+ new SystemObjectProperty() { Name = "Job Units Method", Value = settings.JobUnitsMethod.ToString() },
+ new SystemObjectProperty() { Name = "PowerUp Screen Enabled", Value = settings.DisplayPowerUpScreen.ToStringYesNo() },
+ new SystemObjectProperty() { Name = "Firmware COM Port", Value = settings.EmbeddedComPort.ToString() },
+ new SystemObjectProperty() { Name = "Emergency COM Port", Value = settings.EmergencyComPort.ToString() },
+ new SystemObjectProperty() { Name = "Job Upload Method", Value = settings.JobUploadStrategy.ToString() },
+ new SystemObjectProperty() { Name = "Diagnostics Synchronization", Value = settings.SynchronizeDiagnostics.ToStringYesNo() },
+ new SystemObjectProperty() { Name = "Jobs Synchronization", Value = settings.SynchronizeJobs.ToStringYesNo() },
+ new SystemObjectProperty() { Name = "TCP Write Mode", Value = settings.TcpTransportAdapterWriteMode.ToString() },
+ }.OrderBy(x => x.Name).ToList(),
+ },
+ },
+ });
+
+ response = new GetMachineInformationResponse()
+ {
+ Package = new InformationPackage()
+ {
+ System = system,
+ }
+ };
+ }
+
+ await receiver.SendGenericResponse(response, token);
+ }
+ catch (Exception ex)
+ {
+ LogManager.Log(ex, "Error sending system information.");
+ }
+ }
+
+ public void OnReceiverDisconnected(ExternalBridgeReceiver receiver)
+ {
+
+ }
+ }
+}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/SystemInfo/ISystemInfoService.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/SystemInfo/ISystemInfoService.cs
new file mode 100644
index 000000000..0cc493891
--- /dev/null
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/SystemInfo/ISystemInfoService.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tango.PPC.Common.SystemInfo
+{
+ public interface ISystemInfoService
+ {
+ bool Enabled { get; set; }
+ }
+}
diff --git a/Software/Visual_Studio/SideChains/Priority Queue/FastPriorityQueue.cs b/Software/Visual_Studio/SideChains/Priority Queue/FastPriorityQueue.cs
new file mode 100644
index 000000000..927a20d41
--- /dev/null
+++ b/Software/Visual_Studio/SideChains/Priority Queue/FastPriorityQueue.cs
@@ -0,0 +1,593 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Runtime.CompilerServices;
+
+namespace Priority_Queue
+{
+ /// <summary>
+ /// An implementation of a min-Priority Queue using a heap. Has O(1) .Contains()!
+ /// See https://github.com/BlueRaja/High-Speed-Priority-Queue-for-C-Sharp/wiki/Getting-Started for more information
+ /// </summary>
+ /// <typeparam name="T">The values in the queue. Must extend the FastPriorityQueueNode class</typeparam>
+ public sealed class FastPriorityQueue<T> : IFixedSizePriorityQueue<T, float>
+ where T : FastPriorityQueueNode
+ {
+ private int _numNodes;
+ private T[] _nodes;
+
+ /// <summary>
+ /// Instantiate a new Priority Queue
+ /// </summary>
+ /// <param name="maxNodes">The max nodes ever allowed to be enqueued (going over this will cause undefined behavior)</param>
+ public FastPriorityQueue(int maxNodes)
+ {
+#if DEBUG
+ if (maxNodes <= 0)
+ {
+ throw new InvalidOperationException("New queue size cannot be smaller than 1");
+ }
+#endif
+
+ _numNodes = 0;
+ _nodes = new T[maxNodes + 1];
+ }
+
+ /// <summary>
+ /// Returns the number of nodes in the queue.
+ /// O(1)
+ /// </summary>
+ public int Count
+ {
+ get
+ {
+ return _numNodes;
+ }
+ }
+
+ /// <summary>
+ /// Returns the maximum number of items that can be enqueued at once in this queue. Once you hit this number (ie. once Count == MaxSize),
+ /// attempting to enqueue another item will cause undefined behavior. O(1)
+ /// </summary>
+ public int MaxSize
+ {
+ get
+ {
+ return _nodes.Length - 1;
+ }
+ }
+
+ /// <summary>
+ /// Removes every node from the queue.
+ /// O(n) (So, don't do this often!)
+ /// </summary>
+#if NET_VERSION_4_5
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
+ public void Clear()
+ {
+ Array.Clear(_nodes, 1, _numNodes);
+ _numNodes = 0;
+ }
+
+ /// <summary>
+ /// Returns (in O(1)!) whether the given node is in the queue.
+ /// If node is or has been previously added to another queue, the result is undefined unless oldQueue.ResetNode(node) has been called
+ /// O(1)
+ /// </summary>
+#if NET_VERSION_4_5
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
+ public bool Contains(T node)
+ {
+#if DEBUG
+ if(node == null)
+ {
+ throw new ArgumentNullException("node");
+ }
+ if (node.Queue != null && !Equals(node.Queue))
+ {
+ throw new InvalidOperationException("node.Contains was called on a node from another queue. Please call originalQueue.ResetNode() first");
+ }
+ if(node.QueueIndex < 0 || node.QueueIndex >= _nodes.Length)
+ {
+ throw new InvalidOperationException("node.QueueIndex has been corrupted. Did you change it manually? Or add this node to another queue?");
+ }
+#endif
+
+ return (_nodes[node.QueueIndex] == node);
+ }
+
+ /// <summary>
+ /// Enqueue a node to the priority queue. Lower values are placed in front. Ties are broken arbitrarily.
+ /// If the queue is full, the result is undefined.
+ /// If the node is already enqueued, the result is undefined.
+ /// If node is or has been previously added to another queue, the result is undefined unless oldQueue.ResetNode(node) has been called
+ /// O(log n)
+ /// </summary>
+#if NET_VERSION_4_5
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
+ public void Enqueue(T node, float priority)
+ {
+#if DEBUG
+ if(node == null)
+ {
+ throw new ArgumentNullException("node");
+ }
+ if(_numNodes >= _nodes.Length - 1)
+ {
+ throw new InvalidOperationException("Queue is full - node cannot be added: " + node);
+ }
+ if (node.Queue != null && !Equals(node.Queue))
+ {
+ throw new InvalidOperationException("node.Enqueue was called on a node from another queue. Please call originalQueue.ResetNode() first");
+ }
+ if (Contains(node))
+ {
+ throw new InvalidOperationException("Node is already enqueued: " + node);
+ }
+ node.Queue = this;
+#endif
+
+ node.Priority = priority;
+ _numNodes++;
+ _nodes[_numNodes] = node;
+ node.QueueIndex = _numNodes;
+ CascadeUp(node);
+ }
+
+#if NET_VERSION_4_5
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
+ private void CascadeUp(T node)
+ {
+ //aka Heapify-up
+ int parent;
+ if(node.QueueIndex > 1)
+ {
+ parent = node.QueueIndex >> 1;
+ T parentNode = _nodes[parent];
+ if(HasHigherOrEqualPriority(parentNode, node))
+ return;
+
+ //Node has lower priority value, so move parent down the heap to make room
+ _nodes[node.QueueIndex] = parentNode;
+ parentNode.QueueIndex = node.QueueIndex;
+
+ node.QueueIndex = parent;
+ }
+ else
+ {
+ return;
+ }
+ while(parent > 1)
+ {
+ parent >>= 1;
+ T parentNode = _nodes[parent];
+ if(HasHigherOrEqualPriority(parentNode, node))
+ break;
+
+ //Node has lower priority value, so move parent down the heap to make room
+ _nodes[node.QueueIndex] = parentNode;
+ parentNode.QueueIndex = node.QueueIndex;
+
+ node.QueueIndex = parent;
+ }
+ _nodes[node.QueueIndex] = node;
+ }
+
+#if NET_VERSION_4_5
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
+ private void CascadeDown(T node)
+ {
+ //aka Heapify-down
+ int finalQueueIndex = node.QueueIndex;
+ int childLeftIndex = 2 * finalQueueIndex;
+
+ // If leaf node, we're done
+ if(childLeftIndex > _numNodes)
+ {
+ return;
+ }
+
+ // Check if the left-child is higher-priority than the current node
+ int childRightIndex = childLeftIndex + 1;
+ T childLeft = _nodes[childLeftIndex];
+ if(HasHigherPriority(childLeft, node))
+ {
+ // Check if there is a right child. If not, swap and finish.
+ if(childRightIndex > _numNodes)
+ {
+ node.QueueIndex = childLeftIndex;
+ childLeft.QueueIndex = finalQueueIndex;
+ _nodes[finalQueueIndex] = childLeft;
+ _nodes[childLeftIndex] = node;
+ return;
+ }
+ // Check if the left-child is higher-priority than the right-child
+ T childRight = _nodes[childRightIndex];
+ if(HasHigherPriority(childLeft, childRight))
+ {
+ // left is highest, move it up and continue
+ childLeft.QueueIndex = finalQueueIndex;
+ _nodes[finalQueueIndex] = childLeft;
+ finalQueueIndex = childLeftIndex;
+ }
+ else
+ {
+ // right is even higher, move it up and continue
+ childRight.QueueIndex = finalQueueIndex;
+ _nodes[finalQueueIndex] = childRight;
+ finalQueueIndex = childRightIndex;
+ }
+ }
+ // Not swapping with left-child, does right-child exist?
+ else if(childRightIndex > _numNodes)
+ {
+ return;
+ }
+ else
+ {
+ // Check if the right-child is higher-priority than the current node
+ T childRight = _nodes[childRightIndex];
+ if(HasHigherPriority(childRight, node))
+ {
+ childRight.QueueIndex = finalQueueIndex;
+ _nodes[finalQueueIndex] = childRight;
+ finalQueueIndex = childRightIndex;
+ }
+ // Neither child is higher-priority than current, so finish and stop.
+ else
+ {
+ return;
+ }
+ }
+
+ while(true)
+ {
+ childLeftIndex = 2 * finalQueueIndex;
+
+ // If leaf node, we're done
+ if(childLeftIndex > _numNodes)
+ {
+ node.QueueIndex = finalQueueIndex;
+ _nodes[finalQueueIndex] = node;
+ break;
+ }
+
+ // Check if the left-child is higher-priority than the current node
+ childRightIndex = childLeftIndex + 1;
+ childLeft = _nodes[childLeftIndex];
+ if(HasHigherPriority(childLeft, node))
+ {
+ // Check if there is a right child. If not, swap and finish.
+ if(childRightIndex > _numNodes)
+ {
+ node.QueueIndex = childLeftIndex;
+ childLeft.QueueIndex = finalQueueIndex;
+ _nodes[finalQueueIndex] = childLeft;
+ _nodes[childLeftIndex] = node;
+ break;
+ }
+ // Check if the left-child is higher-priority than the right-child
+ T childRight = _nodes[childRightIndex];
+ if(HasHigherPriority(childLeft, childRight))
+ {
+ // left is highest, move it up and continue
+ childLeft.QueueIndex = finalQueueIndex;
+ _nodes[finalQueueIndex] = childLeft;
+ finalQueueIndex = childLeftIndex;
+ }
+ else
+ {
+ // right is even higher, move it up and continue
+ childRight.QueueIndex = finalQueueIndex;
+ _nodes[finalQueueIndex] = childRight;
+ finalQueueIndex = childRightIndex;
+ }
+ }
+ // Not swapping with left-child, does right-child exist?
+ else if(childRightIndex > _numNodes)
+ {
+ node.QueueIndex = finalQueueIndex;
+ _nodes[finalQueueIndex] = node;
+ break;
+ }
+ else
+ {
+ // Check if the right-child is higher-priority than the current node
+ T childRight = _nodes[childRightIndex];
+ if(HasHigherPriority(childRight, node))
+ {
+ childRight.QueueIndex = finalQueueIndex;
+ _nodes[finalQueueIndex] = childRight;
+ finalQueueIndex = childRightIndex;
+ }
+ // Neither child is higher-priority than current, so finish and stop.
+ else
+ {
+ node.QueueIndex = finalQueueIndex;
+ _nodes[finalQueueIndex] = node;
+ break;
+ }
+ }
+ }
+ }
+
+ /// <summary>
+ /// Returns true if 'higher' has higher priority than 'lower', false otherwise.
+ /// Note that calling HasHigherPriority(node, node) (ie. both arguments the same node) will return false
+ /// </summary>
+#if NET_VERSION_4_5
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
+ private bool HasHigherPriority(T higher, T lower)
+ {
+ return (higher.Priority < lower.Priority);
+ }
+
+ /// <summary>
+ /// Returns true if 'higher' has higher priority than 'lower', false otherwise.
+ /// Note that calling HasHigherOrEqualPriority(node, node) (ie. both arguments the same node) will return true
+ /// </summary>
+#if NET_VERSION_4_5
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
+ private bool HasHigherOrEqualPriority(T higher, T lower)
+ {
+ return (higher.Priority <= lower.Priority);
+ }
+
+ /// <summary>
+ /// Removes the head of the queue and returns it.
+ /// If queue is empty, result is undefined
+ /// O(log n)
+ /// </summary>
+#if NET_VERSION_4_5
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
+ public T Dequeue()
+ {
+#if DEBUG
+ if(_numNodes <= 0)
+ {
+ throw new InvalidOperationException("Cannot call Dequeue() on an empty queue");
+ }
+
+ if(!IsValidQueue())
+ {
+ throw new InvalidOperationException("Queue has been corrupted (Did you update a node priority manually instead of calling UpdatePriority()?" +
+ "Or add the same node to two different queues?)");
+ }
+#endif
+
+ T returnMe = _nodes[1];
+ //If the node is already the last node, we can remove it immediately
+ if(_numNodes == 1)
+ {
+ _nodes[1] = null;
+ _numNodes = 0;
+ return returnMe;
+ }
+
+ //Swap the node with the last node
+ T formerLastNode = _nodes[_numNodes];
+ _nodes[1] = formerLastNode;
+ formerLastNode.QueueIndex = 1;
+ _nodes[_numNodes] = null;
+ _numNodes--;
+
+ //Now bubble formerLastNode (which is no longer the last node) down
+ CascadeDown(formerLastNode);
+ return returnMe;
+ }
+
+ /// <summary>
+ /// Resize the queue so it can accept more nodes. All currently enqueued nodes are remain.
+ /// Attempting to decrease the queue size to a size too small to hold the existing nodes results in undefined behavior
+ /// O(n)
+ /// </summary>
+ public void Resize(int maxNodes)
+ {
+#if DEBUG
+ if (maxNodes <= 0)
+ {
+ throw new InvalidOperationException("Queue size cannot be smaller than 1");
+ }
+
+ if (maxNodes < _numNodes)
+ {
+ throw new InvalidOperationException("Called Resize(" + maxNodes + "), but current queue contains " + _numNodes + " nodes");
+ }
+#endif
+
+ T[] newArray = new T[maxNodes + 1];
+ int highestIndexToCopy = Math.Min(maxNodes, _numNodes);
+ Array.Copy(_nodes, newArray, highestIndexToCopy + 1);
+ _nodes = newArray;
+ }
+
+ /// <summary>
+ /// Returns the head of the queue, without removing it (use Dequeue() for that).
+ /// If the queue is empty, behavior is undefined.
+ /// O(1)
+ /// </summary>
+ public T First
+ {
+ get
+ {
+#if DEBUG
+ if(_numNodes <= 0)
+ {
+ throw new InvalidOperationException("Cannot call .First on an empty queue");
+ }
+#endif
+
+ return _nodes[1];
+ }
+ }
+
+ /// <summary>
+ /// This method must be called on a node every time its priority changes while it is in the queue.
+ /// <b>Forgetting to call this method will result in a corrupted queue!</b>
+ /// Calling this method on a node not in the queue results in undefined behavior
+ /// O(log n)
+ /// </summary>
+#if NET_VERSION_4_5
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
+ public void UpdatePriority(T node, float priority)
+ {
+#if DEBUG
+ if(node == null)
+ {
+ throw new ArgumentNullException("node");
+ }
+ if (node.Queue != null && !Equals(node.Queue))
+ {
+ throw new InvalidOperationException("node.UpdatePriority was called on a node from another queue");
+ }
+ if (!Contains(node))
+ {
+ throw new InvalidOperationException("Cannot call UpdatePriority() on a node which is not enqueued: " + node);
+ }
+#endif
+
+ node.Priority = priority;
+ OnNodeUpdated(node);
+ }
+
+#if NET_VERSION_4_5
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
+ private void OnNodeUpdated(T node)
+ {
+ //Bubble the updated node up or down as appropriate
+ int parentIndex = node.QueueIndex >> 1;
+
+ if(parentIndex > 0 && HasHigherPriority(node, _nodes[parentIndex]))
+ {
+ CascadeUp(node);
+ }
+ else
+ {
+ //Note that CascadeDown will be called if parentNode == node (that is, node is the root)
+ CascadeDown(node);
+ }
+ }
+
+ /// <summary>
+ /// Removes a node from the queue. The node does not need to be the head of the queue.
+ /// If the node is not in the queue, the result is undefined. If unsure, check Contains() first
+ /// O(log n)
+ /// </summary>
+#if NET_VERSION_4_5
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
+ public void Remove(T node)
+ {
+#if DEBUG
+ if(node == null)
+ {
+ throw new ArgumentNullException("node");
+ }
+ if (node.Queue != null && !Equals(node.Queue))
+ {
+ throw new InvalidOperationException("node.Remove was called on a node from another queue");
+ }
+ if (!Contains(node))
+ {
+ throw new InvalidOperationException("Cannot call Remove() on a node which is not enqueued: " + node);
+ }
+#endif
+
+ //If the node is already the last node, we can remove it immediately
+ if(node.QueueIndex == _numNodes)
+ {
+ _nodes[_numNodes] = null;
+ _numNodes--;
+ return;
+ }
+
+ //Swap the node with the last node
+ T formerLastNode = _nodes[_numNodes];
+ _nodes[node.QueueIndex] = formerLastNode;
+ formerLastNode.QueueIndex = node.QueueIndex;
+ _nodes[_numNodes] = null;
+ _numNodes--;
+
+ //Now bubble formerLastNode (which is no longer the last node) up or down as appropriate
+ OnNodeUpdated(formerLastNode);
+ }
+
+ /// <summary>
+ /// By default, nodes that have been previously added to one queue cannot be added to another queue.
+ /// If you need to do this, please call originalQueue.ResetNode(node) before attempting to add it in the new queue
+ /// If the node is currently in the queue or belongs to another queue, the result is undefined
+ /// </summary>
+#if NET_VERSION_4_5
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
+ public void ResetNode(T node)
+ {
+#if DEBUG
+ if (node == null)
+ {
+ throw new ArgumentNullException("node");
+ }
+ if (node.Queue != null && !Equals(node.Queue))
+ {
+ throw new InvalidOperationException("node.ResetNode was called on a node from another queue");
+ }
+ if (Contains(node))
+ {
+ throw new InvalidOperationException("node.ResetNode was called on a node that is still in the queue");
+ }
+
+ node.Queue = null;
+#endif
+
+ node.QueueIndex = 0;
+ }
+
+ public IEnumerator<T> GetEnumerator()
+ {
+#if NET_VERSION_4_5 // ArraySegment does not implement IEnumerable before 4.5
+ IEnumerable<T> e = new ArraySegment<T>(_nodes, 1, _numNodes);
+ return e.GetEnumerator();
+#else
+ for(int i = 1; i <= _numNodes; i++)
+ yield return _nodes[i];
+#endif
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+
+ /// <summary>
+ /// <b>Should not be called in production code.</b>
+ /// Checks to make sure the queue is still in a valid state. Used for testing/debugging the queue.
+ /// </summary>
+ public bool IsValidQueue()
+ {
+ for(int i = 1; i < _nodes.Length; i++)
+ {
+ if(_nodes[i] != null)
+ {
+ int childLeftIndex = 2 * i;
+ if(childLeftIndex < _nodes.Length && _nodes[childLeftIndex] != null && HasHigherPriority(_nodes[childLeftIndex], _nodes[i]))
+ return false;
+
+ int childRightIndex = childLeftIndex + 1;
+ if(childRightIndex < _nodes.Length && _nodes[childRightIndex] != null && HasHigherPriority(_nodes[childRightIndex], _nodes[i]))
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+} \ No newline at end of file
diff --git a/Software/Visual_Studio/SideChains/Priority Queue/FastPriorityQueueNode.cs b/Software/Visual_Studio/SideChains/Priority Queue/FastPriorityQueueNode.cs
new file mode 100644
index 000000000..54b0573ec
--- /dev/null
+++ b/Software/Visual_Studio/SideChains/Priority Queue/FastPriorityQueueNode.cs
@@ -0,0 +1,25 @@
+using System;
+
+namespace Priority_Queue
+{
+ public class FastPriorityQueueNode
+ {
+ /// <summary>
+ /// The Priority to insert this node at. Must be set BEFORE adding a node to the queue (ideally just once, in the node's constructor).
+ /// Should not be manually edited once the node has been enqueued - use queue.UpdatePriority() instead
+ /// </summary>
+ public float Priority { get; protected internal set; }
+
+ /// <summary>
+ /// Represents the current position in the queue
+ /// </summary>
+ public int QueueIndex { get; internal set; }
+
+#if DEBUG
+ /// <summary>
+ /// The queue this node is tied to. Used only for debug builds.
+ /// </summary>
+ public object Queue { get; internal set; }
+#endif
+ }
+}
diff --git a/Software/Visual_Studio/SideChains/Priority Queue/GenericPriorityQueue.cs b/Software/Visual_Studio/SideChains/Priority Queue/GenericPriorityQueue.cs
new file mode 100644
index 000000000..7687ba213
--- /dev/null
+++ b/Software/Visual_Studio/SideChains/Priority Queue/GenericPriorityQueue.cs
@@ -0,0 +1,602 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Runtime.CompilerServices;
+
+namespace Priority_Queue
+{
+ /// <summary>
+ /// A copy of StablePriorityQueue which also has generic priority-type
+ /// </summary>
+ /// <typeparam name="TItem">The values in the queue. Must extend the GenericPriorityQueueNode class</typeparam>
+ /// <typeparam name="TPriority">The priority-type. Must extend IComparable&lt;TPriority&gt;</typeparam>
+ public sealed class GenericPriorityQueue<TItem, TPriority> : IFixedSizePriorityQueue<TItem, TPriority>
+ where TItem : GenericPriorityQueueNode<TPriority>
+ where TPriority : IComparable<TPriority>
+ {
+ private int _numNodes;
+ private TItem[] _nodes;
+ private long _numNodesEverEnqueued;
+ private readonly Comparison<TPriority> _comparer;
+
+ /// <summary>
+ /// Instantiate a new Priority Queue
+ /// </summary>
+ /// <param name="maxNodes">The max nodes ever allowed to be enqueued (going over this will cause undefined behavior)</param>
+ public GenericPriorityQueue(int maxNodes) : this(maxNodes, Comparer<TPriority>.Default) { }
+
+ /// <summary>
+ /// Instantiate a new Priority Queue
+ /// </summary>
+ /// <param name="maxNodes">The max nodes ever allowed to be enqueued (going over this will cause undefined behavior)</param>
+ /// <param name="comparer">The comparer used to compare TPriority values.</param>
+ public GenericPriorityQueue(int maxNodes, IComparer<TPriority> comparer) : this(maxNodes, comparer.Compare) { }
+
+ /// <summary>
+ /// Instantiate a new Priority Queue
+ /// </summary>
+ /// <param name="maxNodes">The max nodes ever allowed to be enqueued (going over this will cause undefined behavior)</param>
+ /// <param name="comparer">The comparison function to use to compare TPriority values</param>
+ public GenericPriorityQueue(int maxNodes, Comparison<TPriority> comparer)
+ {
+#if DEBUG
+ if (maxNodes <= 0)
+ {
+ throw new InvalidOperationException("New queue size cannot be smaller than 1");
+ }
+#endif
+
+ _numNodes = 0;
+ _nodes = new TItem[maxNodes + 1];
+ _numNodesEverEnqueued = 0;
+ _comparer = comparer;
+ }
+
+ /// <summary>
+ /// Returns the number of nodes in the queue.
+ /// O(1)
+ /// </summary>
+ public int Count
+ {
+ get
+ {
+ return _numNodes;
+ }
+ }
+
+ /// <summary>
+ /// Returns the maximum number of items that can be enqueued at once in this queue. Once you hit this number (ie. once Count == MaxSize),
+ /// attempting to enqueue another item will cause undefined behavior. O(1)
+ /// </summary>
+ public int MaxSize
+ {
+ get
+ {
+ return _nodes.Length - 1;
+ }
+ }
+
+ /// <summary>
+ /// Removes every node from the queue.
+ /// O(n) (So, don't do this often!)
+ /// </summary>
+#if NET_VERSION_4_5
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
+ public void Clear()
+ {
+ Array.Clear(_nodes, 1, _numNodes);
+ _numNodes = 0;
+ }
+
+ /// <summary>
+ /// Returns (in O(1)!) whether the given node is in the queue.
+ /// If node is or has been previously added to another queue, the result is undefined unless oldQueue.ResetNode(node) has been called
+ /// O(1)
+ /// </summary>
+#if NET_VERSION_4_5
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
+ public bool Contains(TItem node)
+ {
+#if DEBUG
+ if(node == null)
+ {
+ throw new ArgumentNullException("node");
+ }
+ if (node.Queue != null && !Equals(node.Queue))
+ {
+ throw new InvalidOperationException("node.Contains was called on a node from another queue. Please call originalQueue.ResetNode() first");
+ }
+ if (node.QueueIndex < 0 || node.QueueIndex >= _nodes.Length)
+ {
+ throw new InvalidOperationException("node.QueueIndex has been corrupted. Did you change it manually?");
+ }
+#endif
+
+ return (_nodes[node.QueueIndex] == node);
+ }
+
+ /// <summary>
+ /// Enqueue a node to the priority queue. Lower values are placed in front. Ties are broken by first-in-first-out.
+ /// If the queue is full, the result is undefined.
+ /// If the node is already enqueued, the result is undefined.
+ /// If node is or has been previously added to another queue, the result is undefined unless oldQueue.ResetNode(node) has been called
+ /// O(log n)
+ /// </summary>
+#if NET_VERSION_4_5
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
+ public void Enqueue(TItem node, TPriority priority)
+ {
+#if DEBUG
+ if(node == null)
+ {
+ throw new ArgumentNullException("node");
+ }
+ if(_numNodes >= _nodes.Length - 1)
+ {
+ throw new InvalidOperationException("Queue is full - node cannot be added: " + node);
+ }
+ if (node.Queue != null && !Equals(node.Queue))
+ {
+ throw new InvalidOperationException("node.Enqueue was called on a node from another queue. Please call originalQueue.ResetNode() first");
+ }
+ if (Contains(node))
+ {
+ throw new InvalidOperationException("Node is already enqueued: " + node);
+ }
+ node.Queue = this;
+#endif
+
+ node.Priority = priority;
+ _numNodes++;
+ _nodes[_numNodes] = node;
+ node.QueueIndex = _numNodes;
+ node.InsertionIndex = _numNodesEverEnqueued++;
+ CascadeUp(node);
+ }
+
+#if NET_VERSION_4_5
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
+ private void CascadeUp(TItem node)
+ {
+ //aka Heapify-up
+ int parent;
+ if (node.QueueIndex > 1)
+ {
+ parent = node.QueueIndex >> 1;
+ TItem parentNode = _nodes[parent];
+ if(HasHigherPriority(parentNode, node))
+ return;
+
+ //Node has lower priority value, so move parent down the heap to make room
+ _nodes[node.QueueIndex] = parentNode;
+ parentNode.QueueIndex = node.QueueIndex;
+
+ node.QueueIndex = parent;
+ }
+ else
+ {
+ return;
+ }
+ while(parent > 1)
+ {
+ parent >>= 1;
+ TItem parentNode = _nodes[parent];
+ if(HasHigherPriority(parentNode, node))
+ break;
+
+ //Node has lower priority value, so move parent down the heap to make room
+ _nodes[node.QueueIndex] = parentNode;
+ parentNode.QueueIndex = node.QueueIndex;
+
+ node.QueueIndex = parent;
+ }
+ _nodes[node.QueueIndex] = node;
+ }
+
+#if NET_VERSION_4_5
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
+ private void CascadeDown(TItem node)
+ {
+ //aka Heapify-down
+ int finalQueueIndex = node.QueueIndex;
+ int childLeftIndex = 2 * finalQueueIndex;
+
+ // If leaf node, we're done
+ if(childLeftIndex > _numNodes)
+ {
+ return;
+ }
+
+ // Check if the left-child is higher-priority than the current node
+ int childRightIndex = childLeftIndex + 1;
+ TItem childLeft = _nodes[childLeftIndex];
+ if(HasHigherPriority(childLeft, node))
+ {
+ // Check if there is a right child. If not, swap and finish.
+ if(childRightIndex > _numNodes)
+ {
+ node.QueueIndex = childLeftIndex;
+ childLeft.QueueIndex = finalQueueIndex;
+ _nodes[finalQueueIndex] = childLeft;
+ _nodes[childLeftIndex] = node;
+ return;
+ }
+ // Check if the left-child is higher-priority than the right-child
+ TItem childRight = _nodes[childRightIndex];
+ if(HasHigherPriority(childLeft, childRight))
+ {
+ // left is highest, move it up and continue
+ childLeft.QueueIndex = finalQueueIndex;
+ _nodes[finalQueueIndex] = childLeft;
+ finalQueueIndex = childLeftIndex;
+ }
+ else
+ {
+ // right is even higher, move it up and continue
+ childRight.QueueIndex = finalQueueIndex;
+ _nodes[finalQueueIndex] = childRight;
+ finalQueueIndex = childRightIndex;
+ }
+ }
+ // Not swapping with left-child, does right-child exist?
+ else if(childRightIndex > _numNodes)
+ {
+ return;
+ }
+ else
+ {
+ // Check if the right-child is higher-priority than the current node
+ TItem childRight = _nodes[childRightIndex];
+ if(HasHigherPriority(childRight, node))
+ {
+ childRight.QueueIndex = finalQueueIndex;
+ _nodes[finalQueueIndex] = childRight;
+ finalQueueIndex = childRightIndex;
+ }
+ // Neither child is higher-priority than current, so finish and stop.
+ else
+ {
+ return;
+ }
+ }
+
+ while(true)
+ {
+ childLeftIndex = 2 * finalQueueIndex;
+
+ // If leaf node, we're done
+ if(childLeftIndex > _numNodes)
+ {
+ node.QueueIndex = finalQueueIndex;
+ _nodes[finalQueueIndex] = node;
+ break;
+ }
+
+ // Check if the left-child is higher-priority than the current node
+ childRightIndex = childLeftIndex + 1;
+ childLeft = _nodes[childLeftIndex];
+ if(HasHigherPriority(childLeft, node))
+ {
+ // Check if there is a right child. If not, swap and finish.
+ if(childRightIndex > _numNodes)
+ {
+ node.QueueIndex = childLeftIndex;
+ childLeft.QueueIndex = finalQueueIndex;
+ _nodes[finalQueueIndex] = childLeft;
+ _nodes[childLeftIndex] = node;
+ break;
+ }
+ // Check if the left-child is higher-priority than the right-child
+ TItem childRight = _nodes[childRightIndex];
+ if(HasHigherPriority(childLeft, childRight))
+ {
+ // left is highest, move it up and continue
+ childLeft.QueueIndex = finalQueueIndex;
+ _nodes[finalQueueIndex] = childLeft;
+ finalQueueIndex = childLeftIndex;
+ }
+ else
+ {
+ // right is even higher, move it up and continue
+ childRight.QueueIndex = finalQueueIndex;
+ _nodes[finalQueueIndex] = childRight;
+ finalQueueIndex = childRightIndex;
+ }
+ }
+ // Not swapping with left-child, does right-child exist?
+ else if(childRightIndex > _numNodes)
+ {
+ node.QueueIndex = finalQueueIndex;
+ _nodes[finalQueueIndex] = node;
+ break;
+ }
+ else
+ {
+ // Check if the right-child is higher-priority than the current node
+ TItem childRight = _nodes[childRightIndex];
+ if(HasHigherPriority(childRight, node))
+ {
+ childRight.QueueIndex = finalQueueIndex;
+ _nodes[finalQueueIndex] = childRight;
+ finalQueueIndex = childRightIndex;
+ }
+ // Neither child is higher-priority than current, so finish and stop.
+ else
+ {
+ node.QueueIndex = finalQueueIndex;
+ _nodes[finalQueueIndex] = node;
+ break;
+ }
+ }
+ }
+ }
+
+ /// <summary>
+ /// Returns true if 'higher' has higher priority than 'lower', false otherwise.
+ /// Note that calling HasHigherPriority(node, node) (ie. both arguments the same node) will return false
+ /// </summary>
+#if NET_VERSION_4_5
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
+ private bool HasHigherPriority(TItem higher, TItem lower)
+ {
+ var cmp = _comparer(higher.Priority, lower.Priority);
+ return (cmp < 0 || (cmp == 0 && higher.InsertionIndex < lower.InsertionIndex));
+ }
+
+ /// <summary>
+ /// Removes the head of the queue (node with minimum priority; ties are broken by order of insertion), and returns it.
+ /// If queue is empty, result is undefined
+ /// O(log n)
+ /// </summary>
+#if NET_VERSION_4_5
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
+ public TItem Dequeue()
+ {
+#if DEBUG
+ if(_numNodes <= 0)
+ {
+ throw new InvalidOperationException("Cannot call Dequeue() on an empty queue");
+ }
+
+ if(!IsValidQueue())
+ {
+ throw new InvalidOperationException("Queue has been corrupted (Did you update a node priority manually instead of calling UpdatePriority()?" +
+ "Or add the same node to two different queues?)");
+ }
+#endif
+
+ TItem returnMe = _nodes[1];
+ //If the node is already the last node, we can remove it immediately
+ if(_numNodes == 1)
+ {
+ _nodes[1] = null;
+ _numNodes = 0;
+ return returnMe;
+ }
+
+ //Swap the node with the last node
+ TItem formerLastNode = _nodes[_numNodes];
+ _nodes[1] = formerLastNode;
+ formerLastNode.QueueIndex = 1;
+ _nodes[_numNodes] = null;
+ _numNodes--;
+
+ //Now bubble formerLastNode (which is no longer the last node) down
+ CascadeDown(formerLastNode);
+ return returnMe;
+ }
+
+ /// <summary>
+ /// Resize the queue so it can accept more nodes. All currently enqueued nodes are remain.
+ /// Attempting to decrease the queue size to a size too small to hold the existing nodes results in undefined behavior
+ /// O(n)
+ /// </summary>
+ public void Resize(int maxNodes)
+ {
+#if DEBUG
+ if (maxNodes <= 0)
+ {
+ throw new InvalidOperationException("Queue size cannot be smaller than 1");
+ }
+
+ if (maxNodes < _numNodes)
+ {
+ throw new InvalidOperationException("Called Resize(" + maxNodes + "), but current queue contains " + _numNodes + " nodes");
+ }
+#endif
+
+ TItem[] newArray = new TItem[maxNodes + 1];
+ int highestIndexToCopy = Math.Min(maxNodes, _numNodes);
+ Array.Copy(_nodes, newArray, highestIndexToCopy + 1);
+ _nodes = newArray;
+ }
+
+ /// <summary>
+ /// Returns the head of the queue, without removing it (use Dequeue() for that).
+ /// If the queue is empty, behavior is undefined.
+ /// O(1)
+ /// </summary>
+ public TItem First
+ {
+ get
+ {
+#if DEBUG
+ if(_numNodes <= 0)
+ {
+ throw new InvalidOperationException("Cannot call .First on an empty queue");
+ }
+#endif
+
+ return _nodes[1];
+ }
+ }
+
+ /// <summary>
+ /// This method must be called on a node every time its priority changes while it is in the queue.
+ /// <b>Forgetting to call this method will result in a corrupted queue!</b>
+ /// Calling this method on a node not in the queue results in undefined behavior
+ /// O(log n)
+ /// </summary>
+#if NET_VERSION_4_5
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
+ public void UpdatePriority(TItem node, TPriority priority)
+ {
+#if DEBUG
+ if(node == null)
+ {
+ throw new ArgumentNullException("node");
+ }
+ if (node.Queue != null && !Equals(node.Queue))
+ {
+ throw new InvalidOperationException("node.UpdatePriority was called on a node from another queue");
+ }
+ if (!Contains(node))
+ {
+ throw new InvalidOperationException("Cannot call UpdatePriority() on a node which is not enqueued: " + node);
+ }
+#endif
+
+ node.Priority = priority;
+ OnNodeUpdated(node);
+ }
+
+#if NET_VERSION_4_5
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
+ private void OnNodeUpdated(TItem node)
+ {
+ //Bubble the updated node up or down as appropriate
+ int parentIndex = node.QueueIndex >> 1;
+
+ if(parentIndex > 0 && HasHigherPriority(node, _nodes[parentIndex]))
+ {
+ CascadeUp(node);
+ }
+ else
+ {
+ //Note that CascadeDown will be called if parentNode == node (that is, node is the root)
+ CascadeDown(node);
+ }
+ }
+
+ /// <summary>
+ /// Removes a node from the queue. The node does not need to be the head of the queue.
+ /// If the node is not in the queue, the result is undefined. If unsure, check Contains() first
+ /// O(log n)
+ /// </summary>
+#if NET_VERSION_4_5
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
+ public void Remove(TItem node)
+ {
+#if DEBUG
+ if(node == null)
+ {
+ throw new ArgumentNullException("node");
+ }
+ if (node.Queue != null && !Equals(node.Queue))
+ {
+ throw new InvalidOperationException("node.Remove was called on a node from another queue");
+ }
+ if (!Contains(node))
+ {
+ throw new InvalidOperationException("Cannot call Remove() on a node which is not enqueued: " + node);
+ }
+#endif
+
+ //If the node is already the last node, we can remove it immediately
+ if(node.QueueIndex == _numNodes)
+ {
+ _nodes[_numNodes] = null;
+ _numNodes--;
+ return;
+ }
+
+ //Swap the node with the last node
+ TItem formerLastNode = _nodes[_numNodes];
+ _nodes[node.QueueIndex] = formerLastNode;
+ formerLastNode.QueueIndex = node.QueueIndex;
+ _nodes[_numNodes] = null;
+ _numNodes--;
+
+ //Now bubble formerLastNode (which is no longer the last node) up or down as appropriate
+ OnNodeUpdated(formerLastNode);
+ }
+
+ /// <summary>
+ /// By default, nodes that have been previously added to one queue cannot be added to another queue.
+ /// If you need to do this, please call originalQueue.ResetNode(node) before attempting to add it in the new queue
+ /// </summary>
+#if NET_VERSION_4_5
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
+ public void ResetNode(TItem node)
+ {
+#if DEBUG
+ if (node == null)
+ {
+ throw new ArgumentNullException("node");
+ }
+ if (node.Queue != null && !Equals(node.Queue))
+ {
+ throw new InvalidOperationException("node.ResetNode was called on a node from another queue");
+ }
+ if (Contains(node))
+ {
+ throw new InvalidOperationException("node.ResetNode was called on a node that is still in the queue");
+ }
+
+ node.Queue = null;
+#endif
+
+ node.QueueIndex = 0;
+ }
+
+
+ public IEnumerator<TItem> GetEnumerator()
+ {
+#if NET_VERSION_4_5 // ArraySegment does not implement IEnumerable before 4.5
+ IEnumerable<TItem> e = new ArraySegment<TItem>(_nodes, 1, _numNodes);
+ return e.GetEnumerator();
+#else
+ for(int i = 1; i <= _numNodes; i++)
+ yield return _nodes[i];
+#endif
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+
+ /// <summary>
+ /// <b>Should not be called in production code.</b>
+ /// Checks to make sure the queue is still in a valid state. Used for testing/debugging the queue.
+ /// </summary>
+ public bool IsValidQueue()
+ {
+ for(int i = 1; i < _nodes.Length; i++)
+ {
+ if(_nodes[i] != null)
+ {
+ int childLeftIndex = 2 * i;
+ if(childLeftIndex < _nodes.Length && _nodes[childLeftIndex] != null && HasHigherPriority(_nodes[childLeftIndex], _nodes[i]))
+ return false;
+
+ int childRightIndex = childLeftIndex + 1;
+ if(childRightIndex < _nodes.Length && _nodes[childRightIndex] != null && HasHigherPriority(_nodes[childRightIndex], _nodes[i]))
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+}
diff --git a/Software/Visual_Studio/SideChains/Priority Queue/GenericPriorityQueueNode.cs b/Software/Visual_Studio/SideChains/Priority Queue/GenericPriorityQueueNode.cs
new file mode 100644
index 000000000..5a53ed24e
--- /dev/null
+++ b/Software/Visual_Studio/SideChains/Priority Queue/GenericPriorityQueueNode.cs
@@ -0,0 +1,29 @@
+namespace Priority_Queue
+{
+ public class GenericPriorityQueueNode<TPriority>
+ {
+ /// <summary>
+ /// The Priority to insert this node at. Must be set BEFORE adding a node to the queue (ideally just once, in the node's constructor).
+ /// Should not be manually edited once the node has been enqueued - use queue.UpdatePriority() instead
+ /// </summary>
+ public TPriority Priority { get; protected internal set; }
+
+ /// <summary>
+ /// Represents the current position in the queue
+ /// </summary>
+ public int QueueIndex { get; internal set; }
+
+ /// <summary>
+ /// Represents the order the node was inserted in
+ /// </summary>
+ public long InsertionIndex { get; internal set; }
+
+
+#if DEBUG
+ /// <summary>
+ /// The queue this node is tied to. Used only for debug builds.
+ /// </summary>
+ public object Queue { get; internal set; }
+#endif
+ }
+}
diff --git a/Software/Visual_Studio/SideChains/Priority Queue/IFixedSizePriorityQueue.cs b/Software/Visual_Studio/SideChains/Priority Queue/IFixedSizePriorityQueue.cs
new file mode 100644
index 000000000..e95f7c29b
--- /dev/null
+++ b/Software/Visual_Studio/SideChains/Priority Queue/IFixedSizePriorityQueue.cs
@@ -0,0 +1,31 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Priority_Queue
+{
+ /// <summary>
+ /// A helper-interface only needed to make writing unit tests a bit easier (hence the 'internal' access modifier)
+ /// </summary>
+ internal interface IFixedSizePriorityQueue<TItem, in TPriority> : IPriorityQueue<TItem, TPriority>
+ where TPriority : IComparable<TPriority>
+ {
+ /// <summary>
+ /// Resize the queue so it can accept more nodes. All currently enqueued nodes are remain.
+ /// Attempting to decrease the queue size to a size too small to hold the existing nodes results in undefined behavior
+ /// </summary>
+ void Resize(int maxNodes);
+
+ /// <summary>
+ /// Returns the maximum number of items that can be enqueued at once in this queue. Once you hit this number (ie. once Count == MaxSize),
+ /// attempting to enqueue another item will cause undefined behavior.
+ /// </summary>
+ int MaxSize { get; }
+
+ /// <summary>
+ /// By default, nodes that have been previously added to one queue cannot be added to another queue.
+ /// If you need to do this, please call originalQueue.ResetNode(node) before attempting to add it in the new queue
+ /// </summary>
+ void ResetNode(TItem node);
+ }
+}
diff --git a/Software/Visual_Studio/SideChains/Priority Queue/IPriorityQueue.cs b/Software/Visual_Studio/SideChains/Priority Queue/IPriorityQueue.cs
new file mode 100644
index 000000000..a5053e955
--- /dev/null
+++ b/Software/Visual_Studio/SideChains/Priority Queue/IPriorityQueue.cs
@@ -0,0 +1,55 @@
+using System;
+using System.Collections.Generic;
+
+namespace Priority_Queue
+{
+ /// <summary>
+ /// The IPriorityQueue interface. This is mainly here for purists, and in case I decide to add more implementations later.
+ /// For speed purposes, it is actually recommended that you *don't* access the priority queue through this interface, since the JIT can
+ /// (theoretically?) optimize method calls from concrete-types slightly better.
+ /// </summary>
+ public interface IPriorityQueue<TItem, in TPriority> : IEnumerable<TItem>
+ where TPriority : IComparable<TPriority>
+ {
+ /// <summary>
+ /// Enqueue a node to the priority queue. Lower values are placed in front. Ties are broken by first-in-first-out.
+ /// See implementation for how duplicates are handled.
+ /// </summary>
+ void Enqueue(TItem node, TPriority priority);
+
+ /// <summary>
+ /// Removes the head of the queue (node with minimum priority; ties are broken by order of insertion), and returns it.
+ /// </summary>
+ TItem Dequeue();
+
+ /// <summary>
+ /// Removes every node from the queue.
+ /// </summary>
+ void Clear();
+
+ /// <summary>
+ /// Returns whether the given node is in the queue.
+ /// </summary>
+ bool Contains(TItem node);
+
+ /// <summary>
+ /// Removes a node from the queue. The node does not need to be the head of the queue.
+ /// </summary>
+ void Remove(TItem node);
+
+ /// <summary>
+ /// Call this method to change the priority of a node.
+ /// </summary>
+ void UpdatePriority(TItem node, TPriority priority);
+
+ /// <summary>
+ /// Returns the head of the queue, without removing it (use Dequeue() for that).
+ /// </summary>
+ TItem First { get; }
+
+ /// <summary>
+ /// Returns the number of nodes in the queue.
+ /// </summary>
+ int Count { get; }
+ }
+}
diff --git a/Software/Visual_Studio/SideChains/Priority Queue/Priority Queue.csproj b/Software/Visual_Studio/SideChains/Priority Queue/Priority Queue.csproj
new file mode 100644
index 000000000..8b02157f1
--- /dev/null
+++ b/Software/Visual_Studio/SideChains/Priority Queue/Priority Queue.csproj
@@ -0,0 +1,89 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <ProjectGuid>{1531C1EA-BD53-41D1-A34B-CFCDF79D2651}</ProjectGuid>
+ <OutputType>Library</OutputType>
+ <AppDesignerFolder>Properties</AppDesignerFolder>
+ <RootNamespace>Priority_Queue</RootNamespace>
+ <AssemblyName>Priority Queue</AssemblyName>
+ <TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
+ <FileAlignment>512</FileAlignment>
+ <CustomConstants Condition=" '$(TargetFrameworkVersion)' == 'v4.5' ">NET_VERSION_4_5</CustomConstants>
+ <DefineConstants>$(DefineConstants);$(CustomConstants)</DefineConstants>
+ <TargetFrameworkProfile>
+ </TargetFrameworkProfile>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>bin\Debug\</OutputPath>
+ <DefineConstants>DEBUG;TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ <CustomConstants Condition=" '$(TargetFrameworkVersion)' == 'v4.5' ">NET_VERSION_4_5</CustomConstants>
+ <DefineConstants>$(DefineConstants);$(CustomConstants)</DefineConstants>
+ <Prefer32Bit>false</Prefer32Bit>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <DebugType>pdbonly</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>bin\Release\</OutputPath>
+ <DefineConstants>TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ <CustomConstants Condition=" '$(TargetFrameworkVersion)' == 'v4.5' ">NET_VERSION_4_5</CustomConstants>
+ <DefineConstants>$(DefineConstants);$(CustomConstants)</DefineConstants>
+ <Prefer32Bit>false</Prefer32Bit>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release 4.5|AnyCPU' ">
+ <DebugType>pdbonly</DebugType>
+ <Optimize>true</Optimize>
+ <DefineConstants>TRACE;NET_VERSION_4_5</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ <Prefer32Bit>false</Prefer32Bit>
+ <OutputPath>bin\Release\net45\</OutputPath>
+ <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+ <DocumentationFile>bin\Release\net45\Priority Queue.xml</DocumentationFile>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release 2.0|AnyCPU'">
+ <DebugType>pdbonly</DebugType>
+ <Optimize>true</Optimize>
+ <DefineConstants>TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ <Prefer32Bit>false</Prefer32Bit>
+ <OutputPath>bin\Release\net20\</OutputPath>
+ <TargetFrameworkVersion>v2.0</TargetFrameworkVersion>
+ <DocumentationFile>bin\Release\net20\Priority Queue.xml</DocumentationFile>
+ </PropertyGroup>
+ <ItemGroup>
+ <Reference Include="System" />
+ <Reference Include="System.Data" />
+ <Reference Include="System.Xml" />
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="GenericPriorityQueue.cs" />
+ <Compile Include="GenericPriorityQueueNode.cs" />
+ <Compile Include="IFixedSizePriorityQueue.cs" />
+ <Compile Include="StablePriorityQueue.cs" />
+ <Compile Include="FastPriorityQueue.cs" />
+ <Compile Include="StablePriorityQueueNode.cs" />
+ <Compile Include="IPriorityQueue.cs" />
+ <Compile Include="FastPriorityQueueNode.cs" />
+ <Compile Include="Properties\AssemblyInfo.cs" />
+ <Compile Include="SimplePriorityQueue.cs" />
+ </ItemGroup>
+ <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+ <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
+ Other similar extension points exist, see Microsoft.Common.targets.
+ <Target Name="BeforeBuild">
+ </Target>
+ <Target Name="AfterBuild">
+ </Target>
+ -->
+</Project> \ No newline at end of file
diff --git a/Software/Visual_Studio/SideChains/Priority Queue/Priority Queue.nuspec b/Software/Visual_Studio/SideChains/Priority Queue/Priority Queue.nuspec
new file mode 100644
index 000000000..94ab70f4d
--- /dev/null
+++ b/Software/Visual_Studio/SideChains/Priority Queue/Priority Queue.nuspec
@@ -0,0 +1,42 @@
+<?xml version="1.0"?>
+<package >
+ <metadata>
+ <id>OptimizedPriorityQueue</id>
+ <version>4.2.0</version>
+ <title>Highly Optimized Priority Queue</title>
+ <authors>BlueRaja</authors>
+ <owners>BlueRaja</owners>
+ <licenseUrl>https://github.com/BlueRaja/High-Speed-Priority-Queue-for-C-Sharp/blob/master/LICENSE.txt</licenseUrl>
+ <projectUrl>https://github.com/BlueRaja/High-Speed-Priority-Queue-for-C-Sharp</projectUrl>
+ <requireLicenseAcceptance>false</requireLicenseAcceptance>
+ <description>A highly optimized Priority Queue for path-finding and related applications</description>
+ <releaseNotes>Speed improvements; added ResetNode(); included IEqualityComparer in SimplePriorityQueue to avoid boxing</releaseNotes>
+ <copyright>Copyright 2018</copyright>
+ <tags>C# priority-queue pathfinding optimized</tags>
+ </metadata>
+ <files>
+ <file src="bin\Release\net20\Priority Queue.dll" target="lib\net20\Priority Queue.dll" />
+ <file src="bin\Release\net20\Priority Queue.pdb" target="lib\net20\Priority Queue.pdb" />
+ <file src="bin\Release\net20\Priority Queue.xml" target="lib\net20\Priority Queue.xml" />
+
+ <file src="bin\Release\net45\Priority Queue.dll" target="lib\net45\Priority Queue.dll" />
+ <file src="bin\Release\net45\Priority Queue.pdb" target="lib\net45\Priority Queue.pdb" />
+ <file src="bin\Release\net45\Priority Queue.xml" target="lib\net45\Priority Queue.xml" />
+
+ <file src="..\Priority Queue UWP\bin\Release\netcore45\Priority Queue.dll" target="lib\netstandard1.0\Priority Queue.dll" />
+ <file src="..\Priority Queue UWP\bin\Release\netcore45\Priority Queue.pdb" target="lib\netstandard1.0\Priority Queue.pdb" />
+ <file src="..\Priority Queue UWP\bin\Release\netcore45\Priority Queue.xml" target="lib\netstandard1.0\Priority Queue.xml" />
+
+ <file src="..\Priority Queue PCL\bin\Release\Priority Queue.dll" target="lib\portable-net40+sl5+win8+wpa81+wp8\Priority Queue.dll" />
+ <file src="..\Priority Queue PCL\bin\Release\Priority Queue.pdb" target="lib\portable-net40+sl5+win8+wpa81+wp8\Priority Queue.pdb" />
+ <file src="..\Priority Queue PCL\bin\Release\Priority Queue.xml" target="lib\portable-net40+sl5+win8+wpa81+wp8\Priority Queue.xml" />
+
+ <file src="..\Priority Queue Unity Full\bin\Release\Priority Queue.dll" target="lib\net35-unity full v3.5\Priority Queue.dll" />
+ <file src="..\Priority Queue Unity Full\bin\Release\Priority Queue.pdb" target="lib\net35-unity full v3.5\Priority Queue.pdb" />
+ <file src="..\Priority Queue Unity Full\bin\Release\Priority Queue.xml" target="lib\net35-unity full v3.5\Priority Queue.xml" />
+
+ <file src="..\Priority Queue Unity Subset\bin\Release\Priority Queue.dll" target="lib\net35-unity subset v3.5\Priority Queue.dll" />
+ <file src="..\Priority Queue Unity Subset\bin\Release\Priority Queue.pdb" target="lib\net35-unity subset v3.5\Priority Queue.pdb" />
+ <file src="..\Priority Queue Unity Subset\bin\Release\Priority Queue.xml" target="lib\net35-unity subset v3.5\Priority Queue.xml" />
+ </files>
+</package> \ No newline at end of file
diff --git a/Software/Visual_Studio/SideChains/Priority Queue/Properties/AssemblyInfo.cs b/Software/Visual_Studio/SideChains/Priority Queue/Properties/AssemblyInfo.cs
new file mode 100644
index 000000000..7143261f2
--- /dev/null
+++ b/Software/Visual_Studio/SideChains/Priority Queue/Properties/AssemblyInfo.cs
@@ -0,0 +1,38 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("Highly Optimized Priority Queue")]
+[assembly: AssemblyDescription("A highly optimized Priority Queue for path-finding and related applications")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("Highly Optimized Priority Queue")]
+[assembly: AssemblyCopyright("Copyright © BlueRaja 2017")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("3eee6b54-af8a-494b-9121-3d46ed09a58b")]
+
+[assembly: InternalsVisibleToAttribute("Priority Queue Tests")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("4.2.0")]
+[assembly: AssemblyFileVersion("4.2.0")] \ No newline at end of file
diff --git a/Software/Visual_Studio/SideChains/Priority Queue/SimplePriorityQueue.cs b/Software/Visual_Studio/SideChains/Priority Queue/SimplePriorityQueue.cs
new file mode 100644
index 000000000..0d35ff1ac
--- /dev/null
+++ b/Software/Visual_Studio/SideChains/Priority Queue/SimplePriorityQueue.cs
@@ -0,0 +1,588 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+
+namespace Priority_Queue
+{
+ /// <summary>
+ /// A simplified priority queue implementation. Is stable, auto-resizes, and thread-safe, at the cost of being slightly slower than
+ /// FastPriorityQueue
+ /// Methods tagged as O(1) or O(log n) are assuming there are no duplicates. Duplicates may increase the algorithmic complexity.
+ /// </summary>
+ /// <typeparam name="TItem">The type to enqueue</typeparam>
+ /// <typeparam name="TPriority">The priority-type to use for nodes. Must extend IComparable&lt;TPriority&gt;</typeparam>
+ public class SimplePriorityQueue<TItem, TPriority> : IPriorityQueue<TItem, TPriority>
+ where TPriority : IComparable<TPriority>
+ {
+ private class SimpleNode : GenericPriorityQueueNode<TPriority>
+ {
+ public TItem Data { get; private set; }
+
+ public SimpleNode(TItem data)
+ {
+ Data = data;
+ }
+ }
+
+ private const int INITIAL_QUEUE_SIZE = 10;
+ private readonly GenericPriorityQueue<SimpleNode, TPriority> _queue;
+ private readonly Dictionary<TItem, IList<SimpleNode>> _itemToNodesCache;
+ private readonly IList<SimpleNode> _nullNodesCache;
+
+ #region Constructors
+ /// <summary>
+ /// Instantiate a new Priority Queue
+ /// </summary>
+ public SimplePriorityQueue() : this(Comparer<TPriority>.Default, EqualityComparer<TItem>.Default) { }
+
+ /// <summary>
+ /// Instantiate a new Priority Queue
+ /// </summary>
+ /// <param name="priorityComparer">The comparer used to compare TPriority values. Defaults to Comparer&lt;TPriority&gt;.default</param>
+ public SimplePriorityQueue(IComparer<TPriority> priorityComparer) : this(priorityComparer.Compare, EqualityComparer<TItem>.Default) { }
+
+ /// <summary>
+ /// Instantiate a new Priority Queue
+ /// </summary>
+ /// <param name="priorityComparer">The comparison function to use to compare TPriority values</param>
+ public SimplePriorityQueue(Comparison<TPriority> priorityComparer) : this(priorityComparer, EqualityComparer<TItem>.Default) { }
+
+ /// <summary>
+ /// Instantiate a new Priority Queue
+ /// </summary>
+ /// <param name="itemEquality">The equality comparison function to use to compare TItem values</param>
+ public SimplePriorityQueue(IEqualityComparer<TItem> itemEquality) : this(Comparer<TPriority>.Default, itemEquality) { }
+
+ /// <summary>
+ /// Instantiate a new Priority Queue
+ /// </summary>
+ /// <param name="priorityComparer">The comparer used to compare TPriority values. Defaults to Comparer&lt;TPriority&gt;.default</param>
+ /// <param name="itemEquality">The equality comparison function to use to compare TItem values</param>
+ public SimplePriorityQueue(IComparer<TPriority> priorityComparer, IEqualityComparer<TItem> itemEquality) : this(priorityComparer.Compare, itemEquality) { }
+
+ /// <summary>
+ /// Instantiate a new Priority Queue
+ /// </summary>
+ /// <param name="priorityComparer">The comparison function to use to compare TPriority values</param>
+ /// <param name="itemEquality">The equality comparison function to use to compare TItem values</param>
+ public SimplePriorityQueue(Comparison<TPriority> priorityComparer, IEqualityComparer<TItem> itemEquality)
+ {
+ _queue = new GenericPriorityQueue<SimpleNode, TPriority>(INITIAL_QUEUE_SIZE, priorityComparer);
+ _itemToNodesCache = new Dictionary<TItem, IList<SimpleNode>>(itemEquality);
+ _nullNodesCache = new List<SimpleNode>();
+ }
+ #endregion
+
+ /// <summary>
+ /// Given an item of type T, returns the existing SimpleNode in the queue
+ /// </summary>
+ private SimpleNode GetExistingNode(TItem item)
+ {
+ if (item == null)
+ {
+ return _nullNodesCache.Count > 0 ? _nullNodesCache[0] : null;
+ }
+
+ IList<SimpleNode> nodes;
+ if (!_itemToNodesCache.TryGetValue(item, out nodes))
+ {
+ return null;
+ }
+ return nodes[0];
+ }
+
+ /// <summary>
+ /// Adds an item to the Node-cache to allow for many methods to be O(1) or O(log n)
+ /// </summary>
+ private void AddToNodeCache(SimpleNode node)
+ {
+ if (node.Data == null)
+ {
+ _nullNodesCache.Add(node);
+ return;
+ }
+
+ IList<SimpleNode> nodes;
+ if (!_itemToNodesCache.TryGetValue(node.Data, out nodes))
+ {
+ nodes = new List<SimpleNode>();
+ _itemToNodesCache[node.Data] = nodes;
+ }
+ nodes.Add(node);
+ }
+
+ /// <summary>
+ /// Removes an item to the Node-cache to allow for many methods to be O(1) or O(log n) (assuming no duplicates)
+ /// </summary>
+ private void RemoveFromNodeCache(SimpleNode node)
+ {
+ if (node.Data == null)
+ {
+ _nullNodesCache.Remove(node);
+ return;
+ }
+
+ IList<SimpleNode> nodes;
+ if (!_itemToNodesCache.TryGetValue(node.Data, out nodes))
+ {
+ return;
+ }
+ nodes.Remove(node);
+ if (nodes.Count == 0)
+ {
+ _itemToNodesCache.Remove(node.Data);
+ }
+ }
+
+ /// <summary>
+ /// Returns the number of nodes in the queue.
+ /// O(1)
+ /// </summary>
+ public int Count
+ {
+ get
+ {
+ lock(_queue)
+ {
+ return _queue.Count;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Returns the head of the queue, without removing it (use Dequeue() for that).
+ /// Throws an exception when the queue is empty.
+ /// O(1)
+ /// </summary>
+ public TItem First
+ {
+ get
+ {
+ lock(_queue)
+ {
+ if(_queue.Count <= 0)
+ {
+ throw new InvalidOperationException("Cannot call .First on an empty queue");
+ }
+
+ return _queue.First.Data;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Removes every node from the queue.
+ /// O(n)
+ /// </summary>
+ public void Clear()
+ {
+ lock(_queue)
+ {
+ _queue.Clear();
+ _itemToNodesCache.Clear();
+ _nullNodesCache.Clear();
+ }
+ }
+
+ /// <summary>
+ /// Returns whether the given item is in the queue.
+ /// O(1)
+ /// </summary>
+ public bool Contains(TItem item)
+ {
+ lock(_queue)
+ {
+ return item == null ? _nullNodesCache.Count > 0 : _itemToNodesCache.ContainsKey(item);
+ }
+ }
+
+ /// <summary>
+ /// Removes the head of the queue (node with minimum priority; ties are broken by order of insertion), and returns it.
+ /// If queue is empty, throws an exception
+ /// O(log n)
+ /// </summary>
+ public TItem Dequeue()
+ {
+ lock(_queue)
+ {
+ if(_queue.Count <= 0)
+ {
+ throw new InvalidOperationException("Cannot call Dequeue() on an empty queue");
+ }
+
+ SimpleNode node =_queue.Dequeue();
+ RemoveFromNodeCache(node);
+ return node.Data;
+ }
+ }
+
+ /// <summary>
+ /// Enqueue the item with the given priority, without calling lock(_queue) or AddToNodeCache(node)
+ /// </summary>
+ /// <param name="item"></param>
+ /// <param name="priority"></param>
+ /// <returns></returns>
+ private SimpleNode EnqueueNoLockOrCache(TItem item, TPriority priority)
+ {
+ SimpleNode node = new SimpleNode(item);
+ if (_queue.Count == _queue.MaxSize)
+ {
+ _queue.Resize(_queue.MaxSize * 2 + 1);
+ }
+ _queue.Enqueue(node, priority);
+ return node;
+ }
+
+ /// <summary>
+ /// Enqueue a node to the priority queue. Lower values are placed in front. Ties are broken by first-in-first-out.
+ /// This queue automatically resizes itself, so there's no concern of the queue becoming 'full'.
+ /// Duplicates and null-values are allowed.
+ /// O(log n)
+ /// </summary>
+ public void Enqueue(TItem item, TPriority priority)
+ {
+ lock(_queue)
+ {
+ IList<SimpleNode> nodes;
+ if (item == null)
+ {
+ nodes = _nullNodesCache;
+ }
+ else if (!_itemToNodesCache.TryGetValue(item, out nodes))
+ {
+ nodes = new List<SimpleNode>();
+ _itemToNodesCache[item] = nodes;
+ }
+ SimpleNode node = EnqueueNoLockOrCache(item, priority);
+ nodes.Add(node);
+ }
+ }
+
+ /// <summary>
+ /// Enqueue a node to the priority queue if it doesn't already exist. Lower values are placed in front. Ties are broken by first-in-first-out.
+ /// This queue automatically resizes itself, so there's no concern of the queue becoming 'full'. Null values are allowed.
+ /// Returns true if the node was successfully enqueued; false if it already exists.
+ /// O(log n)
+ /// </summary>
+ public bool EnqueueWithoutDuplicates(TItem item, TPriority priority)
+ {
+ lock(_queue)
+ {
+ IList<SimpleNode> nodes;
+ if (item == null)
+ {
+ if (_nullNodesCache.Count > 0)
+ {
+ return false;
+ }
+ nodes = _nullNodesCache;
+ }
+ else if (_itemToNodesCache.ContainsKey(item))
+ {
+ return false;
+ }
+ else
+ {
+ nodes = new List<SimpleNode>();
+ _itemToNodesCache[item] = nodes;
+ }
+ SimpleNode node = EnqueueNoLockOrCache(item, priority);
+ nodes.Add(node);
+ return true;
+ }
+ }
+
+ /// <summary>
+ /// Removes an item from the queue. The item does not need to be the head of the queue.
+ /// If the item is not in the queue, an exception is thrown. If unsure, check Contains() first.
+ /// If multiple copies of the item are enqueued, only the first one is removed.
+ /// O(log n)
+ /// </summary>
+ public void Remove(TItem item)
+ {
+ lock(_queue)
+ {
+ SimpleNode removeMe;
+ IList<SimpleNode> nodes;
+ if (item == null)
+ {
+ if (_nullNodesCache.Count == 0)
+ {
+ throw new InvalidOperationException("Cannot call Remove() on a node which is not enqueued: " + item);
+ }
+ removeMe = _nullNodesCache[0];
+ nodes = _nullNodesCache;
+ }
+ else
+ {
+ if (!_itemToNodesCache.TryGetValue(item, out nodes))
+ {
+ throw new InvalidOperationException("Cannot call Remove() on a node which is not enqueued: " + item);
+ }
+ removeMe = nodes[0];
+ if (nodes.Count == 1)
+ {
+ _itemToNodesCache.Remove(item);
+ }
+ }
+ _queue.Remove(removeMe);
+ nodes.Remove(removeMe);
+ }
+ }
+
+ /// <summary>
+ /// Call this method to change the priority of an item.
+ /// Calling this method on a item not in the queue will throw an exception.
+ /// If the item is enqueued multiple times, only the first one will be updated.
+ /// (If your requirements are complex enough that you need to enqueue the same item multiple times <i>and</i> be able
+ /// to update all of them, please wrap your items in a wrapper class so they can be distinguished).
+ /// O(log n)
+ /// </summary>
+ public void UpdatePriority(TItem item, TPriority priority)
+ {
+ lock (_queue)
+ {
+ SimpleNode updateMe = GetExistingNode(item);
+ if (updateMe == null)
+ {
+ throw new InvalidOperationException("Cannot call UpdatePriority() on a node which is not enqueued: " + item);
+ }
+ _queue.UpdatePriority(updateMe, priority);
+ }
+ }
+
+ /// <summary>
+ /// Returns the priority of the given item.
+ /// Calling this method on a item not in the queue will throw an exception.
+ /// If the item is enqueued multiple times, only the priority of the first will be returned.
+ /// (If your requirements are complex enough that you need to enqueue the same item multiple times <i>and</i> be able
+ /// to query all their priorities, please wrap your items in a wrapper class so they can be distinguished).
+ /// O(1)
+ /// </summary>
+ public TPriority GetPriority(TItem item)
+ {
+ lock (_queue)
+ {
+ SimpleNode findMe = GetExistingNode(item);
+ if(findMe == null)
+ {
+ throw new InvalidOperationException("Cannot call GetPriority() on a node which is not enqueued: " + item);
+ }
+ return findMe.Priority;
+ }
+ }
+
+ #region Try* methods for multithreading
+ /// Get the head of the queue, without removing it (use TryDequeue() for that).
+ /// Useful for multi-threading, where the queue may become empty between calls to Contains() and First
+ /// Returns true if successful, false otherwise
+ /// O(1)
+ public bool TryFirst(out TItem first)
+ {
+ if (_queue.Count > 0)
+ {
+ lock (_queue)
+ {
+ if (_queue.Count > 0)
+ {
+ first = _queue.First.Data;
+ return true;
+ }
+ }
+ }
+
+ first = default(TItem);
+ return false;
+ }
+
+ /// <summary>
+ /// Removes the head of the queue (node with minimum priority; ties are broken by order of insertion), and sets it to first.
+ /// Useful for multi-threading, where the queue may become empty between calls to Contains() and Dequeue()
+ /// Returns true if successful; false if queue was empty
+ /// O(log n)
+ /// </summary>
+ public bool TryDequeue(out TItem first)
+ {
+ if (_queue.Count > 0)
+ {
+ lock (_queue)
+ {
+ if (_queue.Count > 0)
+ {
+ SimpleNode node = _queue.Dequeue();
+ first = node.Data;
+ RemoveFromNodeCache(node);
+ return true;
+ }
+ }
+ }
+
+ first = default(TItem);
+ return false;
+ }
+
+ /// <summary>
+ /// Attempts to remove an item from the queue. The item does not need to be the head of the queue.
+ /// Useful for multi-threading, where the queue may become empty between calls to Contains() and Remove()
+ /// Returns true if the item was successfully removed, false if it wasn't in the queue.
+ /// If multiple copies of the item are enqueued, only the first one is removed.
+ /// O(log n)
+ /// </summary>
+ public bool TryRemove(TItem item)
+ {
+ lock(_queue)
+ {
+ SimpleNode removeMe;
+ IList<SimpleNode> nodes;
+ if (item == null)
+ {
+ if (_nullNodesCache.Count == 0)
+ {
+ return false;
+ }
+ removeMe = _nullNodesCache[0];
+ nodes = _nullNodesCache;
+ }
+ else
+ {
+ if (!_itemToNodesCache.TryGetValue(item, out nodes))
+ {
+ return false;
+ }
+ removeMe = nodes[0];
+ if (nodes.Count == 1)
+ {
+ _itemToNodesCache.Remove(item);
+ }
+ }
+ _queue.Remove(removeMe);
+ nodes.Remove(removeMe);
+ return true;
+ }
+ }
+
+ /// <summary>
+ /// Call this method to change the priority of an item.
+ /// Useful for multi-threading, where the queue may become empty between calls to Contains() and UpdatePriority()
+ /// If the item is enqueued multiple times, only the first one will be updated.
+ /// (If your requirements are complex enough that you need to enqueue the same item multiple times <i>and</i> be able
+ /// to update all of them, please wrap your items in a wrapper class so they can be distinguished).
+ /// Returns true if the item priority was updated, false otherwise.
+ /// O(log n)
+ /// </summary>
+ public bool TryUpdatePriority(TItem item, TPriority priority)
+ {
+ lock(_queue)
+ {
+ SimpleNode updateMe = GetExistingNode(item);
+ if(updateMe == null)
+ {
+ return false;
+ }
+ _queue.UpdatePriority(updateMe, priority);
+ return true;
+ }
+ }
+
+ /// <summary>
+ /// Attempt to get the priority of the given item.
+ /// Useful for multi-threading, where the queue may become empty between calls to Contains() and GetPriority()
+ /// If the item is enqueued multiple times, only the priority of the first will be returned.
+ /// (If your requirements are complex enough that you need to enqueue the same item multiple times <i>and</i> be able
+ /// to query all their priorities, please wrap your items in a wrapper class so they can be distinguished).
+ /// Returns true if the item was found in the queue, false otherwise
+ /// O(1)
+ /// </summary>
+ public bool TryGetPriority(TItem item, out TPriority priority)
+ {
+ lock(_queue)
+ {
+ SimpleNode findMe = GetExistingNode(item);
+ if(findMe == null)
+ {
+ priority = default(TPriority);
+ return false;
+ }
+ priority = findMe.Priority;
+ return true;
+ }
+ }
+ #endregion
+
+ public IEnumerator<TItem> GetEnumerator()
+ {
+ List<TItem> queueData = new List<TItem>();
+ lock (_queue)
+ {
+ //Copy to a separate list because we don't want to 'yield return' inside a lock
+ foreach(var node in _queue)
+ {
+ queueData.Add(node.Data);
+ }
+ }
+
+ return queueData.GetEnumerator();
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+
+ public bool IsValidQueue()
+ {
+ lock(_queue)
+ {
+ // Check all items in cache are in the queue
+ foreach (IList<SimpleNode> nodes in _itemToNodesCache.Values)
+ {
+ foreach (SimpleNode node in nodes)
+ {
+ if (!_queue.Contains(node))
+ {
+ return false;
+ }
+ }
+ }
+
+ // Check all items in queue are in cache
+ foreach (SimpleNode node in _queue)
+ {
+ if (GetExistingNode(node.Data) == null)
+ {
+ return false;
+ }
+ }
+
+ // Check queue structure itself
+ return _queue.IsValidQueue();
+ }
+ }
+ }
+
+ /// <summary>
+ /// A simplified priority queue implementation. Is stable, auto-resizes, and thread-safe, at the cost of being slightly slower than
+ /// FastPriorityQueue
+ /// This class is kept here for backwards compatibility. It's recommended you use SimplePriorityQueue&lt;TItem, TPriority&gt;
+ /// </summary>
+ /// <typeparam name="TItem">The type to enqueue</typeparam>
+ public class SimplePriorityQueue<TItem> : SimplePriorityQueue<TItem, float>
+ {
+ /// <summary>
+ /// Instantiate a new Priority Queue
+ /// </summary>
+ public SimplePriorityQueue() { }
+
+ /// <summary>
+ /// Instantiate a new Priority Queue
+ /// </summary>
+ /// <param name="comparer">The comparer used to compare priority values. Defaults to Comparer&lt;float&gt;.default</param>
+ public SimplePriorityQueue(IComparer<float> comparer) : base(comparer) { }
+
+ /// <summary>
+ /// Instantiate a new Priority Queue
+ /// </summary>
+ /// <param name="comparer">The comparison function to use to compare priority values</param>
+ public SimplePriorityQueue(Comparison<float> comparer) : base(comparer) { }
+ }
+} \ No newline at end of file
diff --git a/Software/Visual_Studio/SideChains/Priority Queue/StablePriorityQueue.cs b/Software/Visual_Studio/SideChains/Priority Queue/StablePriorityQueue.cs
new file mode 100644
index 000000000..442b2dbcc
--- /dev/null
+++ b/Software/Visual_Studio/SideChains/Priority Queue/StablePriorityQueue.cs
@@ -0,0 +1,587 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Runtime.CompilerServices;
+
+namespace Priority_Queue
+{
+ /// <summary>
+ /// A copy of FastPriorityQueue which is also stable - that is, when two nodes are enqueued with the same priority, they
+ /// are always dequeued in the same order.
+ /// See https://github.com/BlueRaja/High-Speed-Priority-Queue-for-C-Sharp/wiki/Getting-Started for more information
+ /// </summary>
+ /// <typeparam name="T">The values in the queue. Must extend the StablePriorityQueueNode class</typeparam>
+ public sealed class StablePriorityQueue<T> : IFixedSizePriorityQueue<T, float>
+ where T : StablePriorityQueueNode
+ {
+ private int _numNodes;
+ private T[] _nodes;
+ private long _numNodesEverEnqueued;
+
+ /// <summary>
+ /// Instantiate a new Priority Queue
+ /// </summary>
+ /// <param name="maxNodes">The max nodes ever allowed to be enqueued (going over this will cause undefined behavior)</param>
+ public StablePriorityQueue(int maxNodes)
+ {
+ #if DEBUG
+ if (maxNodes <= 0)
+ {
+ throw new InvalidOperationException("New queue size cannot be smaller than 1");
+ }
+ #endif
+
+ _numNodes = 0;
+ _nodes = new T[maxNodes + 1];
+ _numNodesEverEnqueued = 0;
+ }
+
+ /// <summary>
+ /// Returns the number of nodes in the queue.
+ /// O(1)
+ /// </summary>
+ public int Count
+ {
+ get
+ {
+ return _numNodes;
+ }
+ }
+
+ /// <summary>
+ /// Returns the maximum number of items that can be enqueued at once in this queue. Once you hit this number (ie. once Count == MaxSize),
+ /// attempting to enqueue another item will cause undefined behavior. O(1)
+ /// </summary>
+ public int MaxSize
+ {
+ get
+ {
+ return _nodes.Length - 1;
+ }
+ }
+
+ /// <summary>
+ /// Removes every node from the queue.
+ /// O(n) (So, don't do this often!)
+ /// </summary>
+ #if NET_VERSION_4_5
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ #endif
+ public void Clear()
+ {
+ Array.Clear(_nodes, 1, _numNodes);
+ _numNodes = 0;
+ }
+
+ /// <summary>
+ /// Returns (in O(1)!) whether the given node is in the queue.
+ /// If node is or has been previously added to another queue, the result is undefined unless oldQueue.ResetNode(node) has been called
+ /// O(1)
+ /// </summary>
+ #if NET_VERSION_4_5
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ #endif
+ public bool Contains(T node)
+ {
+ #if DEBUG
+ if(node == null)
+ {
+ throw new ArgumentNullException("node");
+ }
+ if (node.Queue != null && !Equals(node.Queue))
+ {
+ throw new InvalidOperationException("node.Contains was called on a node from another queue. Please call originalQueue.ResetNode() first");
+ }
+ if (node.QueueIndex < 0 || node.QueueIndex >= _nodes.Length)
+ {
+ throw new InvalidOperationException("node.QueueIndex has been corrupted. Did you change it manually?");
+ }
+ #endif
+
+ return (_nodes[node.QueueIndex] == node);
+ }
+
+ /// <summary>
+ /// Enqueue a node to the priority queue. Lower values are placed in front. Ties are broken by first-in-first-out.
+ /// If the queue is full, the result is undefined.
+ /// If the node is already enqueued, the result is undefined.
+ /// If node is or has been previously added to another queue, the result is undefined unless oldQueue.ResetNode(node) has been called
+ /// O(log n)
+ /// </summary>
+ #if NET_VERSION_4_5
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ #endif
+ public void Enqueue(T node, float priority)
+ {
+ #if DEBUG
+ if(node == null)
+ {
+ throw new ArgumentNullException("node");
+ }
+ if(_numNodes >= _nodes.Length - 1)
+ {
+ throw new InvalidOperationException("Queue is full - node cannot be added: " + node);
+ }
+ if (node.Queue != null && !Equals(node.Queue))
+ {
+ throw new InvalidOperationException("node.Enqueue was called on a node from another queue. Please call originalQueue.ResetNode() first");
+ }
+ if (Contains(node))
+ {
+ throw new InvalidOperationException("Node is already enqueued: " + node);
+ }
+ node.Queue = this;
+ #endif
+
+ node.Priority = priority;
+ _numNodes++;
+ _nodes[_numNodes] = node;
+ node.QueueIndex = _numNodes;
+ node.InsertionIndex = _numNodesEverEnqueued++;
+ CascadeUp(node);
+ }
+
+ //Performance appears to be slightly better when this is NOT inlined o_O
+ #if NET_VERSION_4_5
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ #endif
+ private void CascadeUp(T node)
+ {
+ //aka Heapify-up
+ int parent;
+ if(node.QueueIndex > 1)
+ {
+ parent = node.QueueIndex >> 1;
+ T parentNode = _nodes[parent];
+ if(HasHigherPriority(parentNode, node))
+ return;
+
+ //Node has lower priority value, so move parent down the heap to make room
+ _nodes[node.QueueIndex] = parentNode;
+ parentNode.QueueIndex = node.QueueIndex;
+
+ node.QueueIndex = parent;
+ }
+ else
+ {
+ return;
+ }
+ while(parent > 1)
+ {
+ parent >>= 1;
+ T parentNode = _nodes[parent];
+ if(HasHigherPriority(parentNode, node))
+ break;
+
+ //Node has lower priority value, so move parent down the heap to make room
+ _nodes[node.QueueIndex] = parentNode;
+ parentNode.QueueIndex = node.QueueIndex;
+
+ node.QueueIndex = parent;
+ }
+ _nodes[node.QueueIndex] = node;
+ }
+
+#if NET_VERSION_4_5
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
+ private void CascadeDown(T node)
+ {
+ //aka Heapify-down
+ int finalQueueIndex = node.QueueIndex;
+ int childLeftIndex = 2 * finalQueueIndex;
+
+ // If leaf node, we're done
+ if(childLeftIndex > _numNodes)
+ {
+ return;
+ }
+
+ // Check if the left-child is higher-priority than the current node
+ int childRightIndex = childLeftIndex + 1;
+ T childLeft = _nodes[childLeftIndex];
+ if(HasHigherPriority(childLeft, node))
+ {
+ // Check if there is a right child. If not, swap and finish.
+ if(childRightIndex > _numNodes)
+ {
+ node.QueueIndex = childLeftIndex;
+ childLeft.QueueIndex = finalQueueIndex;
+ _nodes[finalQueueIndex] = childLeft;
+ _nodes[childLeftIndex] = node;
+ return;
+ }
+ // Check if the left-child is higher-priority than the right-child
+ T childRight = _nodes[childRightIndex];
+ if(HasHigherPriority(childLeft, childRight))
+ {
+ // left is highest, move it up and continue
+ childLeft.QueueIndex = finalQueueIndex;
+ _nodes[finalQueueIndex] = childLeft;
+ finalQueueIndex = childLeftIndex;
+ }
+ else
+ {
+ // right is even higher, move it up and continue
+ childRight.QueueIndex = finalQueueIndex;
+ _nodes[finalQueueIndex] = childRight;
+ finalQueueIndex = childRightIndex;
+ }
+ }
+ // Not swapping with left-child, does right-child exist?
+ else if(childRightIndex > _numNodes)
+ {
+ return;
+ }
+ else
+ {
+ // Check if the right-child is higher-priority than the current node
+ T childRight = _nodes[childRightIndex];
+ if(HasHigherPriority(childRight, node))
+ {
+ childRight.QueueIndex = finalQueueIndex;
+ _nodes[finalQueueIndex] = childRight;
+ finalQueueIndex = childRightIndex;
+ }
+ // Neither child is higher-priority than current, so finish and stop.
+ else
+ {
+ return;
+ }
+ }
+
+ while(true)
+ {
+ childLeftIndex = 2 * finalQueueIndex;
+
+ // If leaf node, we're done
+ if(childLeftIndex > _numNodes)
+ {
+ node.QueueIndex = finalQueueIndex;
+ _nodes[finalQueueIndex] = node;
+ break;
+ }
+
+ // Check if the left-child is higher-priority than the current node
+ childRightIndex = childLeftIndex + 1;
+ childLeft = _nodes[childLeftIndex];
+ if(HasHigherPriority(childLeft, node))
+ {
+ // Check if there is a right child. If not, swap and finish.
+ if(childRightIndex > _numNodes)
+ {
+ node.QueueIndex = childLeftIndex;
+ childLeft.QueueIndex = finalQueueIndex;
+ _nodes[finalQueueIndex] = childLeft;
+ _nodes[childLeftIndex] = node;
+ break;
+ }
+ // Check if the left-child is higher-priority than the right-child
+ T childRight = _nodes[childRightIndex];
+ if(HasHigherPriority(childLeft, childRight))
+ {
+ // left is highest, move it up and continue
+ childLeft.QueueIndex = finalQueueIndex;
+ _nodes[finalQueueIndex] = childLeft;
+ finalQueueIndex = childLeftIndex;
+ }
+ else
+ {
+ // right is even higher, move it up and continue
+ childRight.QueueIndex = finalQueueIndex;
+ _nodes[finalQueueIndex] = childRight;
+ finalQueueIndex = childRightIndex;
+ }
+ }
+ // Not swapping with left-child, does right-child exist?
+ else if(childRightIndex > _numNodes)
+ {
+ node.QueueIndex = finalQueueIndex;
+ _nodes[finalQueueIndex] = node;
+ break;
+ }
+ else
+ {
+ // Check if the right-child is higher-priority than the current node
+ T childRight = _nodes[childRightIndex];
+ if(HasHigherPriority(childRight, node))
+ {
+ childRight.QueueIndex = finalQueueIndex;
+ _nodes[finalQueueIndex] = childRight;
+ finalQueueIndex = childRightIndex;
+ }
+ // Neither child is higher-priority than current, so finish and stop.
+ else
+ {
+ node.QueueIndex = finalQueueIndex;
+ _nodes[finalQueueIndex] = node;
+ break;
+ }
+ }
+ }
+ }
+
+ /// <summary>
+ /// Returns true if 'higher' has higher priority than 'lower', false otherwise.
+ /// Note that calling HasHigherPriority(node, node) (ie. both arguments the same node) will return false
+ /// </summary>
+#if NET_VERSION_4_5
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ #endif
+ private bool HasHigherPriority(T higher, T lower)
+ {
+ return (higher.Priority < lower.Priority ||
+ (higher.Priority == lower.Priority && higher.InsertionIndex < lower.InsertionIndex));
+ }
+
+ /// <summary>
+ /// Removes the head of the queue (node with minimum priority; ties are broken by order of insertion), and returns it.
+ /// If queue is empty, result is undefined
+ /// O(log n)
+ /// </summary>
+#if NET_VERSION_4_5
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
+ public T Dequeue()
+ {
+ #if DEBUG
+ if(_numNodes <= 0)
+ {
+ throw new InvalidOperationException("Cannot call Dequeue() on an empty queue");
+ }
+
+ if(!IsValidQueue())
+ {
+ throw new InvalidOperationException("Queue has been corrupted (Did you update a node priority manually instead of calling UpdatePriority()?" +
+ "Or add the same node to two different queues?)");
+ }
+ #endif
+
+ T returnMe = _nodes[1];
+ //If the node is already the last node, we can remove it immediately
+ if(_numNodes == 1)
+ {
+ _nodes[1] = null;
+ _numNodes = 0;
+ return returnMe;
+ }
+
+ //Swap the node with the last node
+ T formerLastNode = _nodes[_numNodes];
+ _nodes[1] = formerLastNode;
+ formerLastNode.QueueIndex = 1;
+ _nodes[_numNodes] = null;
+ _numNodes--;
+
+ //Now bubble formerLastNode (which is no longer the last node) down
+ CascadeDown(formerLastNode);
+ return returnMe;
+ }
+
+ /// <summary>
+ /// Resize the queue so it can accept more nodes. All currently enqueued nodes are remain.
+ /// Attempting to decrease the queue size to a size too small to hold the existing nodes results in undefined behavior
+ /// O(n)
+ /// </summary>
+ public void Resize(int maxNodes)
+ {
+ #if DEBUG
+ if (maxNodes <= 0)
+ {
+ throw new InvalidOperationException("Queue size cannot be smaller than 1");
+ }
+
+ if (maxNodes < _numNodes)
+ {
+ throw new InvalidOperationException("Called Resize(" + maxNodes + "), but current queue contains " + _numNodes + " nodes");
+ }
+ #endif
+
+ T[] newArray = new T[maxNodes + 1];
+ int highestIndexToCopy = Math.Min(maxNodes, _numNodes);
+ Array.Copy(_nodes, newArray, highestIndexToCopy + 1);
+ _nodes = newArray;
+ }
+
+ /// <summary>
+ /// Returns the head of the queue, without removing it (use Dequeue() for that).
+ /// If the queue is empty, behavior is undefined.
+ /// O(1)
+ /// </summary>
+ public T First
+ {
+ get
+ {
+ #if DEBUG
+ if(_numNodes <= 0)
+ {
+ throw new InvalidOperationException("Cannot call .First on an empty queue");
+ }
+ #endif
+
+ return _nodes[1];
+ }
+ }
+
+ /// <summary>
+ /// This method must be called on a node every time its priority changes while it is in the queue.
+ /// <b>Forgetting to call this method will result in a corrupted queue!</b>
+ /// Calling this method on a node not in the queue results in undefined behavior
+ /// O(log n)
+ /// </summary>
+ #if NET_VERSION_4_5
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ #endif
+ public void UpdatePriority(T node, float priority)
+ {
+ #if DEBUG
+ if(node == null)
+ {
+ throw new ArgumentNullException("node");
+ }
+ if (node.Queue != null && !Equals(node.Queue))
+ {
+ throw new InvalidOperationException("node.UpdatePriority was called on a node from another queue");
+ }
+ if (!Contains(node))
+ {
+ throw new InvalidOperationException("Cannot call UpdatePriority() on a node which is not enqueued: " + node);
+ }
+ #endif
+
+ node.Priority = priority;
+ OnNodeUpdated(node);
+ }
+
+#if NET_VERSION_4_5
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
+ private void OnNodeUpdated(T node)
+ {
+ //Bubble the updated node up or down as appropriate
+ int parentIndex = node.QueueIndex >> 1;
+
+ if(parentIndex > 0 && HasHigherPriority(node, _nodes[parentIndex]))
+ {
+ CascadeUp(node);
+ }
+ else
+ {
+ //Note that CascadeDown will be called if parentNode == node (that is, node is the root)
+ CascadeDown(node);
+ }
+ }
+
+ /// <summary>
+ /// Removes a node from the queue. The node does not need to be the head of the queue.
+ /// If the node is not in the queue, the result is undefined. If unsure, check Contains() first
+ /// O(log n)
+ /// </summary>
+#if NET_VERSION_4_5
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
+ public void Remove(T node)
+ {
+#if DEBUG
+ if(node == null)
+ {
+ throw new ArgumentNullException("node");
+ }
+ if (node.Queue != null && !Equals(node.Queue))
+ {
+ throw new InvalidOperationException("node.Remove was called on a node from another queue");
+ }
+ if (!Contains(node))
+ {
+ throw new InvalidOperationException("Cannot call Remove() on a node which is not enqueued: " + node);
+ }
+#endif
+
+ //If the node is already the last node, we can remove it immediately
+ if(node.QueueIndex == _numNodes)
+ {
+ _nodes[_numNodes] = null;
+ _numNodes--;
+ return;
+ }
+
+ //Swap the node with the last node
+ T formerLastNode = _nodes[_numNodes];
+ _nodes[node.QueueIndex] = formerLastNode;
+ formerLastNode.QueueIndex = node.QueueIndex;
+ _nodes[_numNodes] = null;
+ _numNodes--;
+
+ //Now bubble formerLastNode (which is no longer the last node) up or down as appropriate
+ OnNodeUpdated(formerLastNode);
+ }
+
+ /// <summary>
+ /// By default, nodes that have been previously added to one queue cannot be added to another queue.
+ /// If you need to do this, please call originalQueue.ResetNode(node) before attempting to add it in the new queue
+ /// </summary>
+#if NET_VERSION_4_5
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
+ public void ResetNode(T node)
+ {
+#if DEBUG
+ if (node == null)
+ {
+ throw new ArgumentNullException("node");
+ }
+ if (node.Queue != null && !Equals(node.Queue))
+ {
+ throw new InvalidOperationException("node.ResetNode was called on a node from another queue");
+ }
+ if (Contains(node))
+ {
+ throw new InvalidOperationException("node.ResetNode was called on a node that is still in the queue");
+ }
+
+ node.Queue = null;
+#endif
+
+ node.QueueIndex = 0;
+ }
+
+
+ public IEnumerator<T> GetEnumerator()
+ {
+#if NET_VERSION_4_5 // ArraySegment does not implement IEnumerable before 4.5
+ IEnumerable<T> e = new ArraySegment<T>(_nodes, 1, _numNodes);
+ return e.GetEnumerator();
+#else
+ for(int i = 1; i <= _numNodes; i++)
+ yield return _nodes[i];
+#endif
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+
+ /// <summary>
+ /// <b>Should not be called in production code.</b>
+ /// Checks to make sure the queue is still in a valid state. Used for testing/debugging the queue.
+ /// </summary>
+ public bool IsValidQueue()
+ {
+ for(int i = 1; i < _nodes.Length; i++)
+ {
+ if(_nodes[i] != null)
+ {
+ int childLeftIndex = 2 * i;
+ if(childLeftIndex < _nodes.Length && _nodes[childLeftIndex] != null && HasHigherPriority(_nodes[childLeftIndex], _nodes[i]))
+ return false;
+
+ int childRightIndex = childLeftIndex + 1;
+ if(childRightIndex < _nodes.Length && _nodes[childRightIndex] != null && HasHigherPriority(_nodes[childRightIndex], _nodes[i]))
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+} \ No newline at end of file
diff --git a/Software/Visual_Studio/SideChains/Priority Queue/StablePriorityQueueNode.cs b/Software/Visual_Studio/SideChains/Priority Queue/StablePriorityQueueNode.cs
new file mode 100644
index 000000000..c794aa0d0
--- /dev/null
+++ b/Software/Visual_Studio/SideChains/Priority Queue/StablePriorityQueueNode.cs
@@ -0,0 +1,10 @@
+namespace Priority_Queue
+{
+ public class StablePriorityQueueNode : FastPriorityQueueNode
+ {
+ /// <summary>
+ /// Represents the order the node was inserted in
+ /// </summary>
+ public long InsertionIndex { get; internal set; }
+ }
+}
diff --git a/Software/Visual_Studio/SideChains/RealTimeGraphX-master/RealTimeGraphX/DataPoints/DateTimeDataPoint.cs b/Software/Visual_Studio/SideChains/RealTimeGraphX-master/RealTimeGraphX/DataPoints/DateTimeDataPoint.cs
new file mode 100644
index 000000000..9ec750af9
--- /dev/null
+++ b/Software/Visual_Studio/SideChains/RealTimeGraphX-master/RealTimeGraphX/DataPoints/DateTimeDataPoint.cs
@@ -0,0 +1,178 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace RealTimeGraphX.DataPoints
+{
+ public class DateTimeDataPoint : GraphDataPoint<DateTime, DateTimeDataPoint>
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DateTimeDataPoint"/> class.
+ /// </summary>
+ public DateTimeDataPoint() : base()
+ {
+
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DateTimeDataPoint"/> class.
+ /// </summary>
+ /// <param name="value">The value.</param>
+ public DateTimeDataPoint(DateTime value) : base(value)
+ {
+
+ }
+
+ /// <summary>
+ /// Performs an implicit conversion from <see cref="System.TimeSpan"/> to <see cref="DateTimeDataPoint"/>.
+ /// </summary>
+ /// <param name="value">The value.</param>
+ /// <returns>
+ /// The result of the conversion.
+ /// </returns>
+ public static implicit operator DateTimeDataPoint(DateTime value)
+ {
+ return new DateTimeDataPoint(value);
+ }
+
+ /// <summary>
+ /// Implements the operator -.
+ /// </summary>
+ /// <param name="a">a.</param>
+ /// <param name="b">The b.</param>
+ /// <returns>
+ /// The result of the operator.
+ /// </returns>
+ public static DateTimeDataPoint operator -(DateTimeDataPoint a, DateTimeDataPoint b)
+ {
+ return new DateTimeDataPoint(new DateTime(a.Value.Ticks - b.Value.Ticks));
+ }
+
+ /// <summary>
+ /// Implements the operator +.
+ /// </summary>
+ /// <param name="a">a.</param>
+ /// <param name="b">The b.</param>
+ /// <returns>
+ /// The result of the operator.
+ /// </returns>
+ public static DateTimeDataPoint operator +(DateTimeDataPoint a, DateTimeDataPoint b)
+ {
+ return new DateTimeDataPoint(new DateTime(a.Value.Ticks + b.Value.Ticks));
+ }
+
+ /// <summary>
+ /// Sums the value of this instance with another instance value and returns the result.
+ /// </summary>
+ /// <param name="other">The other instance.</param>
+ /// <returns></returns>
+ public override IGraphDataPoint Add(IGraphDataPoint other)
+ {
+ return new DateTimeDataPoint(new DateTime(this.Value.Ticks + (other as DateTimeDataPoint).Value.Ticks));
+ }
+
+ /// <summary>
+ /// Subtract the value of another instance from this instance and returns the result.
+ /// </summary>
+ /// <param name="other">The other instance.</param>
+ /// <returns></returns>
+ public override IGraphDataPoint Subtract(IGraphDataPoint other)
+ {
+ return new DateTimeDataPoint(new DateTime(this.Value.Ticks - (other as DateTimeDataPoint).Value.Ticks));
+ }
+
+ /// <summary>
+ /// Multiplies the value of this instance with another instance value and returns the result.
+ /// </summary>
+ /// <param name="other">The other instance.</param>
+ /// <returns></returns>
+ public override IGraphDataPoint Multiply(IGraphDataPoint other)
+ {
+ return new DateTimeDataPoint(new DateTime(this.Value.Ticks * (other as DateTimeDataPoint).Value.Ticks));
+ }
+
+ /// <summary>
+ /// Divides the value of this instance with another instance value and returns the result.
+ /// </summary>
+ /// <param name="other">The other instance.</param>
+ /// <returns></returns>
+ public override IGraphDataPoint Divide(IGraphDataPoint other)
+ {
+ return new DateTimeDataPoint(new DateTime(this.Value.Ticks / (other as DateTimeDataPoint).Value.Ticks));
+ }
+
+ /// <summary>
+ /// Returns the percentage value of this instance between the specified minimum and maximum values.
+ /// </summary>
+ /// <param name="min">The minimum.</param>
+ /// <param name="max">The maximum.</param>
+ /// <returns></returns>
+ public override double ComputeRelativePosition(IGraphDataPoint min, IGraphDataPoint max)
+ {
+ DateTime dMin = min as DateTimeDataPoint;
+ DateTime dMax = max as DateTimeDataPoint;
+
+ if (dMax.Ticks - dMin.Ticks == 0) //Prevent divide by zero
+ {
+ return dMin.Ticks;
+ }
+
+ var result = ((Value.Ticks - dMin.Ticks) * 100) / (dMax.Ticks - dMin.Ticks);
+
+ return double.IsNaN(result) ? dMin.Ticks : result;
+ }
+
+ /// <summary>
+ /// Returns the absolute value of the specified percentage value between the specified minimum and maximum values.
+ /// </summary>
+ /// <param name="min">The minimum.</param>
+ /// <param name="max">The maximum.</param>
+ /// <param name="percentage">The percentage.</param>
+ /// <returns></returns>
+ public override IGraphDataPoint ComputeAbsolutePosition(IGraphDataPoint min, IGraphDataPoint max, double percentage)
+ {
+ double minimum = ((DateTime)min.GetValue()).Ticks;
+ double maximum = ((DateTime)max.GetValue()).Ticks;
+
+ return new DateTimeDataPoint(new DateTime((long)(minimum + (maximum - minimum) * percentage)));
+ }
+
+ /// <summary>
+ /// Creates a range of values from the specified minimum and maximum.
+ /// </summary>
+ /// <param name="min">The minimum.</param>
+ /// <param name="max">The maximum.</param>
+ /// <param name="count">The count.</param>
+ /// <returns></returns>
+ public override IEnumerable<IGraphDataPoint> CreateRange(IGraphDataPoint min, IGraphDataPoint max, int count)
+ {
+ double minimum = ((DateTime)min.GetValue()).Ticks;
+ double maximum = ((DateTime)max.GetValue()).Ticks;
+
+ return Enumerable.Range(0, count).
+ Select(i => minimum + (maximum - minimum) * ((double)i / (count - 1))).
+ Select(x => new DateTimeDataPoint(new DateTime((long)x)));
+ }
+
+ /// <summary>
+ /// Returns a formated string of this data point.
+ /// </summary>
+ /// <param name="format">The format.</param>
+ /// <returns></returns>
+ public override string ToString(string format)
+ {
+ return Value.ToString(format);
+ }
+
+ /// <summary>
+ /// Parses the specified value and returns a new instance of <see cref="!:TDataType" /> data point.
+ /// </summary>
+ /// <param name="value">The value.</param>
+ /// <returns></returns>
+ public override IGraphDataPoint Parse(string value)
+ {
+ return new DateTimeDataPoint(DateTime.Parse(value));
+ }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.Console/Network/ConsoleCommandRequest.cs b/Software/Visual_Studio/Tango.Console/Network/ConsoleCommandRequest.cs
new file mode 100644
index 000000000..d7541506c
--- /dev/null
+++ b/Software/Visual_Studio/Tango.Console/Network/ConsoleCommandRequest.cs
@@ -0,0 +1,15 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tango.Console.Network
+{
+ public class ConsoleCommandRequest
+ {
+ public String WorkingFolder { get; set; }
+ public String Command { get; set; }
+ public bool RunAsAdministrator { get; set; }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.Console/Network/ConsoleCommandResponse.cs b/Software/Visual_Studio/Tango.Console/Network/ConsoleCommandResponse.cs
new file mode 100644
index 000000000..70b72e703
--- /dev/null
+++ b/Software/Visual_Studio/Tango.Console/Network/ConsoleCommandResponse.cs
@@ -0,0 +1,20 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tango.Console.Network
+{
+ public class ConsoleCommandResponse
+ {
+ public String WorkingFolder { get; set; }
+ public String Output { get; set; }
+ public List<ConsoleSuggestion> Suggestions { get; set; }
+
+ public ConsoleCommandResponse()
+ {
+ Suggestions = new List<ConsoleSuggestion>();
+ }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.Console/Network/GetCurrentDirectoryRequest.cs b/Software/Visual_Studio/Tango.Console/Network/GetCurrentDirectoryRequest.cs
new file mode 100644
index 000000000..f5abfda70
--- /dev/null
+++ b/Software/Visual_Studio/Tango.Console/Network/GetCurrentDirectoryRequest.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tango.Console.Network
+{
+ public class GetCurrentDirectoryRequest
+ {
+
+ }
+}
diff --git a/Software/Visual_Studio/Tango.Console/Network/GetCurrentDirectoryResponse.cs b/Software/Visual_Studio/Tango.Console/Network/GetCurrentDirectoryResponse.cs
new file mode 100644
index 000000000..7b6eedda0
--- /dev/null
+++ b/Software/Visual_Studio/Tango.Console/Network/GetCurrentDirectoryResponse.cs
@@ -0,0 +1,19 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tango.Console.Network
+{
+ public class GetCurrentDirectoryResponse
+ {
+ public String CurrentDirectory { get; set; }
+ public List<ConsoleSuggestion> Suggestions { get; set; }
+
+ public GetCurrentDirectoryResponse()
+ {
+ Suggestions = new List<ConsoleSuggestion>();
+ }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.Core/ExtensionMethods/BooleanExtensions.cs b/Software/Visual_Studio/Tango.Core/ExtensionMethods/BooleanExtensions.cs
new file mode 100644
index 000000000..02464dc8e
--- /dev/null
+++ b/Software/Visual_Studio/Tango.Core/ExtensionMethods/BooleanExtensions.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+public static class BooleanExtensions
+{
+ public static String ToStringYesNo(this Boolean value)
+ {
+ return value ? "Yes" : "No";
+ }
+}
+
diff --git a/Software/Visual_Studio/Tango.Core/PriorityProducerConsumerQueue.cs b/Software/Visual_Studio/Tango.Core/PriorityProducerConsumerQueue.cs
new file mode 100644
index 000000000..dcc71a4ef
--- /dev/null
+++ b/Software/Visual_Studio/Tango.Core/PriorityProducerConsumerQueue.cs
@@ -0,0 +1,63 @@
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tango.Core
+{
+ public enum QueuePriority
+ {
+ Standard,
+ High,
+ Low
+ }
+
+ public class PriorityProducerConsumerQueue<T>
+ {
+ private ProducerConsumerQueue<T> _lowPriorityCollection;
+ private ProducerConsumerQueue<T> _standardPriorityCollection;
+ private ProducerConsumerQueue<T> _highPriorityCollection;
+ private ProducerConsumerQueue<T>[] _collections;
+
+ public PriorityProducerConsumerQueue()
+ {
+ _lowPriorityCollection = new ProducerConsumerQueue<T>();
+ _standardPriorityCollection = new ProducerConsumerQueue<T>();
+ _highPriorityCollection = new ProducerConsumerQueue<T>();
+ _collections = new ProducerConsumerQueue<T>[] { _highPriorityCollection, _standardPriorityCollection, _lowPriorityCollection };
+ }
+
+ /// <summary>
+ /// Enqueues the specified item.
+ /// </summary>
+ /// <param name="item">The item.</param>
+ public void BlockEnqueue(T item, QueuePriority priority = QueuePriority.Standard)
+ {
+ switch (priority)
+ {
+ case QueuePriority.Low:
+ _lowPriorityCollection.Add(item);
+ break;
+ case QueuePriority.Standard:
+ _standardPriorityCollection.Add(item);
+ break;
+ case QueuePriority.High:
+ _highPriorityCollection.Add(item);
+ break;
+ }
+ }
+
+ /// <summary>
+ /// Blocks until an item is available for dequeuing.
+ /// </summary>
+ /// <returns></returns>
+ public T BlockDequeue()
+ {
+ T item;
+ int index = BlockingCollection<T>.TakeFromAny(_collections, out item);
+ return item;
+ }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.FileSystem/DragItem.cs b/Software/Visual_Studio/Tango.FileSystem/DragItem.cs
new file mode 100644
index 000000000..e71b095e1
--- /dev/null
+++ b/Software/Visual_Studio/Tango.FileSystem/DragItem.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tango.FileSystem
+{
+ public class DragItem
+ {
+ public FileSystemItem FileSystemItem { get; set; }
+ public String Destination { get; set; }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.FileSystem/DriveItem.cs b/Software/Visual_Studio/Tango.FileSystem/DriveItem.cs
new file mode 100644
index 000000000..9c4494fec
--- /dev/null
+++ b/Software/Visual_Studio/Tango.FileSystem/DriveItem.cs
@@ -0,0 +1,38 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tango.FileSystem
+{
+ public class DriveItem : FileSystemItem, IFileSystemContainer
+ {
+ public ObservableCollection<FileSystemItem> Items { get; set; }
+
+ public String Label { get; set; }
+
+ public DriveType DriveType { get; set; }
+
+ public DriveItem()
+ {
+ Type = FileSystemItemType.Drive;
+ Items = new ObservableCollection<FileSystemItem>();
+ }
+
+ protected override string OnGetName()
+ {
+ return Label;
+ }
+
+ public override string Description
+ {
+ get
+ {
+ return DriveType.ToString().ToTitle();
+ }
+ }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.FileSystem/FileExplorerControl.cs b/Software/Visual_Studio/Tango.FileSystem/FileExplorerControl.cs
new file mode 100644
index 000000000..23cac7733
--- /dev/null
+++ b/Software/Visual_Studio/Tango.FileSystem/FileExplorerControl.cs
@@ -0,0 +1,423 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Navigation;
+using System.Windows.Shapes;
+using Tango.Core.IO;
+
+namespace Tango.FileSystem
+{
+ public class FileExplorerControl : Control
+ {
+ private ListBox _listBox;
+ private DataGrid _datagrid;
+ private bool _preventSynchronization;
+ private Point _dragOutStartPoint;
+ private bool _isMouseDown;
+ private List<FileSystemItem> _selectedItemsBeforeDrag;
+
+ public IFileSystemContainer CurrentItem
+ {
+ get { return (IFileSystemContainer)GetValue(CurrentItemProperty); }
+ set { SetValue(CurrentItemProperty, value); }
+ }
+ public static readonly DependencyProperty CurrentItemProperty =
+ DependencyProperty.Register("CurrentItem", typeof(IFileSystemContainer), typeof(FileExplorerControl), new PropertyMetadata(null));
+
+ public FileSystemItem SelectedItem
+ {
+ get { return (FileSystemItem)GetValue(SelectedItemProperty); }
+ set { SetValue(SelectedItemProperty, value); }
+ }
+ public static readonly DependencyProperty SelectedItemProperty =
+ DependencyProperty.Register("SelectedItem", typeof(FileSystemItem), typeof(FileExplorerControl), new PropertyMetadata(null));
+
+ public ICommand ItemDoubleClickedCommand
+ {
+ get { return (ICommand)GetValue(ItemDoubleClickedCommandProperty); }
+ set { SetValue(ItemDoubleClickedCommandProperty, value); }
+ }
+ public static readonly DependencyProperty ItemDoubleClickedCommandProperty =
+ DependencyProperty.Register("ItemDoubleClickedCommand", typeof(ICommand), typeof(FileExplorerControl), new PropertyMetadata(null));
+
+ public ICommand DeleteCommand
+ {
+ get { return (ICommand)GetValue(DeleteCommandProperty); }
+ set { SetValue(DeleteCommandProperty, value); }
+ }
+ public static readonly DependencyProperty DeleteCommandProperty =
+ DependencyProperty.Register("DeleteCommand", typeof(ICommand), typeof(FileExplorerControl), new PropertyMetadata(null));
+
+ public ICommand DropCommand
+ {
+ get { return (ICommand)GetValue(DropCommandProperty); }
+ set { SetValue(DropCommandProperty, value); }
+ }
+ public static readonly DependencyProperty DropCommandProperty =
+ DependencyProperty.Register("DropCommand", typeof(ICommand), typeof(FileExplorerControl), new PropertyMetadata(null));
+
+ public ICommand DragCommand
+ {
+ get { return (ICommand)GetValue(DragCommandProperty); }
+ set { SetValue(DragCommandProperty, value); }
+ }
+ public static readonly DependencyProperty DragCommandProperty =
+ DependencyProperty.Register("DragCommand", typeof(ICommand), typeof(FileExplorerControl), new PropertyMetadata(null));
+
+ public ImageSource DriveIcon
+ {
+ get { return (ImageSource)GetValue(DriveIconProperty); }
+ set { SetValue(DriveIconProperty, value); }
+ }
+ public static readonly DependencyProperty DriveIconProperty =
+ DependencyProperty.Register("DriveIcon", typeof(ImageSource), typeof(FileExplorerControl), new PropertyMetadata(null));
+
+ public ImageSource FolderIcon
+ {
+ get { return (ImageSource)GetValue(FolderIconProperty); }
+ set { SetValue(FolderIconProperty, value); }
+ }
+ public static readonly DependencyProperty FolderIconProperty =
+ DependencyProperty.Register("FolderIcon", typeof(ImageSource), typeof(FileExplorerControl), new PropertyMetadata(null));
+
+ public FileExplorerControlMode Mode
+ {
+ get { return (FileExplorerControlMode)GetValue(ModeProperty); }
+ set { SetValue(ModeProperty, value); }
+ }
+ public static readonly DependencyProperty ModeProperty =
+ DependencyProperty.Register("Mode", typeof(FileExplorerControlMode), typeof(FileExplorerControl), new PropertyMetadata(FileExplorerControlMode.Large));
+
+ public ObservableCollection<FileSystemItem> SelectedItems
+ {
+ get { return (ObservableCollection<FileSystemItem>)GetValue(SelectedItemsProperty); }
+ set { SetValue(SelectedItemsProperty, value); }
+ }
+ public static readonly DependencyProperty SelectedItemsProperty =
+ DependencyProperty.Register("SelectedItems", typeof(ObservableCollection<FileSystemItem>), typeof(FileExplorerControl), new PropertyMetadata(null, (d, e) => (d as FileExplorerControl).OnSelectedItemsChanged()));
+
+ public bool AllowDrag
+ {
+ get { return (bool)GetValue(AllowDragProperty); }
+ set { SetValue(AllowDragProperty, value); }
+ }
+ public static readonly DependencyProperty AllowDragProperty =
+ DependencyProperty.Register("AllowDrag", typeof(bool), typeof(FileExplorerControl), new PropertyMetadata(null));
+
+ static FileExplorerControl()
+ {
+ DefaultStyleKeyProperty.OverrideMetadata(typeof(FileExplorerControl), new FrameworkPropertyMetadata(typeof(FileExplorerControl)));
+ }
+
+ public FileExplorerControl()
+ {
+ _selectedItemsBeforeDrag = new List<FileSystemItem>();
+ }
+
+ public override void OnApplyTemplate()
+ {
+ base.OnApplyTemplate();
+
+ _listBox = GetTemplateChild("PART_listbox") as ListBox;
+ _datagrid = GetTemplateChild("PART_datagrid") as DataGrid;
+
+ _listBox.SelectionChanged += _listBox_SelectionChanged;
+ _datagrid.SelectionChanged += _datagrid_SelectionChanged;
+ }
+
+ protected override void OnPreviewKeyUp(KeyEventArgs e)
+ {
+ base.OnPreviewKeyUp(e);
+
+ if (e.Key == Key.Delete)
+ {
+ if (SelectedItems != null && SelectedItems.Count > 0)
+ {
+ DeleteCommand?.Execute(SelectedItems);
+ }
+ }
+ }
+
+ private void _datagrid_SelectionChanged(object sender, SelectionChangedEventArgs e)
+ {
+ if (!_preventSynchronization)
+ {
+ _preventSynchronization = true;
+
+ _listBox.SelectedItems.Clear();
+
+ foreach (var item in _datagrid.SelectedItems)
+ {
+ _listBox.SelectedItems.Add(item);
+ }
+
+ SynchronizeSelectedItems(_listBox.SelectedItems);
+
+ _preventSynchronization = false;
+ }
+ }
+
+ private void _listBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
+ {
+ if (!_preventSynchronization)
+ {
+ _preventSynchronization = true;
+
+ _datagrid.SelectedItems.Clear();
+
+ foreach (var item in _listBox.SelectedItems)
+ {
+ _datagrid.SelectedItems.Add(item);
+ }
+
+ SynchronizeSelectedItems(_listBox.SelectedItems);
+
+ _preventSynchronization = false;
+ }
+ }
+
+ private void SynchronizeSelectedItems(IList items)
+ {
+ if (SelectedItems == null)
+ {
+ SelectedItems = items.Cast<FileSystemItem>().ToObservableCollection();
+ }
+ else
+ {
+ SelectedItems.Clear();
+
+ foreach (var item in items.Cast<FileSystemItem>().ToList())
+ {
+ SelectedItems.Add(item);
+ }
+ }
+ }
+
+ private void OnSelectedItemsChanged()
+ {
+ if (SelectedItems != null)
+ {
+ SelectedItems.CollectionChanged -= SelectedItems_CollectionChanged;
+ SelectedItems.CollectionChanged += SelectedItems_CollectionChanged;
+ SynchronizeControls();
+ }
+ }
+
+ private void SelectedItems_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
+ {
+ SynchronizeControls();
+ }
+
+ private void SynchronizeControls()
+ {
+ if (_listBox == null) return;
+
+ if (!_preventSynchronization)
+ {
+ _preventSynchronization = true;
+
+ _listBox.SelectedItems.Clear();
+ _datagrid.SelectedItems.Clear();
+
+ foreach (var item in SelectedItems)
+ {
+ _listBox.SelectedItems.Add(item);
+ _datagrid.SelectedItems.Add(item);
+ }
+
+ _preventSynchronization = false;
+ }
+ }
+
+ protected override void OnDrop(DragEventArgs e)
+ {
+ base.OnDrop(e);
+
+ try
+ {
+ string[] items = (string[])e.Data.GetData(DataFormats.FileDrop, false);
+
+ if (items != null && items.Length > 0)
+ {
+ List<FileSystemItem> fItems = new List<FileSystemItem>();
+
+ foreach (var item in items)
+ {
+ if (Directory.Exists(item))
+ {
+ fItems.Add(new FolderItem() { Path = item });
+ }
+ else if (File.Exists(item))
+ {
+ fItems.Add(new FileItem() { Path = item });
+ }
+ }
+
+ if (fItems.Count > 0)
+ {
+ DropCommand?.Execute(fItems);
+ }
+ }
+ }
+ catch { } //Ignore
+ }
+
+ protected override void OnPreviewMouseLeftButtonDown(MouseButtonEventArgs e)
+ {
+ base.OnPreviewMouseLeftButtonDown(e);
+
+ if (!AllowDrag) return;
+
+ if (e.OriginalSource is FrameworkElement)
+ {
+ var listBoxItem = (e.OriginalSource as FrameworkElement).FindAncestor<ListBoxItem>();
+ var dataGridRow = (e.OriginalSource as FrameworkElement).FindAncestor<DataGridRow>();
+ if (listBoxItem == null && dataGridRow == null)
+ {
+ return;
+ }
+ }
+
+ _selectedItemsBeforeDrag.Clear();
+ _selectedItemsBeforeDrag.AddRange(SelectedItems);
+ _isMouseDown = true;
+ AllowDrop = false;
+ _dragOutStartPoint = e.GetPosition(null);
+ }
+
+ protected override void OnPreviewMouseLeftButtonUp(MouseButtonEventArgs e)
+ {
+ base.OnPreviewMouseLeftButtonUp(e);
+ AllowDrop = true;
+ _isMouseDown = false;
+ }
+
+ protected async override void OnPreviewMouseMove(MouseEventArgs e)
+ {
+ base.OnPreviewMouseMove(e);
+
+ if (_isMouseDown)
+ {
+ Point mpos = e.GetPosition(null);
+ Vector diff = this._dragOutStartPoint - mpos;
+
+ if (e.LeftButton == MouseButtonState.Pressed &&
+ Math.Abs(diff.X) > SystemParameters.MinimumHorizontalDragDistance ||
+ Math.Abs(diff.Y) > SystemParameters.MinimumVerticalDragDistance)
+ {
+ if (SelectedItems.Count == 0)
+ {
+ return;
+ }
+
+
+ SelectedItems.Clear();
+
+ foreach (var item in _selectedItemsBeforeDrag)
+ {
+ SelectedItems.Add(item);
+ }
+
+ List<Tuple<FileSystemItem, TemporaryFile>> dropItems = new List<Tuple<FileSystemItem, TemporaryFile>>();
+
+ foreach (var item in SelectedItems)
+ {
+ var tempFile = TemporaryManager.Default.CreateFile(".remote");
+ dropItems.Add(new Tuple<FileSystemItem, TemporaryFile>(item, tempFile));
+ }
+
+ List<DragItem> notifyItems = new List<DragItem>();
+
+ List<FileSystemWatcher> watchers = new List<FileSystemWatcher>();
+
+ FileSystemEventHandler handler = (x, args) =>
+ {
+ var detectedDropItem = dropItems.SingleOrDefault(y => y.Item2.FileName == System.IO.Path.GetFileName(args.FullPath));
+
+ Debug.WriteLine($"File Created: {args.FullPath}");
+
+ if (detectedDropItem != null)
+ {
+ try
+ {
+ detectedDropItem.Item2.Delete(); //Delete temp file.
+
+ if (File.Exists(args.FullPath))
+ {
+ File.Delete(args.FullPath); //Delete dropped fake file.
+
+ dropItems.Remove(detectedDropItem);
+
+ notifyItems.Add(new DragItem()
+ {
+ FileSystemItem = detectedDropItem.Item1,
+ Destination = System.IO.Path.GetDirectoryName(args.FullPath),
+ });
+
+ if (dropItems.Count == 0)
+ {
+ foreach (var watcher in watchers)
+ {
+ watcher.Dispose();
+ }
+
+ //Notify to user with all items!
+ Dispatcher.BeginInvoke(new Action(() =>
+ {
+ DragCommand?.Execute(notifyItems);
+ }));
+ }
+ }
+ }
+ catch { }
+ }
+ else
+ {
+ Debug.WriteLine($"Not Found: {args.FullPath}");
+ }
+ };
+
+ foreach (var drive in DriveInfo.GetDrives().Where(x => x.IsReady && (x.DriveType == DriveType.Fixed || x.DriveType == DriveType.Removable)))
+ {
+ FileSystemWatcher watcher = new FileSystemWatcher(drive.RootDirectory.FullName, "*.remote");
+ watcher.IncludeSubdirectories = true;
+ watcher.EnableRaisingEvents = true;
+ watcher.Created += handler;
+ watchers.Add(watcher);
+ }
+
+ string[] files = dropItems.Select(x => x.Item2.Path).ToArray();
+ var ef = DragDrop.DoDragDrop(this, new DataObject(DataFormats.FileDrop, files, false), DragDropEffects.Copy);
+
+ await Task.Delay(3000);
+
+ foreach (var watcher in watchers)
+ {
+ watcher.Dispose();
+ }
+
+ if (dropItems.Count > 0)
+ {
+ //Notify about problem!
+ }
+
+ _isMouseDown = false;
+ AllowDrop = true;
+ }
+ }
+ }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.FileSystem/FileExplorerControlMode.cs b/Software/Visual_Studio/Tango.FileSystem/FileExplorerControlMode.cs
new file mode 100644
index 000000000..b11c93845
--- /dev/null
+++ b/Software/Visual_Studio/Tango.FileSystem/FileExplorerControlMode.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tango.FileSystem
+{
+ public enum FileExplorerControlMode
+ {
+ Large,
+ Details
+ }
+}
diff --git a/Software/Visual_Studio/Tango.FileSystem/FileItem.cs b/Software/Visual_Studio/Tango.FileSystem/FileItem.cs
new file mode 100644
index 000000000..cbc90ce06
--- /dev/null
+++ b/Software/Visual_Studio/Tango.FileSystem/FileItem.cs
@@ -0,0 +1,132 @@
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Interop;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using Tango.Core.IO;
+
+namespace Tango.FileSystem
+{
+ public class FileItem : FileSystemItem
+ {
+ private static Dictionary<String, BitmapSource> iconCache;
+ private static Dictionary<String, BitmapSource> smallIconCache;
+ private static Dictionary<String, String> typeDescriptionCache;
+
+ static FileItem()
+ {
+ iconCache = new Dictionary<string, BitmapSource>();
+ smallIconCache = new Dictionary<string, BitmapSource>();
+ typeDescriptionCache = new Dictionary<string, string>();
+ }
+
+ public FileItem()
+ {
+ Type = FileSystemItemType.File;
+ }
+
+ public String Extension
+ {
+ get { return System.IO.Path.GetExtension(Path); }
+ }
+
+ private BitmapSource _icon;
+ public BitmapSource Icon
+ {
+ get
+ {
+ if (_icon == null)
+ {
+ _icon = GetFileIcon();
+ }
+
+ return _icon;
+ }
+ }
+
+ private BitmapSource _smallIcon;
+ public BitmapSource SmallIcon
+ {
+ get
+ {
+ if (_smallIcon == null)
+ {
+ _smallIcon = GetSmallFileIcon();
+ }
+
+ return _smallIcon;
+ }
+ }
+
+ public override string Description
+ {
+ get
+ {
+ if (typeDescriptionCache.ContainsKey(Extension))
+ {
+ return typeDescriptionCache[Extension];
+ }
+ else
+ {
+ var tempFile = TemporaryManager.Default.CreateFile(Extension);
+ var shellFile = Microsoft.WindowsAPICodePack.Shell.ShellFile.FromFilePath(tempFile);
+ var text = shellFile.Properties.System.ItemTypeText.Value.ToStringSafe();
+ shellFile.Dispose();
+ tempFile.Delete();
+ typeDescriptionCache.Add(Extension, text);
+ return text;
+ }
+ }
+ }
+
+ protected BitmapSource GetFileIcon()
+ {
+ if (iconCache.ContainsKey(Extension))
+ {
+ return iconCache[Extension];
+ }
+ else
+ {
+ var tempFile = TemporaryManager.Default.CreateFile(Extension);
+ var shellFile = Microsoft.WindowsAPICodePack.Shell.ShellFile.FromFilePath(tempFile);
+ var source = shellFile.Thumbnail.MediumBitmapSource;
+ shellFile.Dispose();
+ tempFile.Delete();
+ iconCache.Add(Extension, source);
+ return source;
+ }
+ }
+
+ private BitmapSource GetSmallFileIcon()
+ {
+ if (smallIconCache.ContainsKey(Extension))
+ {
+ return smallIconCache[Extension];
+ }
+ else
+ {
+ var tempFile = TemporaryManager.Default.CreateFile(Extension);
+ var shellFile = Microsoft.WindowsAPICodePack.Shell.ShellFile.FromFilePath(tempFile);
+ var source = shellFile.Thumbnail.SmallBitmapSource;
+ shellFile.Dispose();
+ tempFile.Delete();
+ smallIconCache.Add(Extension, source);
+ return source;
+ }
+ }
+
+ private BitmapSource IconToBitmapSource(Icon icon)
+ {
+ var imageSource = Imaging.CreateBitmapSourceFromHIcon(
+ icon.Handle,
+ Int32Rect.Empty,
+ BitmapSizeOptions.FromEmptyOptions());
+ return imageSource;
+ }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.FileSystem/FileSystemDataGrid.cs b/Software/Visual_Studio/Tango.FileSystem/FileSystemDataGrid.cs
new file mode 100644
index 000000000..c1c31cf95
--- /dev/null
+++ b/Software/Visual_Studio/Tango.FileSystem/FileSystemDataGrid.cs
@@ -0,0 +1,30 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Navigation;
+using System.Windows.Shapes;
+
+namespace Tango.FileSystem
+{
+ public class FileSystemDataGrid : DataGrid
+ {
+ static FileSystemDataGrid()
+ {
+ DefaultStyleKeyProperty.OverrideMetadata(typeof(FileSystemDataGrid), new FrameworkPropertyMetadata(typeof(FileSystemDataGrid)));
+ }
+
+ protected override DependencyObject GetContainerForItemOverride()
+ {
+ return new FileSystemDataGridRow();
+ }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.FileSystem/FileSystemDataGridRow.cs b/Software/Visual_Studio/Tango.FileSystem/FileSystemDataGridRow.cs
new file mode 100644
index 000000000..4cc67f888
--- /dev/null
+++ b/Software/Visual_Studio/Tango.FileSystem/FileSystemDataGridRow.cs
@@ -0,0 +1,40 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Navigation;
+using System.Windows.Shapes;
+
+namespace Tango.FileSystem
+{
+ public class FileSystemDataGridRow : DataGridRow
+ {
+ public ICommand DoubleClickCommand
+ {
+ get { return (ICommand)GetValue(DoubleClickCommandProperty); }
+ set { SetValue(DoubleClickCommandProperty, value); }
+ }
+ public static readonly DependencyProperty DoubleClickCommandProperty =
+ DependencyProperty.Register("DoubleClickCommand", typeof(ICommand), typeof(FileSystemDataGridRow), new PropertyMetadata(null));
+
+ protected override void OnPreviewMouseDoubleClick(MouseButtonEventArgs e)
+ {
+ base.OnPreviewMouseDoubleClick(e);
+
+ DoubleClickCommand?.Execute(DataContext);
+ }
+
+ static FileSystemDataGridRow()
+ {
+ DefaultStyleKeyProperty.OverrideMetadata(typeof(FileSystemDataGridRow), new FrameworkPropertyMetadata(typeof(FileSystemDataGridRow)));
+ }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.FileSystem/FileSystemItem.cs b/Software/Visual_Studio/Tango.FileSystem/FileSystemItem.cs
new file mode 100644
index 000000000..536409f63
--- /dev/null
+++ b/Software/Visual_Studio/Tango.FileSystem/FileSystemItem.cs
@@ -0,0 +1,79 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Media.Imaging;
+using Tango.FileSystem.Network;
+
+namespace Tango.FileSystem
+{
+ public abstract class FileSystemItem
+ {
+ public String Path { get; set; }
+
+ public FileSystemItemType Type { get; protected set; }
+
+ public abstract String Description { get; }
+
+ public DateTime DateModified { get; set; }
+
+ public long Size { get; set; }
+
+ public String Name
+ {
+ get { return OnGetName(); }
+ }
+
+ public FileSystemItem()
+ {
+ DateModified = DateTime.Now;
+ Size = 1000 * 1000;
+ }
+
+ protected virtual String OnGetName()
+ {
+ return System.IO.Path.GetFileName(Path);
+ }
+
+ public override string ToString()
+ {
+ return Name;
+ }
+
+ public static FileSystemItem FromDTO(FileSystemItemDTO dto)
+ {
+ FileSystemItem item = null;
+
+ if (dto.Type == FileSystemItemType.Drive)
+ {
+ item = new DriveItem()
+ {
+ DriveType = dto.DriveType,
+ Label = dto.DriveLabel,
+ Items = dto.Items?.Select(x => FromDTO(x)).ToObservableCollection()
+ };
+ }
+ else if (dto.Type == FileSystemItemType.Folder)
+ {
+ item = new FolderItem()
+ {
+ Items = dto.Items?.Select(x => FromDTO(x)).ToObservableCollection(),
+ IsRoot = dto.IsRoot,
+ };
+ }
+ else if (dto.Type == FileSystemItemType.File)
+ {
+ item = new FileItem();
+ }
+
+ item.DateModified = dto.DateModified;
+ item.Path = dto.Path;
+ item.Size = dto.Size;
+ item.Type = dto.Type;
+
+ return item;
+ }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.FileSystem/FileSystemItemType.cs b/Software/Visual_Studio/Tango.FileSystem/FileSystemItemType.cs
new file mode 100644
index 000000000..5af1858a9
--- /dev/null
+++ b/Software/Visual_Studio/Tango.FileSystem/FileSystemItemType.cs
@@ -0,0 +1,15 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tango.FileSystem
+{
+ public enum FileSystemItemType
+ {
+ Drive,
+ Folder,
+ File,
+ }
+}
diff --git a/Software/Visual_Studio/Tango.FileSystem/FileSystemManager.cs b/Software/Visual_Studio/Tango.FileSystem/FileSystemManager.cs
new file mode 100644
index 000000000..44c8f1901
--- /dev/null
+++ b/Software/Visual_Studio/Tango.FileSystem/FileSystemManager.cs
@@ -0,0 +1,110 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Threading.Tasks;
+using Tango.FileSystem.Network;
+
+namespace Tango.FileSystem
+{
+ public class FileSystemManager
+ {
+ public FileSystemItemDTO GetRoot()
+ {
+ FileSystemItemDTO folder = new FileSystemItemDTO();
+ folder.Path = "This PC";
+ folder.IsRoot = true;
+ folder.Type = FileSystemItemType.Folder;
+ folder.Items = DriveInfo.GetDrives().Where(x => x.DriveType == DriveType.Fixed || x.DriveType == DriveType.Removable || x.DriveType == DriveType.Network).Select(x => new FileSystemItemDTO()
+ {
+ Path = x.RootDirectory.FullName,
+ DriveType = x.DriveType,
+ DriveLabel = x.Name,
+ Type = FileSystemItemType.Drive,
+ }).Cast<FileSystemItemDTO>().ToList();
+
+ return folder;
+ }
+
+ public FileSystemItemDTO GetFolder(GetFileSystemItemRequest request)
+ {
+ List<FileSystemItemDTO> items = new List<FileSystemItemDTO>();
+
+ if (request.SpecialFolder.HasValue)
+ {
+ request.Path = Environment.GetFolderPath(request.SpecialFolder.Value);
+ }
+
+ if (String.IsNullOrWhiteSpace(request.Path))
+ {
+ return GetRoot();
+ }
+
+ try
+ {
+ if (request.Path.Count(x => x == '%') == 2)
+ {
+ var variable = Regex.Match(request.Path, "(?<=%)(.*?)(?=%)").Value;
+ request.Path = request.Path.Replace($"%{variable}%", Environment.ExpandEnvironmentVariables($"%{variable}%"));
+ }
+ }
+ catch
+ {
+ throw new ArgumentException("Could not parse environment variable.");
+ }
+
+ request.Path = Path.GetFullPath(request.Path);
+
+ if (!Directory.Exists(request.Path))
+ {
+ throw new DirectoryNotFoundException("The specified directory could not be located.");
+ }
+
+ foreach (var directory in Directory.GetDirectories(request.Path))
+ {
+ items.Add(new FileSystemItemDTO()
+ {
+ Path = directory,
+ Type = FileSystemItemType.Folder,
+ DateModified = Directory.GetLastWriteTimeUtc(directory),
+ });
+ }
+
+ foreach (var file in Directory.GetFiles(request.Path))
+ {
+ items.Add(new FileSystemItemDTO()
+ {
+ Path = file,
+ Type = FileSystemItemType.File,
+ DateModified = File.GetLastWriteTimeUtc(file),
+ Size = new FileInfo(file).Length
+ });
+ }
+
+ return new FileSystemItemDTO()
+ {
+ Path = request.Path,
+ Type = request.Path.Length == 3 ? FileSystemItemType.Drive : FileSystemItemType.Folder,
+ Items = items
+ };
+ }
+
+ public void Delete(String path)
+ {
+ if (Directory.Exists(path))
+ {
+ Directory.Delete(path, true);
+ }
+ else if (File.Exists(path))
+ {
+ File.Delete(path);
+ }
+ else
+ {
+ throw new FileNotFoundException("Could not locate the specified file or directory.");
+ }
+ }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.FileSystem/FolderItem.cs b/Software/Visual_Studio/Tango.FileSystem/FolderItem.cs
new file mode 100644
index 000000000..95b065a2d
--- /dev/null
+++ b/Software/Visual_Studio/Tango.FileSystem/FolderItem.cs
@@ -0,0 +1,34 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tango.FileSystem
+{
+ public class FolderItem : FileSystemItem, IFileSystemContainer
+ {
+ public ObservableCollection<FileSystemItem> Items { get; set; }
+
+ public bool IsRoot { get; set; }
+
+ public object Icon { get; set; } //Fake for binding error.
+
+ public object SmallIcon { get; set; } //Fake for binding error.
+
+ public FolderItem()
+ {
+ Type = FileSystemItemType.Folder;
+ Items = new ObservableCollection<FileSystemItem>();
+ }
+
+ public override string Description
+ {
+ get
+ {
+ return "Folder";
+ }
+ }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.FileSystem/IFileSystemContainer.cs b/Software/Visual_Studio/Tango.FileSystem/IFileSystemContainer.cs
new file mode 100644
index 000000000..a75764a14
--- /dev/null
+++ b/Software/Visual_Studio/Tango.FileSystem/IFileSystemContainer.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tango.FileSystem
+{
+ public interface IFileSystemContainer
+ {
+ ObservableCollection<FileSystemItem> Items { get; set; }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.FileSystem/Images/drive.png b/Software/Visual_Studio/Tango.FileSystem/Images/drive.png
new file mode 100644
index 000000000..b0cda6195
--- /dev/null
+++ b/Software/Visual_Studio/Tango.FileSystem/Images/drive.png
Binary files differ
diff --git a/Software/Visual_Studio/Tango.FileSystem/Images/folder.png b/Software/Visual_Studio/Tango.FileSystem/Images/folder.png
new file mode 100644
index 000000000..800af7186
--- /dev/null
+++ b/Software/Visual_Studio/Tango.FileSystem/Images/folder.png
Binary files differ
diff --git a/Software/Visual_Studio/Tango.FileSystem/Network/AbortOperationRequest.cs b/Software/Visual_Studio/Tango.FileSystem/Network/AbortOperationRequest.cs
new file mode 100644
index 000000000..3e912b9f0
--- /dev/null
+++ b/Software/Visual_Studio/Tango.FileSystem/Network/AbortOperationRequest.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tango.FileSystem.Network
+{
+ public class AbortOperationRequest
+ {
+ public String OperationId { get; set; }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.FileSystem/Network/AbortOperationResponse.cs b/Software/Visual_Studio/Tango.FileSystem/Network/AbortOperationResponse.cs
new file mode 100644
index 000000000..c5669ad48
--- /dev/null
+++ b/Software/Visual_Studio/Tango.FileSystem/Network/AbortOperationResponse.cs
@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tango.FileSystem.Network
+{
+ public class AbortOperationResponse
+ {
+ }
+}
diff --git a/Software/Visual_Studio/Tango.FileSystem/Network/ChunkDownloadRequest.cs b/Software/Visual_Studio/Tango.FileSystem/Network/ChunkDownloadRequest.cs
new file mode 100644
index 000000000..16951930e
--- /dev/null
+++ b/Software/Visual_Studio/Tango.FileSystem/Network/ChunkDownloadRequest.cs
@@ -0,0 +1,15 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tango.FileSystem.Network
+{
+ public class ChunkDownloadRequest
+ {
+ public String OperationId { get; set; }
+ public long Position { get; set; }
+ public long MaxChunkSize { get; set; } = 1024 * 1024;
+ }
+}
diff --git a/Software/Visual_Studio/Tango.FileSystem/Network/ChunkDownloadResponse.cs b/Software/Visual_Studio/Tango.FileSystem/Network/ChunkDownloadResponse.cs
new file mode 100644
index 000000000..df72e193f
--- /dev/null
+++ b/Software/Visual_Studio/Tango.FileSystem/Network/ChunkDownloadResponse.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tango.FileSystem.Network
+{
+ public class ChunkDownloadResponse
+ {
+ public byte[] Data { get; set; }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.FileSystem/Network/ChunkUploadRequest.cs b/Software/Visual_Studio/Tango.FileSystem/Network/ChunkUploadRequest.cs
new file mode 100644
index 000000000..98a27efd2
--- /dev/null
+++ b/Software/Visual_Studio/Tango.FileSystem/Network/ChunkUploadRequest.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tango.FileSystem.Network
+{
+ public class ChunkUploadRequest
+ {
+ public String OperationId { get; set; }
+ public byte[] Data { get; set; }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.FileSystem/Network/ChunkUploadResponse.cs b/Software/Visual_Studio/Tango.FileSystem/Network/ChunkUploadResponse.cs
new file mode 100644
index 000000000..5cd6a3f80
--- /dev/null
+++ b/Software/Visual_Studio/Tango.FileSystem/Network/ChunkUploadResponse.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tango.FileSystem.Network
+{
+ public class ChunkUploadResponse
+ {
+
+ }
+}
diff --git a/Software/Visual_Studio/Tango.FileSystem/Network/FileDownloadRequest.cs b/Software/Visual_Studio/Tango.FileSystem/Network/FileDownloadRequest.cs
new file mode 100644
index 000000000..b63937f8b
--- /dev/null
+++ b/Software/Visual_Studio/Tango.FileSystem/Network/FileDownloadRequest.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tango.FileSystem.Network
+{
+ public class FileDownloadRequest
+ {
+ public String Path { get; set; }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.FileSystem/Network/FileDownloadResponse.cs b/Software/Visual_Studio/Tango.FileSystem/Network/FileDownloadResponse.cs
new file mode 100644
index 000000000..c57a9bad6
--- /dev/null
+++ b/Software/Visual_Studio/Tango.FileSystem/Network/FileDownloadResponse.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tango.FileSystem.Network
+{
+ public class FileDownloadResponse
+ {
+ public long Length { get; set; }
+ public String OperationId { get; set; }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.FileSystem/Network/FileSystemItemDTO.cs b/Software/Visual_Studio/Tango.FileSystem/Network/FileSystemItemDTO.cs
new file mode 100644
index 000000000..900ba0628
--- /dev/null
+++ b/Software/Visual_Studio/Tango.FileSystem/Network/FileSystemItemDTO.cs
@@ -0,0 +1,33 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tango.FileSystem.Network
+{
+ public class FileSystemItemDTO
+ {
+ public String Path { get; set; }
+
+ public String DriveLabel { get; set; }
+
+ public FileSystemItemType Type { get; set; }
+
+ public DriveType DriveType { get; set; }
+
+ public DateTime DateModified { get; set; }
+
+ public long Size { get; set; }
+
+ public bool IsRoot { get; set; }
+
+ public List<FileSystemItemDTO> Items { get; set; }
+
+ public FileSystemItemDTO()
+ {
+ Items = new List<FileSystemItemDTO>();
+ }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.FileSystem/Network/FileUploadRequest.cs b/Software/Visual_Studio/Tango.FileSystem/Network/FileUploadRequest.cs
new file mode 100644
index 000000000..f3cbc2d54
--- /dev/null
+++ b/Software/Visual_Studio/Tango.FileSystem/Network/FileUploadRequest.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 FileUploadRequest
+ {
+ public String Path { get; set; }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.FileSystem/Network/FileUploadResponse.cs b/Software/Visual_Studio/Tango.FileSystem/Network/FileUploadResponse.cs
new file mode 100644
index 000000000..c0af1a797
--- /dev/null
+++ b/Software/Visual_Studio/Tango.FileSystem/Network/FileUploadResponse.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 FileUploadResponse
+ {
+ public String OperationId { get; set; }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.FileSystem/Network/FolderDownloadRequest.cs b/Software/Visual_Studio/Tango.FileSystem/Network/FolderDownloadRequest.cs
new file mode 100644
index 000000000..cb65a8942
--- /dev/null
+++ b/Software/Visual_Studio/Tango.FileSystem/Network/FolderDownloadRequest.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tango.FileSystem.Network
+{
+ public class FolderDownloadRequest
+ {
+ public String Path { get; set; }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.FileSystem/Network/FolderDownloadResponse.cs b/Software/Visual_Studio/Tango.FileSystem/Network/FolderDownloadResponse.cs
new file mode 100644
index 000000000..239ff2e59
--- /dev/null
+++ b/Software/Visual_Studio/Tango.FileSystem/Network/FolderDownloadResponse.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tango.FileSystem.Network
+{
+ public class FolderDownloadResponse
+ {
+ public long Length { get; set; }
+ public String OperationId { get; set; }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.FileSystem/Network/GetFileSystemItemRequest.cs b/Software/Visual_Studio/Tango.FileSystem/Network/GetFileSystemItemRequest.cs
new file mode 100644
index 000000000..1ed6c19e4
--- /dev/null
+++ b/Software/Visual_Studio/Tango.FileSystem/Network/GetFileSystemItemRequest.cs
@@ -0,0 +1,15 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using static System.Environment;
+
+namespace Tango.FileSystem.Network
+{
+ public class GetFileSystemItemRequest
+ {
+ public String Path { get; set; }
+ public SpecialFolder? SpecialFolder { get; set; }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.FileSystem/Network/GetFileSystemItemResponse.cs b/Software/Visual_Studio/Tango.FileSystem/Network/GetFileSystemItemResponse.cs
new file mode 100644
index 000000000..8b82e41fc
--- /dev/null
+++ b/Software/Visual_Studio/Tango.FileSystem/Network/GetFileSystemItemResponse.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 GetFileSystemItemResponse
+ {
+ public FileSystemItemDTO FileSystemItem { get; set; }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.FileSystem/Properties/AssemblyInfo.cs b/Software/Visual_Studio/Tango.FileSystem/Properties/AssemblyInfo.cs
new file mode 100644
index 000000000..6daf8a3bd
--- /dev/null
+++ b/Software/Visual_Studio/Tango.FileSystem/Properties/AssemblyInfo.cs
@@ -0,0 +1,55 @@
+using System.Reflection;
+using System.Resources;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Windows;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("Tango.FileSystem")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("Tango.FileSystem")]
+[assembly: AssemblyCopyright("Copyright © 2020")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+//In order to begin building localizable applications, set
+//<UICulture>CultureYouAreCodingWith</UICulture> in your .csproj file
+//inside a <PropertyGroup>. For example, if you are using US english
+//in your source files, set the <UICulture> to en-US. Then uncomment
+//the NeutralResourceLanguage attribute below. Update the "en-US" in
+//the line below to match the UICulture setting in the project file.
+
+//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
+
+
+[assembly:ThemeInfo(
+ ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
+ //(used if a resource is not found in the page,
+ // or application resource dictionaries)
+ ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
+ //(used if a resource is not found in the page,
+ // app, or any theme specific resource dictionaries)
+)]
+
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/Software/Visual_Studio/Tango.FileSystem/Properties/Resources.Designer.cs b/Software/Visual_Studio/Tango.FileSystem/Properties/Resources.Designer.cs
new file mode 100644
index 000000000..10db63263
--- /dev/null
+++ b/Software/Visual_Studio/Tango.FileSystem/Properties/Resources.Designer.cs
@@ -0,0 +1,62 @@
+//------------------------------------------------------------------------------
+// <auto-generated>
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+namespace Tango.FileSystem.Properties {
+
+
+ /// <summary>
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ /// </summary>
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class Resources {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Resources() {
+ }
+
+ /// <summary>
+ /// Returns the cached ResourceManager instance used by this class.
+ /// </summary>
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if ((resourceMan == null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Tango.FileSystem.Properties.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ /// <summary>
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ /// </summary>
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.FileSystem/Properties/Resources.resx b/Software/Visual_Studio/Tango.FileSystem/Properties/Resources.resx
new file mode 100644
index 000000000..af7dbebba
--- /dev/null
+++ b/Software/Visual_Studio/Tango.FileSystem/Properties/Resources.resx
@@ -0,0 +1,117 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+ <!--
+ Microsoft ResX Schema
+
+ Version 2.0
+
+ The primary goals of this format is to allow a simple XML format
+ that is mostly human readable. The generation and parsing of the
+ various data types are done through the TypeConverter classes
+ associated with the data types.
+
+ Example:
+
+ ... ado.net/XML headers & schema ...
+ <resheader name="resmimetype">text/microsoft-resx</resheader>
+ <resheader name="version">2.0</resheader>
+ <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+ <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+ <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+ <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+ <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+ <value>[base64 mime encoded serialized .NET Framework object]</value>
+ </data>
+ <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+ <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+ <comment>This is a comment</comment>
+ </data>
+
+ There are any number of "resheader" rows that contain simple
+ name/value pairs.
+
+ Each data row contains a name, and value. The row also contains a
+ type or mimetype. Type corresponds to a .NET class that support
+ text/value conversion through the TypeConverter architecture.
+ Classes that don't support this are serialized and stored with the
+ mimetype set.
+
+ The mimetype is used for serialized objects, and tells the
+ ResXResourceReader how to depersist the object. This is currently not
+ extensible. For a given mimetype the value must be set accordingly:
+
+ Note - application/x-microsoft.net.object.binary.base64 is the format
+ that the ResXResourceWriter will generate, however the reader can
+ read any of the formats listed below.
+
+ mimetype: application/x-microsoft.net.object.binary.base64
+ value : The object must be serialized with
+ : System.Serialization.Formatters.Binary.BinaryFormatter
+ : and then encoded with base64 encoding.
+
+ mimetype: application/x-microsoft.net.object.soap.base64
+ value : The object must be serialized with
+ : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+ : and then encoded with base64 encoding.
+
+ mimetype: application/x-microsoft.net.object.bytearray.base64
+ value : The object must be serialized into a byte array
+ : using a System.ComponentModel.TypeConverter
+ : and then encoded with base64 encoding.
+ -->
+ <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+ <xsd:element name="root" msdata:IsDataSet="true">
+ <xsd:complexType>
+ <xsd:choice maxOccurs="unbounded">
+ <xsd:element name="metadata">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" />
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" />
+ <xsd:attribute name="type" type="xsd:string" />
+ <xsd:attribute name="mimetype" type="xsd:string" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="assembly">
+ <xsd:complexType>
+ <xsd:attribute name="alias" type="xsd:string" />
+ <xsd:attribute name="name" type="xsd:string" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="data">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+ <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
+ <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+ <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="resheader">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required" />
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:choice>
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:schema>
+ <resheader name="resmimetype">
+ <value>text/microsoft-resx</value>
+ </resheader>
+ <resheader name="version">
+ <value>2.0</value>
+ </resheader>
+ <resheader name="reader">
+ <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </resheader>
+ <resheader name="writer">
+ <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </resheader>
+</root> \ No newline at end of file
diff --git a/Software/Visual_Studio/Tango.FileSystem/Properties/Settings.Designer.cs b/Software/Visual_Studio/Tango.FileSystem/Properties/Settings.Designer.cs
new file mode 100644
index 000000000..ddac2a111
--- /dev/null
+++ b/Software/Visual_Studio/Tango.FileSystem/Properties/Settings.Designer.cs
@@ -0,0 +1,30 @@
+//------------------------------------------------------------------------------
+// <auto-generated>
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+namespace Tango.FileSystem.Properties
+{
+
+
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
+ internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
+ {
+
+ private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
+
+ public static Settings Default
+ {
+ get
+ {
+ return defaultInstance;
+ }
+ }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.FileSystem/Properties/Settings.settings b/Software/Visual_Studio/Tango.FileSystem/Properties/Settings.settings
new file mode 100644
index 000000000..033d7a5e9
--- /dev/null
+++ b/Software/Visual_Studio/Tango.FileSystem/Properties/Settings.settings
@@ -0,0 +1,7 @@
+<?xml version='1.0' encoding='utf-8'?>
+<SettingsFile xmlns="uri:settings" CurrentProfile="(Default)">
+ <Profiles>
+ <Profile Name="(Default)" />
+ </Profiles>
+ <Settings />
+</SettingsFile> \ No newline at end of file
diff --git a/Software/Visual_Studio/Tango.FileSystem/Tango.FileSystem.csproj b/Software/Visual_Studio/Tango.FileSystem/Tango.FileSystem.csproj
new file mode 100644
index 000000000..733493f02
--- /dev/null
+++ b/Software/Visual_Studio/Tango.FileSystem/Tango.FileSystem.csproj
@@ -0,0 +1,130 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <ProjectGuid>{C6EBBBBE-2123-44DC-AEF7-A0D47D736AC0}</ProjectGuid>
+ <OutputType>library</OutputType>
+ <RootNamespace>Tango.FileSystem</RootNamespace>
+ <AssemblyName>Tango.FileSystem</AssemblyName>
+ <TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
+ <FileAlignment>512</FileAlignment>
+ <ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+ <WarningLevel>4</WarningLevel>
+ <Deterministic>true</Deterministic>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>bin\Debug\</OutputPath>
+ <DefineConstants>DEBUG;TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <DebugType>pdbonly</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>bin\Release\</OutputPath>
+ <DefineConstants>TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <ItemGroup>
+ <Reference Include="System" />
+ <Reference Include="System.Data" />
+ <Reference Include="System.Drawing" />
+ <Reference Include="System.Xml" />
+ <Reference Include="Microsoft.CSharp" />
+ <Reference Include="System.Core" />
+ <Reference Include="System.Xml.Linq" />
+ <Reference Include="System.Data.DataSetExtensions" />
+ <Reference Include="System.Net.Http" />
+ <Reference Include="System.Xaml">
+ <RequiredTargetFramework>4.0</RequiredTargetFramework>
+ </Reference>
+ <Reference Include="WindowsBase" />
+ <Reference Include="PresentationCore" />
+ <Reference Include="PresentationFramework" />
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="DragItem.cs" />
+ <Compile Include="FileSystemDataGrid.cs" />
+ <Compile Include="FileSystemDataGridRow.cs" />
+ <Compile Include="Network\AbortOperationRequest.cs" />
+ <Compile Include="Network\AbortOperationResponse.cs" />
+ <Compile Include="Network\ChunkUploadRequest.cs" />
+ <Compile Include="Network\ChunkUploadResponse.cs" />
+ <Compile Include="Network\ChunkDownloadRequest.cs" />
+ <Compile Include="Network\ChunkDownloadResponse.cs" />
+ <Compile Include="VirtualFileDataObject.cs" />
+ <Page Include="Themes\Generic.xaml">
+ <Generator>MSBuild:Compile</Generator>
+ <SubType>Designer</SubType>
+ </Page>
+ <Compile Include="DriveItem.cs" />
+ <Compile Include="FileExplorerControl.cs">
+ <SubType>Code</SubType>
+ </Compile>
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="FileExplorerControlMode.cs" />
+ <Compile Include="FileItem.cs" />
+ <Compile Include="FileSystemItem.cs" />
+ <Compile Include="FileSystemItemType.cs" />
+ <Compile Include="FileSystemManager.cs" />
+ <Compile Include="FolderItem.cs" />
+ <Compile Include="IFileSystemContainer.cs" />
+ <Compile Include="Network\FileSystemItemDTO.cs" />
+ <Compile Include="Network\FileUploadRequest.cs" />
+ <Compile Include="Network\FileUploadResponse.cs" />
+ <Compile Include="Network\GetFileSystemItemRequest.cs" />
+ <Compile Include="Network\GetFileSystemItemResponse.cs" />
+ <Compile Include="Network\FileDownloadRequest.cs" />
+ <Compile Include="Network\FileDownloadResponse.cs" />
+ <Compile Include="Network\FolderDownloadRequest.cs" />
+ <Compile Include="Network\FolderDownloadResponse.cs" />
+ <Compile Include="Properties\AssemblyInfo.cs">
+ <SubType>Code</SubType>
+ </Compile>
+ <Compile Include="Properties\Resources.Designer.cs">
+ <AutoGen>True</AutoGen>
+ <DesignTime>True</DesignTime>
+ <DependentUpon>Resources.resx</DependentUpon>
+ </Compile>
+ <Compile Include="Properties\Settings.Designer.cs">
+ <AutoGen>True</AutoGen>
+ <DependentUpon>Settings.settings</DependentUpon>
+ <DesignTimeSharedInput>True</DesignTimeSharedInput>
+ </Compile>
+ <EmbeddedResource Include="Properties\Resources.resx">
+ <Generator>ResXFileCodeGenerator</Generator>
+ <LastGenOutput>Resources.Designer.cs</LastGenOutput>
+ </EmbeddedResource>
+ <None Include="Properties\Settings.settings">
+ <Generator>SettingsSingleFileGenerator</Generator>
+ <LastGenOutput>Settings.Designer.cs</LastGenOutput>
+ </None>
+ </ItemGroup>
+ <ItemGroup>
+ <Resource Include="Images\drive.png" />
+ <Resource Include="Images\folder.png" />
+ </ItemGroup>
+ <ItemGroup>
+ <PackageReference Include="WindowsAPICodePack-Shell">
+ <Version>1.1.1</Version>
+ </PackageReference>
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\Tango.Core\Tango.Core.csproj">
+ <Project>{a34ee0f0-649d-41c8-8489-b6f1cc6924ee}</Project>
+ <Name>Tango.Core</Name>
+ </ProjectReference>
+ <ProjectReference Include="..\Tango.SharedUI\Tango.SharedUI.csproj">
+ <Project>{8491d07b-c1f6-4b62-a412-41b9fd2d6538}</Project>
+ <Name>Tango.SharedUI</Name>
+ </ProjectReference>
+ </ItemGroup>
+ <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+</Project> \ No newline at end of file
diff --git a/Software/Visual_Studio/Tango.FileSystem/Themes/Generic.xaml b/Software/Visual_Studio/Tango.FileSystem/Themes/Generic.xaml
new file mode 100644
index 000000000..f793be947
--- /dev/null
+++ b/Software/Visual_Studio/Tango.FileSystem/Themes/Generic.xaml
@@ -0,0 +1,199 @@
+<ResourceDictionary
+ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+ 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">
+
+ <converters:ByteArrayToFileSizeConverter x:Key="ByteArrayToFileSizeConverter" />
+ <converters:DateTimeUtcToLocalDateTime x:Key="DateTimeUtcToLocalDateTime" />
+
+ <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>
+
+ <Style TargetType="{x:Type local:FileSystemDataGrid}" BasedOn="{StaticResource {x:Type DataGrid}}">
+
+ </Style>
+
+ <Style x:Key="FileSystemCellStyle" TargetType="DataGridCell" BasedOn="{StaticResource {x:Type DataGridCell}}">
+ <Setter Property="BorderThickness" Value="0"/>
+ <Setter Property="FocusVisualStyle" Value="{x:Null}"/>
+ <Setter Property="VerticalContentAlignment" Value="Center"></Setter>
+ </Style>
+
+ <Style 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>
+ <Setter Property="Mode" Value="Details"></Setter>
+ <Setter Property="FocusVisualStyle" Value="{x:Null}"></Setter>
+ <Setter Property="Focusable" Value="True"></Setter>
+ <Setter Property="AllowDrop" Value="True"></Setter>
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate TargetType="{x:Type local:FileExplorerControl}">
+ <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>
+ <Style TargetType="ListBox" BasedOn="{StaticResource {x:Type ListBox}}">
+ <Setter Property="Visibility" Value="Collapsed"></Setter>
+ <Style.Triggers>
+ <DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},Path=Mode}" Value="Large">
+ <Setter Property="Visibility" Value="Visible"></Setter>
+ </DataTrigger>
+ </Style.Triggers>
+ </Style>
+ </ListBox.Style>
+ <ListBox.ItemContainerStyle>
+ <Style TargetType="ListBoxItem" BasedOn="{StaticResource {x:Type ListBoxItem}}">
+ <Setter Property="Padding" Value="5"></Setter>
+ <Setter Property="Margin" Value="10"></Setter>
+ </Style>
+ </ListBox.ItemContainerStyle>
+ <ListBox.ItemsPanel>
+ <ItemsPanelTemplate>
+ <WrapPanel Orientation="Horizontal" IsItemsHost="True" />
+ </ItemsPanelTemplate>
+ </ListBox.ItemsPanel>
+ <ListBox.ItemTemplate>
+ <DataTemplate>
+ <DockPanel Width="70" Height="115" Background="Transparent">
+ <DockPanel.InputBindings>
+ <MouseBinding MouseAction="LeftDoubleClick"
+ Command="{Binding RelativeSource={RelativeSource AncestorType=local:FileExplorerControl},Path=ItemDoubleClickedCommand}"
+ CommandParameter="{Binding}" />
+ </DockPanel.InputBindings>
+ <Image DockPanel.Dock="Top" RenderOptions.BitmapScalingMode="Fant" Stretch="Uniform" HorizontalAlignment="Center" VerticalAlignment="Top">
+ <Image.Style>
+ <Style TargetType="Image">
+ <Style.Triggers>
+ <DataTrigger Binding="{Binding Type}" Value="Folder">
+ <Setter Property="Source" Value="{Binding RelativeSource={RelativeSource AncestorType=local:FileExplorerControl},Path=FolderIcon}"></Setter>
+ </DataTrigger>
+ <DataTrigger Binding="{Binding Type}" Value="Drive">
+ <Setter Property="Source" Value="{Binding RelativeSource={RelativeSource AncestorType=local:FileExplorerControl},Path=DriveIcon}"></Setter>
+ </DataTrigger>
+ <DataTrigger Binding="{Binding Type}" Value="File">
+ <Setter Property="Source" Value="{Binding Icon}"></Setter>
+ </DataTrigger>
+ </Style.Triggers>
+ </Style>
+ </Image.Style>
+ </Image>
+ <TextBlock Text="{Binding Name}" TextWrapping="Wrap" TextTrimming="CharacterEllipsis" HorizontalAlignment="Center" TextAlignment="Center" Margin="0 10 0 0"></TextBlock>
+ </DockPanel>
+ </DataTemplate>
+ </ListBox.ItemTemplate>
+ </ListBox>
+
+ <Grid>
+ <Grid.Style>
+ <Style TargetType="Grid">
+ <Setter Property="Visibility" Value="Collapsed"></Setter>
+ <Style.Triggers>
+ <DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},Path=Mode}" Value="Details">
+ <Setter Property="Visibility" Value="Visible"></Setter>
+ </DataTrigger>
+ </Style.Triggers>
+ </Style>
+ </Grid.Style>
+ <local:FileSystemDataGrid x:Name="PART_datagrid" ItemsSource="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},Path=CurrentItem.Items}" CellStyle="{StaticResource FileSystemCellStyle}">
+ <DataGrid.Style>
+ <Style TargetType="DataGrid" BasedOn="{StaticResource {x:Type DataGrid}}">
+ <Setter Property="Background" Value="Transparent"></Setter>
+ <Setter Property="AutoGenerateColumns" Value="False"></Setter>
+ <Setter Property="CanUserAddRows" Value="False"></Setter>
+ <Setter Property="CanUserDeleteRows" Value="False"></Setter>
+ <Setter Property="CanUserReorderColumns" Value="False"></Setter>
+ <Setter Property="CanUserResizeColumns" Value="True"></Setter>
+ <Setter Property="CanUserResizeRows" Value="False"></Setter>
+ <Setter Property="CanUserSortColumns" Value="True"></Setter>
+ <Setter Property="IsReadOnly" Value="True"></Setter>
+ <Setter Property="SelectionMode" Value="Extended"></Setter>
+ <Setter Property="SelectionUnit" Value="FullRow"></Setter>
+ <Setter Property="RowHeight" Value="22"></Setter>
+ <Setter Property="HorizontalGridLinesBrush" Value="Transparent"></Setter>
+ <Setter Property="VerticalGridLinesBrush" Value="Transparent"></Setter>
+ <Setter Property="HorizontalScrollBarVisibility" Value="Disabled"></Setter>
+ <Setter Property="CellStyle" Value="{StaticResource FileSystemCellStyle}" />
+ </Style>
+ </DataGrid.Style>
+
+ <DataGrid.Columns>
+ <DataGridTemplateColumn Header="Name" Width="300*">
+ <DataGridTemplateColumn.CellTemplate>
+ <DataTemplate>
+ <DockPanel Background="Transparent">
+ <Image Width="18" Height="18" RenderOptions.BitmapScalingMode="Fant" Stretch="Uniform" HorizontalAlignment="Center" VerticalAlignment="Center">
+ <Image.Style>
+ <Style TargetType="Image">
+ <Style.Triggers>
+ <DataTrigger Binding="{Binding Type}" Value="Drive">
+ <Setter Property="Source" Value="{Binding RelativeSource={RelativeSource AncestorType=local:FileExplorerControl},Path=DriveIcon}"></Setter>
+ </DataTrigger>
+ <DataTrigger Binding="{Binding Type}" Value="File">
+ <Setter Property="Source" Value="{Binding SmallIcon}"></Setter>
+ </DataTrigger>
+ <DataTrigger Binding="{Binding Type}" Value="Folder">
+ <Setter Property="Source" Value="{Binding RelativeSource={RelativeSource AncestorType=local:FileExplorerControl},Path=FolderIcon}"></Setter>
+ </DataTrigger>
+ </Style.Triggers>
+ </Style>
+ </Image.Style>
+ </Image>
+ <TextBlock Text="{Binding Name}" VerticalAlignment="Center" TextTrimming="CharacterEllipsis" Margin="5 0 0 0"></TextBlock>
+ </DockPanel>
+ </DataTemplate>
+ </DataGridTemplateColumn.CellTemplate>
+ </DataGridTemplateColumn>
+ <DataGridTemplateColumn Header="Date Modified" Width="170*">
+ <DataGridTemplateColumn.CellTemplate>
+ <DataTemplate>
+ <DockPanel Background="Transparent">
+ <TextBlock Text="{Binding DateModified,Converter={StaticResource DateTimeUtcToLocalDateTime}}" VerticalAlignment="Center"></TextBlock>
+ </DockPanel>
+ </DataTemplate>
+ </DataGridTemplateColumn.CellTemplate>
+ </DataGridTemplateColumn>
+ <DataGridTemplateColumn Header="Type" Width="140*">
+ <DataGridTemplateColumn.CellTemplate>
+ <DataTemplate>
+ <DockPanel Background="Transparent">
+ <TextBlock Text="{Binding Description}" VerticalAlignment="Center"></TextBlock>
+ </DockPanel>
+ </DataTemplate>
+ </DataGridTemplateColumn.CellTemplate>
+ </DataGridTemplateColumn>
+ <DataGridTemplateColumn Header="Size" Width="100*">
+ <DataGridTemplateColumn.CellTemplate>
+ <DataTemplate>
+ <DockPanel Background="Transparent">
+ <TextBlock Text="{Binding Size,Converter={StaticResource ByteArrayToFileSizeConverter}}" VerticalAlignment="Center">
+ <TextBlock.Style>
+ <Style TargetType="TextBlock">
+ <Setter Property="Visibility" Value="Hidden"></Setter>
+ <Style.Triggers>
+ <DataTrigger Binding="{Binding Type}" Value="File">
+ <Setter Property="Visibility" Value="Visible"></Setter>
+ </DataTrigger>
+ </Style.Triggers>
+ </Style>
+ </TextBlock.Style>
+ </TextBlock>
+ </DockPanel>
+ </DataTemplate>
+ </DataGridTemplateColumn.CellTemplate>
+ </DataGridTemplateColumn>
+ </DataGrid.Columns>
+ </local:FileSystemDataGrid>
+ </Grid>
+ </Grid>
+ </Border>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+</ResourceDictionary>
diff --git a/Software/Visual_Studio/Tango.FileSystem/VirtualFileDataObject.cs b/Software/Visual_Studio/Tango.FileSystem/VirtualFileDataObject.cs
new file mode 100644
index 000000000..a4a065792
--- /dev/null
+++ b/Software/Visual_Studio/Tango.FileSystem/VirtualFileDataObject.cs
@@ -0,0 +1,983 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+using System.IO;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Runtime.InteropServices.ComTypes;
+using System.Windows;
+
+namespace Tango.FileSystem
+{
+ /// <summary>
+ /// Class implementing drag/drop and clipboard support for virtual files.
+ /// Also offers an alternate interface to the IDataObject interface.
+ /// </summary>
+ public sealed class VirtualFileDataObject : System.Runtime.InteropServices.ComTypes.IDataObject, IAsyncOperation
+ {
+ /// <summary>
+ /// Gets or sets a value indicating whether the data object can be used asynchronously.
+ /// </summary>
+ public bool IsAsynchronous { get; set; }
+
+ /// <summary>
+ /// Identifier for CFSTR_FILECONTENTS.
+ /// </summary>
+ private static short FILECONTENTS = (short)(DataFormats.GetDataFormat(NativeMethods.CFSTR_FILECONTENTS).Id);
+
+ /// <summary>
+ /// Identifier for CFSTR_FILEDESCRIPTORW.
+ /// </summary>
+ private static short FILEDESCRIPTORW = (short)(DataFormats.GetDataFormat(NativeMethods.CFSTR_FILEDESCRIPTORW).Id);
+
+ /// <summary>
+ /// Identifier for CFSTR_PASTESUCCEEDED.
+ /// </summary>
+ private static short PASTESUCCEEDED = (short)(DataFormats.GetDataFormat(NativeMethods.CFSTR_PASTESUCCEEDED).Id);
+
+ /// <summary>
+ /// Identifier for CFSTR_PERFORMEDDROPEFFECT.
+ /// </summary>
+ private static short PERFORMEDDROPEFFECT = (short)(DataFormats.GetDataFormat(NativeMethods.CFSTR_PERFORMEDDROPEFFECT).Id);
+
+ /// <summary>
+ /// Identifier for CFSTR_PREFERREDDROPEFFECT.
+ /// </summary>
+ private static short PREFERREDDROPEFFECT = (short)(DataFormats.GetDataFormat(NativeMethods.CFSTR_PREFERREDDROPEFFECT).Id);
+
+ /// <summary>
+ /// In-order list of registered data objects.
+ /// </summary>
+ private List<DataObject> _dataObjects = new List<DataObject>();
+
+ /// <summary>
+ /// Tracks whether an asynchronous operation is ongoing.
+ /// </summary>
+ private bool _inOperation;
+
+ /// <summary>
+ /// Stores the user-specified start action.
+ /// </summary>
+ private Action<VirtualFileDataObject> _startAction;
+
+ /// <summary>
+ /// Stores the user-specified end action.
+ /// </summary>
+ private Action<VirtualFileDataObject> _endAction;
+
+ /// <summary>
+ /// Initializes a new instance of the VirtualFileDataObject class.
+ /// </summary>
+ public VirtualFileDataObject()
+ {
+ IsAsynchronous = true;
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the VirtualFileDataObject class.
+ /// </summary>
+ /// <param name="startAction">Optional action to run at the start of the data transfer.</param>
+ /// <param name="endAction">Optional action to run at the end of the data transfer.</param>
+ public VirtualFileDataObject(Action<VirtualFileDataObject> startAction, Action<VirtualFileDataObject> endAction)
+ : this()
+ {
+ _startAction = startAction;
+ _endAction = endAction;
+ }
+
+ #region IDataObject Members
+ // Explicit interface implementation hides the technical details from users of VirtualFileDataObject.
+
+ /// <summary>
+ /// Creates a connection between a data object and an advisory sink.
+ /// </summary>
+ /// <param name="pFormatetc">A FORMATETC structure that defines the format, target device, aspect, and medium that will be used for future notifications.</param>
+ /// <param name="advf">One of the ADVF values that specifies a group of flags for controlling the advisory connection.</param>
+ /// <param name="adviseSink">A pointer to the IAdviseSink interface on the advisory sink that will receive the change notification.</param>
+ /// <param name="connection">When this method returns, contains a pointer to a DWORD token that identifies this connection.</param>
+ /// <returns>HRESULT success code.</returns>
+ [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "Method doesn't decrease security.")]
+ int System.Runtime.InteropServices.ComTypes.IDataObject.DAdvise(ref FORMATETC pFormatetc, ADVF advf, IAdviseSink adviseSink, out int connection)
+ {
+ Marshal.ThrowExceptionForHR(NativeMethods.OLE_E_ADVISENOTSUPPORTED);
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Destroys a notification connection that had been previously established.
+ /// </summary>
+ /// <param name="connection">A DWORD token that specifies the connection to remove.</param>
+ [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "Method doesn't decrease security.")]
+ void System.Runtime.InteropServices.ComTypes.IDataObject.DUnadvise(int connection)
+ {
+ Marshal.ThrowExceptionForHR(NativeMethods.OLE_E_ADVISENOTSUPPORTED);
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Creates an object that can be used to enumerate the current advisory connections.
+ /// </summary>
+ /// <param name="enumAdvise">When this method returns, contains an IEnumSTATDATA that receives the interface pointer to the new enumerator object.</param>
+ /// <returns>HRESULT success code.</returns>
+ [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "Method doesn't decrease security.")]
+ int System.Runtime.InteropServices.ComTypes.IDataObject.EnumDAdvise(out IEnumSTATDATA enumAdvise)
+ {
+ Marshal.ThrowExceptionForHR(NativeMethods.OLE_E_ADVISENOTSUPPORTED);
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Creates an object for enumerating the FORMATETC structures for a data object.
+ /// </summary>
+ /// <param name="direction">One of the DATADIR values that specifies the direction of the data.</param>
+ /// <returns>IEnumFORMATETC interface.</returns>
+ [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "Method doesn't decrease security.")]
+ IEnumFORMATETC System.Runtime.InteropServices.ComTypes.IDataObject.EnumFormatEtc(DATADIR direction)
+ {
+ if (direction == DATADIR.DATADIR_GET)
+ {
+ if (0 == _dataObjects.Count)
+ {
+ // Note: SHCreateStdEnumFmtEtc fails for a count of 0; throw helpful exception
+ throw new InvalidOperationException("VirtualFileDataObject requires at least one data object to enumerate.");
+ }
+
+ // Create enumerator and return it
+ IEnumFORMATETC enumerator;
+ if (NativeMethods.SUCCEEDED(NativeMethods.SHCreateStdEnumFmtEtc((uint)(_dataObjects.Count), _dataObjects.Select(d => d.FORMATETC).ToArray(), out enumerator)))
+ {
+ return enumerator;
+ }
+
+ // Returning null here can cause an AV in the caller; throw instead
+ Marshal.ThrowExceptionForHR(NativeMethods.E_FAIL);
+ }
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Provides a standard FORMATETC structure that is logically equivalent to a more complex structure.
+ /// </summary>
+ /// <param name="formatIn">A pointer to a FORMATETC structure that defines the format, medium, and target device that the caller would like to use to retrieve data in a subsequent call such as GetData.</param>
+ /// <param name="formatOut">When this method returns, contains a pointer to a FORMATETC structure that contains the most general information possible for a specific rendering, making it canonically equivalent to formatetIn.</param>
+ /// <returns>HRESULT success code.</returns>
+ int System.Runtime.InteropServices.ComTypes.IDataObject.GetCanonicalFormatEtc(ref FORMATETC formatIn, out FORMATETC formatOut)
+ {
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Obtains data from a source data object.
+ /// </summary>
+ /// <param name="format">A pointer to a FORMATETC structure that defines the format, medium, and target device to use when passing the data.</param>
+ /// <param name="medium">When this method returns, contains a pointer to the STGMEDIUM structure that indicates the storage medium containing the returned data through its tymed member, and the responsibility for releasing the medium through the value of its pUnkForRelease member.</param>
+ [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "Method doesn't decrease security.")]
+ void System.Runtime.InteropServices.ComTypes.IDataObject.GetData(ref FORMATETC format, out STGMEDIUM medium)
+ {
+ medium = new STGMEDIUM();
+ var hr = ((System.Runtime.InteropServices.ComTypes.IDataObject)this).QueryGetData(ref format);
+ if (NativeMethods.SUCCEEDED(hr))
+ {
+ // Find the best match
+ var formatCopy = format; // Cannot use ref or out parameter inside an anonymous method, lambda expression, or query expression
+ var dataObject = _dataObjects
+ .Where(d =>
+ (d.FORMATETC.cfFormat == formatCopy.cfFormat) &&
+ (d.FORMATETC.dwAspect == formatCopy.dwAspect) &&
+ (0 != (d.FORMATETC.tymed & formatCopy.tymed) &&
+ (d.FORMATETC.lindex == formatCopy.lindex)))
+ .FirstOrDefault();
+ if (dataObject != null)
+ {
+ if (!IsAsynchronous && (FILEDESCRIPTORW == dataObject.FORMATETC.cfFormat) && !_inOperation)
+ {
+ // Enter the operation and call the start action
+ _inOperation = true;
+ if (null != _startAction)
+ {
+ _startAction(this);
+ }
+ }
+
+ // Populate the STGMEDIUM
+ medium.tymed = dataObject.FORMATETC.tymed;
+ var result = dataObject.GetData(); // Possible call to user code
+ hr = result.Item2;
+ if (NativeMethods.SUCCEEDED(hr))
+ {
+ medium.unionmember = result.Item1;
+ }
+ }
+ else
+ {
+ // Couldn't find a match
+ hr = NativeMethods.DV_E_FORMATETC;
+ }
+ }
+ if (!NativeMethods.SUCCEEDED(hr)) // Not redundant; hr gets updated in the block above
+ {
+ Marshal.ThrowExceptionForHR(hr);
+ }
+ }
+
+ /// <summary>
+ /// Obtains data from a source data object.
+ /// </summary>
+ /// <param name="format">A pointer to a FORMATETC structure that defines the format, medium, and target device to use when passing the data.</param>
+ /// <param name="medium">A STGMEDIUM that defines the storage medium containing the data being transferred.</param>
+ void System.Runtime.InteropServices.ComTypes.IDataObject.GetDataHere(ref FORMATETC format, ref STGMEDIUM medium)
+ {
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Determines whether the data object is capable of rendering the data described in the FORMATETC structure.
+ /// </summary>
+ /// <param name="format">A pointer to a FORMATETC structure that defines the format, medium, and target device to use for the query.</param>
+ /// <returns>HRESULT success code.</returns>
+ int System.Runtime.InteropServices.ComTypes.IDataObject.QueryGetData(ref FORMATETC format)
+ {
+ var formatCopy = format; // Cannot use ref or out parameter inside an anonymous method, lambda expression, or query expression
+ var formatMatches = _dataObjects.Where(d => d.FORMATETC.cfFormat == formatCopy.cfFormat);
+ if (!formatMatches.Any())
+ {
+ return NativeMethods.DV_E_FORMATETC;
+ }
+ var tymedMatches = formatMatches.Where(d => 0 != (d.FORMATETC.tymed & formatCopy.tymed));
+ if (!tymedMatches.Any())
+ {
+ return NativeMethods.DV_E_TYMED;
+ }
+ var aspectMatches = tymedMatches.Where(d => d.FORMATETC.dwAspect == formatCopy.dwAspect);
+ if (!aspectMatches.Any())
+ {
+ return NativeMethods.DV_E_DVASPECT;
+ }
+ return NativeMethods.S_OK;
+ }
+
+ /// <summary>
+ /// Transfers data to the object that implements this method.
+ /// </summary>
+ /// <param name="formatIn">A FORMATETC structure that defines the format used by the data object when interpreting the data contained in the storage medium.</param>
+ /// <param name="medium">A STGMEDIUM structure that defines the storage medium in which the data is being passed.</param>
+ /// <param name="release">true to specify that the data object called, which implements SetData, owns the storage medium after the call returns.</param>
+ [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "Method doesn't decrease security.")]
+ void System.Runtime.InteropServices.ComTypes.IDataObject.SetData(ref FORMATETC formatIn, ref STGMEDIUM medium, bool release)
+ {
+ var handled = false;
+ if ((formatIn.dwAspect == DVASPECT.DVASPECT_CONTENT) &&
+ (formatIn.tymed == TYMED.TYMED_HGLOBAL) &&
+ (medium.tymed == formatIn.tymed))
+ {
+ // Supported format; capture the data
+ var ptr = NativeMethods.GlobalLock(medium.unionmember);
+ if (IntPtr.Zero != ptr)
+ {
+ try
+ {
+ var length = NativeMethods.GlobalSize(ptr).ToInt32();
+ var data = new byte[length];
+ Marshal.Copy(ptr, data, 0, length);
+ // Store it in our own format
+ SetData(formatIn.cfFormat, data);
+ handled = true;
+ }
+ finally
+ {
+ NativeMethods.GlobalUnlock(medium.unionmember);
+ }
+ }
+
+ // Release memory if we now own it
+ if (release)
+ {
+ Marshal.FreeHGlobal(medium.unionmember);
+ }
+ }
+
+ // Handle synchronous mode
+ if (!IsAsynchronous && (PERFORMEDDROPEFFECT == formatIn.cfFormat) && _inOperation)
+ {
+ // Call the end action and exit the operation
+ if (null != _endAction)
+ {
+ _endAction(this);
+ }
+ _inOperation = false;
+ }
+
+ // Throw if unhandled
+ if (!handled)
+ {
+ throw new NotImplementedException();
+ }
+ }
+
+ #endregion
+
+ /// <summary>
+ /// Provides data for the specified data format (HGLOBAL).
+ /// </summary>
+ /// <param name="dataFormat">Data format.</param>
+ /// <param name="data">Sequence of data.</param>
+ public void SetData(short dataFormat, IEnumerable<byte> data)
+ {
+ _dataObjects.Add(
+ new DataObject
+ {
+ FORMATETC = new FORMATETC
+ {
+ cfFormat = dataFormat,
+ ptd = IntPtr.Zero,
+ dwAspect = DVASPECT.DVASPECT_CONTENT,
+ lindex = -1,
+ tymed = TYMED.TYMED_HGLOBAL
+ },
+ GetData = () =>
+ {
+ var dataArray = data.ToArray();
+ var ptr = Marshal.AllocHGlobal(dataArray.Length);
+ Marshal.Copy(dataArray, 0, ptr, dataArray.Length);
+ return new Tuple<IntPtr, int>(ptr, NativeMethods.S_OK);
+ },
+ });
+ }
+
+ /// <summary>
+ /// Provides data for the specified data format and index (ISTREAM).
+ /// </summary>
+ /// <param name="dataFormat">Data format.</param>
+ /// <param name="index">Index of data.</param>
+ /// <param name="streamData">Action generating the data.</param>
+ /// <remarks>
+ /// Uses Stream instead of IEnumerable(T) because Stream is more likely
+ /// to be natural for the expected scenarios.
+ /// </remarks>
+ public void SetData(short dataFormat, int index, Action<Stream> streamData)
+ {
+ _dataObjects.Add(
+ new DataObject
+ {
+ FORMATETC = new FORMATETC
+ {
+ cfFormat = dataFormat,
+ ptd = IntPtr.Zero,
+ dwAspect = DVASPECT.DVASPECT_CONTENT,
+ lindex = index,
+ tymed = TYMED.TYMED_ISTREAM
+ },
+ GetData = () =>
+ {
+ // Create IStream for data
+ var ptr = IntPtr.Zero;
+ var iStream = NativeMethods.CreateStreamOnHGlobal(IntPtr.Zero, true);
+ if (streamData != null)
+ {
+ // Wrap in a .NET-friendly Stream and call provided code to fill it
+ using (var stream = new IStreamWrapper(iStream))
+ {
+ streamData(stream);
+ }
+ }
+ // Return an IntPtr for the IStream
+ ptr = Marshal.GetComInterfaceForObject(iStream, typeof(IStream));
+ Marshal.ReleaseComObject(iStream);
+ return new Tuple<IntPtr, int>(ptr, NativeMethods.S_OK);
+ },
+ });
+ }
+
+ /// <summary>
+ /// Provides data for the specified data format (FILEGROUPDESCRIPTOR/FILEDESCRIPTOR)
+ /// </summary>
+ /// <param name="fileDescriptors">Collection of virtual files.</param>
+ public void SetData(IEnumerable<FileDescriptor> fileDescriptors)
+ {
+ // Prepare buffer
+ var bytes = new List<byte>();
+ // Add FILEGROUPDESCRIPTOR header
+ bytes.AddRange(StructureBytes(new NativeMethods.FILEGROUPDESCRIPTOR { cItems = (uint)(fileDescriptors.Count()) }));
+ // Add n FILEDESCRIPTORs
+ foreach (var fileDescriptor in fileDescriptors)
+ {
+ // Set required fields
+ var FILEDESCRIPTOR = new NativeMethods.FILEDESCRIPTOR
+ {
+ cFileName = fileDescriptor.Name,
+ };
+ // Set optional timestamp
+ if (fileDescriptor.ChangeTimeUtc.HasValue)
+ {
+ FILEDESCRIPTOR.dwFlags |= NativeMethods.FD_CREATETIME | NativeMethods.FD_WRITESTIME;
+ var changeTime = fileDescriptor.ChangeTimeUtc.Value.ToLocalTime().ToFileTime();
+ var changeTimeFileTime = new System.Runtime.InteropServices.ComTypes.FILETIME
+ {
+ dwLowDateTime = (int)(changeTime & 0xffffffff),
+ dwHighDateTime = (int)(changeTime >> 32),
+ };
+ FILEDESCRIPTOR.ftLastWriteTime = changeTimeFileTime;
+ FILEDESCRIPTOR.ftCreationTime = changeTimeFileTime;
+ }
+ // Set optional length
+ if (fileDescriptor.Length.HasValue)
+ {
+ FILEDESCRIPTOR.dwFlags |= NativeMethods.FD_FILESIZE;
+ FILEDESCRIPTOR.nFileSizeLow = (uint)(fileDescriptor.Length & 0xffffffff);
+ FILEDESCRIPTOR.nFileSizeHigh = (uint)(fileDescriptor.Length >> 32);
+ }
+ // Add structure to buffer
+ bytes.AddRange(StructureBytes(FILEDESCRIPTOR));
+ }
+
+ // Set CFSTR_FILEDESCRIPTORW
+ SetData(FILEDESCRIPTORW, bytes);
+ // Set n CFSTR_FILECONTENTS
+ var index = 0;
+ foreach (var fileDescriptor in fileDescriptors)
+ {
+ SetData(FILECONTENTS, index, fileDescriptor.StreamContents);
+ index++;
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the CFSTR_PASTESUCCEEDED value for the object.
+ /// </summary>
+ public DragDropEffects? PasteSucceeded
+ {
+ get { return GetDropEffect(PASTESUCCEEDED); }
+ set { SetData(PASTESUCCEEDED, BitConverter.GetBytes((UInt32)value)); }
+ }
+
+ /// <summary>
+ /// Gets or sets the CFSTR_PERFORMEDDROPEFFECT value for the object.
+ /// </summary>
+ public DragDropEffects? PerformedDropEffect
+ {
+ get { return GetDropEffect(PERFORMEDDROPEFFECT); }
+ set { SetData(PERFORMEDDROPEFFECT, BitConverter.GetBytes((UInt32)value)); }
+ }
+
+ /// <summary>
+ /// Gets or sets the CFSTR_PREFERREDDROPEFFECT value for the object.
+ /// </summary>
+ public DragDropEffects? PreferredDropEffect
+ {
+ get { return GetDropEffect(PREFERREDDROPEFFECT); }
+ set { SetData(PREFERREDDROPEFFECT, BitConverter.GetBytes((UInt32)value)); }
+ }
+
+ /// <summary>
+ /// Gets the DragDropEffects value (if any) previously set on the object.
+ /// </summary>
+ /// <param name="format">Clipboard format.</param>
+ /// <returns>DragDropEffects value or null.</returns>
+ [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "Method doesn't decrease security.")]
+ private DragDropEffects? GetDropEffect(short format)
+ {
+ // Get the most recent setting
+ var dataObject = _dataObjects
+ .Where(d =>
+ (format == d.FORMATETC.cfFormat) &&
+ (DVASPECT.DVASPECT_CONTENT == d.FORMATETC.dwAspect) &&
+ (TYMED.TYMED_HGLOBAL == d.FORMATETC.tymed))
+ .LastOrDefault();
+ if (null != dataObject)
+ {
+ // Read the value and return it
+ var result = dataObject.GetData();
+ if (NativeMethods.SUCCEEDED(result.Item2))
+ {
+ var ptr = NativeMethods.GlobalLock(result.Item1);
+ if (IntPtr.Zero != ptr)
+ {
+ try
+ {
+ var length = NativeMethods.GlobalSize(ptr).ToInt32();
+ if (4 == length)
+ {
+ var data = new byte[length];
+ Marshal.Copy(ptr, data, 0, length);
+ return (DragDropEffects)(BitConverter.ToUInt32(data, 0));
+ }
+ }
+ finally
+ {
+ NativeMethods.GlobalUnlock(result.Item1);
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ #region IAsyncOperation Members
+ // Explicit interface implementation hides the technical details from users of VirtualFileDataObject.
+
+ /// <summary>
+ /// Called by a drop source to specify whether the data object supports asynchronous data extraction.
+ /// </summary>
+ /// <param name="fDoOpAsync">A Boolean value that is set to VARIANT_TRUE to indicate that an asynchronous operation is supported, or VARIANT_FALSE otherwise.</param>
+ void IAsyncOperation.SetAsyncMode(int fDoOpAsync)
+ {
+ IsAsynchronous = !(NativeMethods.VARIANT_FALSE == fDoOpAsync);
+ }
+
+ /// <summary>
+ /// Called by a drop target to determine whether the data object supports asynchronous data extraction.
+ /// </summary>
+ /// <param name="pfIsOpAsync">A Boolean value that is set to VARIANT_TRUE to indicate that an asynchronous operation is supported, or VARIANT_FALSE otherwise.</param>
+ void IAsyncOperation.GetAsyncMode(out int pfIsOpAsync)
+ {
+ pfIsOpAsync = IsAsynchronous ? NativeMethods.VARIANT_TRUE : NativeMethods.VARIANT_FALSE;
+ }
+
+ /// <summary>
+ /// Called by a drop target to indicate that asynchronous data extraction is starting.
+ /// </summary>
+ /// <param name="pbcReserved">Reserved. Set this value to NULL.</param>
+ void IAsyncOperation.StartOperation(IBindCtx pbcReserved)
+ {
+ _inOperation = true;
+ if (null != _startAction)
+ {
+ _startAction(this);
+ }
+ }
+
+ /// <summary>
+ /// Called by the drop source to determine whether the target is extracting data asynchronously.
+ /// </summary>
+ /// <param name="pfInAsyncOp">Set to VARIANT_TRUE if data extraction is being handled asynchronously, or VARIANT_FALSE otherwise.</param>
+ void IAsyncOperation.InOperation(out int pfInAsyncOp)
+ {
+ pfInAsyncOp = _inOperation ? NativeMethods.VARIANT_TRUE : NativeMethods.VARIANT_FALSE;
+ }
+
+ /// <summary>
+ /// Notifies the data object that that asynchronous data extraction has ended.
+ /// </summary>
+ /// <param name="hResult">An HRESULT value that indicates the outcome of the data extraction. Set to S_OK if successful, or a COM error code otherwise.</param>
+ /// <param name="pbcReserved">Reserved. Set to NULL.</param>
+ /// <param name="dwEffects">A DROPEFFECT value that indicates the result of an optimized move. This should be the same value that would be passed to the data object as a CFSTR_PERFORMEDDROPEFFECT format with a normal data extraction operation.</param>
+ void IAsyncOperation.EndOperation(int hResult, IBindCtx pbcReserved, uint dwEffects)
+ {
+ if (null != _endAction)
+ {
+ _endAction(this);
+ }
+ _inOperation = false;
+ }
+
+ #endregion
+
+ /// <summary>
+ /// Returns the in-memory representation of an interop structure.
+ /// </summary>
+ /// <param name="source">Structure to return.</param>
+ /// <returns>In-memory representation of structure.</returns>
+ [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "Method doesn't decrease security.")]
+ private static IEnumerable<byte> StructureBytes(object source)
+ {
+ // Set up for call to StructureToPtr
+ var size = Marshal.SizeOf(source.GetType());
+ var ptr = Marshal.AllocHGlobal(size);
+ var bytes = new byte[size];
+ try
+ {
+ Marshal.StructureToPtr(source, ptr, false);
+ // Copy marshalled bytes to buffer
+ Marshal.Copy(ptr, bytes, 0, size);
+ }
+ finally
+ {
+ Marshal.FreeHGlobal(ptr);
+ }
+ return bytes;
+ }
+
+ /// <summary>
+ /// Class representing a virtual file for use by drag/drop or the clipboard.
+ /// </summary>
+ [SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible", Justification = "Deliberate to provide obvious coupling.")]
+ public class FileDescriptor
+ {
+ /// <summary>
+ /// Gets or sets the name of the file.
+ /// </summary>
+ public string Name { get; set; }
+
+ /// <summary>
+ /// Gets or sets the (optional) length of the file.
+ /// </summary>
+ public Int64? Length { get; set; }
+
+ /// <summary>
+ /// Gets or sets the (optional) change time of the file.
+ /// </summary>
+ public DateTime? ChangeTimeUtc { get; set; }
+
+ /// <summary>
+ /// Gets or sets an Action that returns the contents of the file.
+ /// </summary>
+ public Action<Stream> StreamContents { get; set; }
+ }
+
+ /// <summary>
+ /// Class representing the result of a SetData call.
+ /// </summary>
+ private class DataObject
+ {
+ /// <summary>
+ /// FORMATETC structure for the data.
+ /// </summary>
+ public FORMATETC FORMATETC { get; set; }
+
+ /// <summary>
+ /// Func returning the data as an IntPtr and an HRESULT success code.
+ /// </summary>
+ public Func<Tuple<IntPtr, int>> GetData { get; set; }
+ }
+
+ /// <summary>
+ /// Represents a 2-tuple, or pair.
+ /// </summary>
+ /// <remarks>
+ /// Minimal implementation of the .NET 4 Tuple class; remove if running on .NET 4.
+ /// </remarks>
+ /// <typeparam name="T1">The type of the tuple's first component.</typeparam>
+ /// <typeparam name="T2">The type of the tuple's second component.</typeparam>
+ private class Tuple<T1, T2>
+ {
+ /// <summary>
+ /// Gets the value of the current Tuple(T1, T2) object's first component.
+ /// </summary>
+ public T1 Item1 { get; private set; }
+
+ /// <summary>
+ /// Gets the value of the current Tuple(T1, T2) object's second component.
+ /// </summary>
+ public T2 Item2 { get; private set; }
+
+ /// <summary>
+ /// Initializes a new instance of the Tuple(T1, T2) class.
+ /// </summary>
+ /// <param name="item1">The value of the tuple's first component.</param>
+ /// <param name="item2">The value of the tuple's second component.</param>
+ public Tuple(T1 item1, T2 item2)
+ {
+ Item1 = item1;
+ Item2 = item2;
+ }
+ }
+
+ /// <summary>
+ /// Simple class that exposes a write-only IStream as a Stream.
+ /// </summary>
+ private class IStreamWrapper : Stream
+ {
+ /// <summary>
+ /// IStream instance being wrapped.
+ /// </summary>
+ private IStream _iStream;
+
+ /// <summary>
+ /// Initializes a new instance of the IStreamWrapper class.
+ /// </summary>
+ /// <param name="iStream">IStream instance to wrap.</param>
+ public IStreamWrapper(IStream iStream)
+ {
+ _iStream = iStream;
+ }
+
+ /// <summary>
+ /// Gets a value indicating whether the current stream supports reading.
+ /// </summary>
+ public override bool CanRead
+ {
+ get { return false; }
+ }
+
+ /// <summary>
+ /// Gets a value indicating whether the current stream supports seeking.
+ /// </summary>
+ public override bool CanSeek
+ {
+ get { return false; }
+ }
+
+ /// <summary>
+ /// Gets a value indicating whether the current stream supports writing.
+ /// </summary>
+ public override bool CanWrite
+ {
+ get { return true; }
+ }
+
+ /// <summary>
+ /// Clears all buffers for this stream and causes any buffered data to be written to the underlying device.
+ /// </summary>
+ public override void Flush()
+ {
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Gets the length in bytes of the stream.
+ /// </summary>
+ public override long Length
+ {
+ get { throw new NotImplementedException(); }
+ }
+
+ /// <summary>
+ /// Gets or sets the position within the current stream.
+ /// </summary>
+ public override long Position
+ {
+ get { throw new NotImplementedException(); }
+ set { throw new NotImplementedException(); }
+ }
+
+ /// <summary>
+ /// Reads a sequence of bytes from the current stream and advances the position within the stream by the number of bytes read.
+ /// </summary>
+ /// <param name="buffer">An array of bytes. When this method returns, the buffer contains the specified byte array with the values between offset and (offset + count - 1) replaced by the bytes read from the current source.</param>
+ /// <param name="offset">The zero-based byte offset in buffer at which to begin storing the data read from the current stream.</param>
+ /// <param name="count">The maximum number of bytes to be read from the current stream.</param>
+ /// <returns>The total number of bytes read into the buffer. This can be less than the number of bytes requested if that many bytes are not currently available, or zero (0) if the end of the stream has been reached.</returns>
+ public override int Read(byte[] buffer, int offset, int count)
+ {
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Sets the position within the current stream.
+ /// </summary>
+ /// <param name="offset">A byte offset relative to the origin parameter.</param>
+ /// <param name="origin">A value of type SeekOrigin indicating the reference point used to obtain the new position.</param>
+ /// <returns>The new position within the current stream.</returns>
+ public override long Seek(long offset, SeekOrigin origin)
+ {
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Sets the length of the current stream.
+ /// </summary>
+ /// <param name="value">The desired length of the current stream in bytes.</param>
+ public override void SetLength(long value)
+ {
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Writes a sequence of bytes to the current stream and advances the current position within this stream by the number of bytes written.
+ /// </summary>
+ /// <param name="buffer">An array of bytes. This method copies count bytes from buffer to the current stream.</param>
+ /// <param name="offset">The zero-based byte offset in buffer at which to begin copying bytes to the current stream.</param>
+ /// <param name="count">The number of bytes to be written to the current stream.</param>
+ public override void Write(byte[] buffer, int offset, int count)
+ {
+ if (offset == 0)
+ {
+ // Optimize common case to avoid creating extra buffers
+ _iStream.Write(buffer, count, IntPtr.Zero);
+ }
+ else
+ {
+ // Easy way to provide the relevant byte[]
+ _iStream.Write(buffer.Skip(offset).ToArray(), count, IntPtr.Zero);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Initiates a drag-and-drop operation.
+ /// </summary>
+ /// <param name="dragSource">A reference to the dependency object that is the source of the data being dragged.</param>
+ /// <param name="dataObject">A data object that contains the data being dragged.</param>
+ /// <param name="allowedEffects">One of the DragDropEffects values that specifies permitted effects of the drag-and-drop operation.</param>
+ /// <returns>One of the DragDropEffects values that specifies the final effect that was performed during the drag-and-drop operation.</returns>
+ /// <remarks>
+ /// Call this method instead of System.Windows.DragDrop.DoDragDrop because this method handles IDataObject better.
+ /// </remarks>
+ [SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "dragSource", Justification = "Parameter is present so the signature matches that of System.Windows.DragDrop.DoDragDrop.")]
+ public static DragDropEffects DoDragDrop(DependencyObject dragSource, System.Runtime.InteropServices.ComTypes.IDataObject dataObject, DragDropEffects allowedEffects)
+ {
+ int[] finalEffect = new int[1];
+ try
+ {
+ NativeMethods.DoDragDrop(dataObject, new DropSource(), (int)allowedEffects, finalEffect);
+ }
+ finally
+ {
+ var virtualFileDataObject = dataObject as VirtualFileDataObject;
+ if ((null != virtualFileDataObject) && !virtualFileDataObject.IsAsynchronous && virtualFileDataObject._inOperation)
+ {
+ // Call the end action and exit the operation
+ if (null != virtualFileDataObject._endAction)
+ {
+ virtualFileDataObject._endAction(virtualFileDataObject);
+ }
+ virtualFileDataObject._inOperation = false;
+ }
+ }
+ return (DragDropEffects)(finalEffect[0]);
+ }
+
+ /// <summary>
+ /// Contains the methods for generating visual feedback to the end user and for canceling or completing the drag-and-drop operation.
+ /// </summary>
+ private class DropSource : NativeMethods.IDropSource
+ {
+ /// <summary>
+ /// Determines whether a drag-and-drop operation should continue.
+ /// </summary>
+ /// <param name="fEscapePressed">Indicates whether the Esc key has been pressed since the previous call to QueryContinueDrag or to DoDragDrop if this is the first call to QueryContinueDrag. A TRUE value indicates the end user has pressed the escape key; a FALSE value indicates it has not been pressed.</param>
+ /// <param name="grfKeyState">The current state of the keyboard modifier keys on the keyboard. Possible values can be a combination of any of the flags MK_CONTROL, MK_SHIFT, MK_ALT, MK_BUTTON, MK_LBUTTON, MK_MBUTTON, and MK_RBUTTON.</param>
+ /// <returns>This method returns S_OK/DRAGDROP_S_DROP/DRAGDROP_S_CANCEL on success.</returns>
+ public int QueryContinueDrag(int fEscapePressed, uint grfKeyState)
+ {
+ var escapePressed = (0 != fEscapePressed);
+ var keyStates = (DragDropKeyStates)grfKeyState;
+ if (escapePressed)
+ {
+ return NativeMethods.DRAGDROP_S_CANCEL;
+ }
+ else if (DragDropKeyStates.None == (keyStates & DragDropKeyStates.LeftMouseButton))
+ {
+ return NativeMethods.DRAGDROP_S_DROP;
+ }
+ return NativeMethods.S_OK;
+ }
+
+ /// <summary>
+ /// Gives visual feedback to an end user during a drag-and-drop operation.
+ /// </summary>
+ /// <param name="dwEffect">The DROPEFFECT value returned by the most recent call to IDropTarget::DragEnter, IDropTarget::DragOver, or IDropTarget::DragLeave. </param>
+ /// <returns>This method returns S_OK on success.</returns>
+ public int GiveFeedback(uint dwEffect)
+ {
+ return NativeMethods.DRAGDROP_S_USEDEFAULTCURSORS;
+ }
+ }
+
+ /// <summary>
+ /// Provides access to Win32-level constants, structures, and functions.
+ /// </summary>
+ private static class NativeMethods
+ {
+ public const int DRAGDROP_S_DROP = 0x00040100;
+ public const int DRAGDROP_S_CANCEL = 0x00040101;
+ public const int DRAGDROP_S_USEDEFAULTCURSORS = 0x00040102;
+ public const int DV_E_DVASPECT = -2147221397;
+ public const int DV_E_FORMATETC = -2147221404;
+ public const int DV_E_TYMED = -2147221399;
+ public const int E_FAIL = -2147467259;
+ public const uint FD_CREATETIME = 0x00000008;
+ public const uint FD_WRITESTIME = 0x00000020;
+ public const uint FD_FILESIZE = 0x00000040;
+ public const int OLE_E_ADVISENOTSUPPORTED = -2147221501;
+ public const int S_OK = 0;
+ public const int S_FALSE = 1;
+ public const int VARIANT_FALSE = 0;
+ public const int VARIANT_TRUE = -1;
+
+ public const string CFSTR_FILECONTENTS = "FileContents";
+ public const string CFSTR_FILEDESCRIPTORW = "FileGroupDescriptorW";
+ public const string CFSTR_PASTESUCCEEDED = "Paste Succeeded";
+ public const string CFSTR_PERFORMEDDROPEFFECT = "Performed DropEffect";
+ public const string CFSTR_PREFERREDDROPEFFECT = "Preferred DropEffect";
+
+ [SuppressMessage("Microsoft.Performance", "CA1815:OverrideEqualsAndOperatorEqualsOnValueTypes", Justification = "Structure exists for interop.")]
+ [StructLayout(LayoutKind.Sequential)]
+ public struct FILEGROUPDESCRIPTOR
+ {
+ public UInt32 cItems;
+ // Followed by 0 or more FILEDESCRIPTORs
+ }
+
+ [SuppressMessage("Microsoft.Performance", "CA1815:OverrideEqualsAndOperatorEqualsOnValueTypes", Justification = "Structure exists for interop.")]
+ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
+ public struct FILEDESCRIPTOR
+ {
+ public UInt32 dwFlags;
+ public Guid clsid;
+ public Int32 sizelcx;
+ public Int32 sizelcy;
+ public Int32 pointlx;
+ public Int32 pointly;
+ public UInt32 dwFileAttributes;
+ public System.Runtime.InteropServices.ComTypes.FILETIME ftCreationTime;
+ public System.Runtime.InteropServices.ComTypes.FILETIME ftLastAccessTime;
+ public System.Runtime.InteropServices.ComTypes.FILETIME ftLastWriteTime;
+ public UInt32 nFileSizeHigh;
+ public UInt32 nFileSizeLow;
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
+ public string cFileName;
+ }
+
+ [ComImport]
+ [Guid("00000121-0000-0000-C000-000000000046")]
+ [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ public interface IDropSource
+ {
+ [PreserveSig]
+ int QueryContinueDrag(int fEscapePressed, uint grfKeyState);
+ [PreserveSig]
+ int GiveFeedback(uint dwEffect);
+ }
+
+ [SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "2#", Justification = "Win32 API.")]
+ [DllImport("shell32.dll")]
+ public static extern int SHCreateStdEnumFmtEtc(uint cfmt, FORMATETC[] afmt, out IEnumFORMATETC ppenumFormatEtc);
+
+ [return: MarshalAs(UnmanagedType.Interface)]
+ [DllImport("ole32.dll", PreserveSig = false)]
+ public static extern IStream CreateStreamOnHGlobal(IntPtr hGlobal, [MarshalAs(UnmanagedType.Bool)] bool fDeleteOnRelease);
+
+ [DllImport("ole32.dll", CharSet = CharSet.Auto, ExactSpelling = true, PreserveSig = false)]
+ public static extern void DoDragDrop(System.Runtime.InteropServices.ComTypes.IDataObject dataObject, IDropSource dropSource, int allowedEffects, int[] finalEffect);
+
+ [DllImport("kernel32.dll")]
+ public static extern IntPtr GlobalLock(IntPtr hMem);
+
+ [return: MarshalAs(UnmanagedType.Bool)]
+ [DllImport("kernel32.dll")]
+ public static extern bool GlobalUnlock(IntPtr hMem);
+
+ [DllImport("kernel32.dll")]
+ public static extern IntPtr GlobalSize(IntPtr handle);
+
+ /// <summary>
+ /// Returns true iff the HRESULT is a success code.
+ /// </summary>
+ /// <param name="hr">HRESULT to check.</param>
+ /// <returns>True iff a success code.</returns>
+ public static bool SUCCEEDED(int hr)
+ {
+ return (0 <= hr);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Definition of the IAsyncOperation COM interface.
+ /// </summary>
+ /// <remarks>
+ /// Pseudo-public because VirtualFileDataObject implements it.
+ /// </remarks>
+ [ComImport]
+ [Guid("3D8B0590-F691-11d2-8EA9-006097DF5BD4")]
+ [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ internal interface IAsyncOperation
+ {
+ void SetAsyncMode([In] Int32 fDoOpAsync);
+ void GetAsyncMode([Out] out Int32 pfIsOpAsync);
+ void StartOperation([In] IBindCtx pbcReserved);
+ void InOperation([Out] out Int32 pfInAsyncOp);
+ void EndOperation([In] Int32 hResult, [In] IBindCtx pbcReserved, [In] UInt32 dwEffects);
+ }
+}
diff --git a/Software/Visual_Studio/Tango.Integration/ExternalBridge/Network/Information/GetMachineInformationRequest.cs b/Software/Visual_Studio/Tango.Integration/ExternalBridge/Network/Information/GetMachineInformationRequest.cs
new file mode 100644
index 000000000..38c06b024
--- /dev/null
+++ b/Software/Visual_Studio/Tango.Integration/ExternalBridge/Network/Information/GetMachineInformationRequest.cs
@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tango.Integration.ExternalBridge.Network.Information
+{
+ public class GetMachineInformationRequest
+ {
+ }
+}
diff --git a/Software/Visual_Studio/Tango.Integration/ExternalBridge/Network/Information/GetMachineInformationResponse.cs b/Software/Visual_Studio/Tango.Integration/ExternalBridge/Network/Information/GetMachineInformationResponse.cs
new file mode 100644
index 000000000..5ae1aa440
--- /dev/null
+++ b/Software/Visual_Studio/Tango.Integration/ExternalBridge/Network/Information/GetMachineInformationResponse.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tango.Integration.ExternalBridge.Network.Information
+{
+ public class GetMachineInformationResponse
+ {
+ public InformationPackage Package { get; set; }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.Integration/ExternalBridge/Network/Information/InformationPackage.cs b/Software/Visual_Studio/Tango.Integration/ExternalBridge/Network/Information/InformationPackage.cs
new file mode 100644
index 000000000..afa23492e
--- /dev/null
+++ b/Software/Visual_Studio/Tango.Integration/ExternalBridge/Network/Information/InformationPackage.cs
@@ -0,0 +1,19 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Tango.SystemInfo;
+
+namespace Tango.Integration.ExternalBridge.Network.Information
+{
+ public class InformationPackage
+ {
+ public List<SystemObjectsCollection> System { get; set; }
+
+ public InformationPackage()
+ {
+ System = new List<SystemObjectsCollection>();
+ }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.Integration/ExternalBridge/Network/Performance/PerformancePackage.cs b/Software/Visual_Studio/Tango.Integration/ExternalBridge/Network/Performance/PerformancePackage.cs
new file mode 100644
index 000000000..56921e0c5
--- /dev/null
+++ b/Software/Visual_Studio/Tango.Integration/ExternalBridge/Network/Performance/PerformancePackage.cs
@@ -0,0 +1,25 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tango.Integration.ExternalBridge.Network.Performance
+{
+ public class PerformancePackage
+ {
+ public int CPU { get; set; }
+ public int ApplicationCPU { get; set; }
+
+ public int RAM { get; set; }
+ public int ApplicationRAM { get; set; }
+ public int MaxRAM { get; set; }
+
+ public int Temperature { get; set; }
+
+ public int AvailableDiskSpace { get; set; }
+ public int DiskCapacity { get; set; }
+
+ public DateTime DateTime { get; set; }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.Integration/ExternalBridge/Network/Performance/StartPerformanceUpdatesRequest.cs b/Software/Visual_Studio/Tango.Integration/ExternalBridge/Network/Performance/StartPerformanceUpdatesRequest.cs
new file mode 100644
index 000000000..2c036e937
--- /dev/null
+++ b/Software/Visual_Studio/Tango.Integration/ExternalBridge/Network/Performance/StartPerformanceUpdatesRequest.cs
@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tango.Integration.ExternalBridge.Network.Performance
+{
+ public class StartPerformanceUpdatesRequest
+ {
+ }
+}
diff --git a/Software/Visual_Studio/Tango.Integration/ExternalBridge/Network/Performance/StartPerformanceUpdatesResponse.cs b/Software/Visual_Studio/Tango.Integration/ExternalBridge/Network/Performance/StartPerformanceUpdatesResponse.cs
new file mode 100644
index 000000000..8eb22bdef
--- /dev/null
+++ b/Software/Visual_Studio/Tango.Integration/ExternalBridge/Network/Performance/StartPerformanceUpdatesResponse.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tango.Integration.ExternalBridge.Network.Performance
+{
+ public class StartPerformanceUpdatesResponse
+ {
+ public PerformancePackage Package { get; set; }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.RemoteDesktop/Network/RemoteDesktopCommand.cs b/Software/Visual_Studio/Tango.RemoteDesktop/Network/RemoteDesktopCommand.cs
new file mode 100644
index 000000000..8283770ad
--- /dev/null
+++ b/Software/Visual_Studio/Tango.RemoteDesktop/Network/RemoteDesktopCommand.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tango.RemoteDesktop.Network
+{
+ public enum RemoteDesktopCommand
+ {
+ HideAndOpenShell,
+ RestartApplication,
+ }
+}
diff --git a/Software/Visual_Studio/Tango.RemoteDesktop/Network/RemoteDesktopCommandRequest.cs b/Software/Visual_Studio/Tango.RemoteDesktop/Network/RemoteDesktopCommandRequest.cs
new file mode 100644
index 000000000..65f500d7c
--- /dev/null
+++ b/Software/Visual_Studio/Tango.RemoteDesktop/Network/RemoteDesktopCommandRequest.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tango.RemoteDesktop.Network
+{
+ public class RemoteDesktopCommandRequest
+ {
+ public RemoteDesktopCommand Command { get; set; }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.RemoteDesktop/Network/RemoteDesktopCommandResponse.cs b/Software/Visual_Studio/Tango.RemoteDesktop/Network/RemoteDesktopCommandResponse.cs
new file mode 100644
index 000000000..1931dc092
--- /dev/null
+++ b/Software/Visual_Studio/Tango.RemoteDesktop/Network/RemoteDesktopCommandResponse.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tango.RemoteDesktop.Network
+{
+ public class RemoteDesktopCommandResponse
+ {
+
+ }
+}
diff --git a/Software/Visual_Studio/Tango.SharedUI/Binding/BindingEventArgs.cs b/Software/Visual_Studio/Tango.SharedUI/Binding/BindingEventArgs.cs
new file mode 100644
index 000000000..06ea61019
--- /dev/null
+++ b/Software/Visual_Studio/Tango.SharedUI/Binding/BindingEventArgs.cs
@@ -0,0 +1,39 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Media;
+
+namespace Tango.SharedUI
+{
+ /// <summary>
+ /// Represents the <see cref="BindingEventContainer.ValueChanged"/> event arguments.
+ /// </summary>
+ /// <seealso cref="System.EventArgs" />
+ public class BindingEventArgs : EventArgs
+ {
+ internal Action _renewAction;
+
+ /// <summary>
+ /// Gets or sets the element.
+ /// </summary>
+ public DependencyObject DependencyObject { get; set; }
+
+ /// <summary>
+ /// Gets or sets the binding property.
+ /// </summary>
+ public BindingProperty BindingProperty { get; set; }
+
+ /// <summary>
+ /// Gets or sets the value.
+ /// </summary>
+ public Object Value { get; set; }
+
+ public void Renew()
+ {
+ _renewAction?.Invoke();
+ }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.SharedUI/Binding/BindingEventContainer.cs b/Software/Visual_Studio/Tango.SharedUI/Binding/BindingEventContainer.cs
new file mode 100644
index 000000000..a74a9b085
--- /dev/null
+++ b/Software/Visual_Studio/Tango.SharedUI/Binding/BindingEventContainer.cs
@@ -0,0 +1,95 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Data;
+using System.Windows.Media;
+
+namespace Tango.SharedUI
+{
+ /// <summary>
+ /// Represents a binding event container.
+ /// </summary>
+ /// <seealso cref="System.Windows.DependencyObject" />
+ public class BindingEventContainer : DependencyObject
+ {
+ private Action _renewAction;
+
+ /// <summary>
+ /// Occurs when the dependency property value has changed.
+ /// </summary>
+ public event EventHandler<BindingEventArgs> ValueChanged;
+
+ /// <summary>
+ /// Gets or sets the value.
+ /// </summary>
+ public Object Value
+ {
+ get { return (Object)GetValue(ValueProperty); }
+ set { SetValue(ValueProperty, value); }
+ }
+ public static readonly DependencyProperty ValueProperty =
+ DependencyProperty.Register("Value", typeof(Object), typeof(BindingEventContainer), new PropertyMetadata(null, (d, e) => (d as BindingEventContainer).OnValueChanged()));
+
+ /// <summary>
+ /// Gets or sets the binding property.
+ /// </summary>
+ public BindingProperty BindingProperty { get; set; }
+
+ /// <summary>
+ /// Gets or sets the dependency object.
+ /// </summary>
+ public DependencyObject DependencyObject { get; set; }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="BindingEventContainer"/> class.
+ /// </summary>
+ /// <param name="takeElement">The dependency object.</param>
+ /// <param name="bindingProperty">The binding property.</param>
+ public BindingEventContainer(DependencyObject dependencyObject, BindingProperty bindingProperty)
+ {
+ DependencyObject = dependencyObject;
+ BindingProperty = bindingProperty;
+ }
+
+ /// <summary>
+ /// Called when the value has been changed
+ /// </summary>
+ protected virtual void OnValueChanged()
+ {
+ ValueChanged?.Invoke(this, new BindingEventArgs()
+ {
+ BindingProperty = BindingProperty,
+ DependencyObject = DependencyObject,
+ Value = Value,
+ _renewAction = _renewAction
+ });
+ }
+
+ /// <summary>
+ /// Generates a new <see cref="BindingEventContainer"/> for the specified Take element and binding property.
+ /// </summary>
+ /// <param name="dependencyObject">The take element.</param>
+ /// <param name="dependencyProperty">The binding property.</param>
+ /// <returns></returns>
+ public static BindingEventContainer Generate(DependencyObject dependencyObject, DependencyProperty dependencyProperty)
+ {
+ BindingEventContainer container = new BindingEventContainer(dependencyObject, new BindingProperty(dependencyProperty));
+
+ container._renewAction = () =>
+ {
+ Binding binding = new Binding();
+ binding.Mode = BindingMode.OneWay;
+ binding.Source = dependencyObject;
+ binding.Path = new PropertyPath(dependencyProperty);
+ BindingOperations.SetBinding(container, BindingEventContainer.ValueProperty, binding);
+ };
+
+ container._renewAction.Invoke();
+
+ return container;
+ }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.SharedUI/Binding/BindingProperty.cs b/Software/Visual_Studio/Tango.SharedUI/Binding/BindingProperty.cs
new file mode 100644
index 000000000..79d4f1c71
--- /dev/null
+++ b/Software/Visual_Studio/Tango.SharedUI/Binding/BindingProperty.cs
@@ -0,0 +1,38 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+
+namespace Tango.SharedUI
+{
+ /// <summary>
+ /// Represents a binding property.
+ /// </summary>
+ public class BindingProperty
+ {
+ /// <summary>
+ /// Gets or sets the dependency property.
+ /// </summary>
+ public DependencyProperty DependencyProperty { get; set; }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="BindingProperty"/> class.
+ /// </summary>
+ public BindingProperty()
+ {
+
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="BindingProperty"/> class.
+ /// </summary>
+ /// <param name="dp">The dependency property.</param>
+ /// <param name="mode">The mode.</param>
+ public BindingProperty(DependencyProperty dp) : this()
+ {
+ DependencyProperty = dp;
+ }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.SharedUI/Converters/DateTimeUtcToLocalDateTime.cs b/Software/Visual_Studio/Tango.SharedUI/Converters/DateTimeUtcToLocalDateTime.cs
new file mode 100644
index 000000000..7ce4a6532
--- /dev/null
+++ b/Software/Visual_Studio/Tango.SharedUI/Converters/DateTimeUtcToLocalDateTime.cs
@@ -0,0 +1,30 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Data;
+
+namespace Tango.SharedUI.Converters
+{
+ public class DateTimeUtcToLocalDateTime : IValueConverter
+ {
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ try
+ {
+ return ((DateTime)value).ToLocalTime();
+ }
+ catch
+ {
+ return value;
+ }
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.SharedUI/Converters/StringToTitleCaseConverter.cs b/Software/Visual_Studio/Tango.SharedUI/Converters/StringToTitleCaseConverter.cs
new file mode 100644
index 000000000..6bafd1fd0
--- /dev/null
+++ b/Software/Visual_Studio/Tango.SharedUI/Converters/StringToTitleCaseConverter.cs
@@ -0,0 +1,30 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Data;
+
+namespace Tango.SharedUI.Converters
+{
+ public class StringToTitleCaseConverter : IValueConverter
+ {
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ if (value != null)
+ {
+ return value.ToString().ToTitleCase();
+ }
+ else
+ {
+ return value;
+ }
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.SystemInfo/Connection.cs b/Software/Visual_Studio/Tango.SystemInfo/Connection.cs
new file mode 100644
index 000000000..3e4e1e73d
--- /dev/null
+++ b/Software/Visual_Studio/Tango.SystemInfo/Connection.cs
@@ -0,0 +1,83 @@
+using System;
+using System.Management;
+using System.Collections.Generic;
+using System.Text;
+
+//Tango.SystemInfo
+namespace Tango.SystemInfo
+{
+ class Connection
+ {
+ ManagementScope connectionScope;
+ ConnectionOptions options;
+
+ #region "properties"
+ public ManagementScope GetConnectionScope
+ {
+ get { return connectionScope; }
+ }
+ public ConnectionOptions GetOptions
+ {
+ get { return options; }
+ }
+ #endregion
+
+ #region "static helpers"
+ public static ConnectionOptions SetConnectionOptions()
+ {
+ ConnectionOptions options = new ConnectionOptions();
+ options.Impersonation = ImpersonationLevel.Impersonate;
+ options.Authentication = AuthenticationLevel.Default;
+ options.EnablePrivileges = true;
+ return options;
+ }
+
+ public static ManagementScope SetConnectionScope(string machineName,
+ ConnectionOptions options)
+ {
+ ManagementScope connectScope = new ManagementScope();
+ connectScope.Path = new ManagementPath(@"\\" + machineName + @"\root\CIMV2");
+ connectScope.Options = options;
+
+ try
+ {
+ connectScope.Connect();
+ }
+ catch (ManagementException e)
+ {
+ Console.WriteLine("An Error Occurred: " + e.Message.ToString());
+ }
+ return connectScope;
+ }
+ #endregion
+
+ #region "constructors"
+ public Connection()
+ {
+ EstablishConnection(null, null, null, Environment.MachineName);
+ }
+
+ public Connection(string userName,
+ string password,
+ string domain,
+ string machineName)
+ {
+ EstablishConnection(userName, password, domain, machineName);
+ }
+ #endregion
+
+ #region "private helpers"
+ private void EstablishConnection(string userName, string password, string domain, string machineName)
+ {
+ options = Connection.SetConnectionOptions();
+ if (domain != null || userName != null)
+ {
+ options.Username = domain + "\\" + userName;
+ options.Password = password;
+ }
+ connectionScope = Connection.SetConnectionScope(machineName, options);
+ }
+ #endregion
+
+ }
+}
diff --git a/Software/Visual_Studio/Tango.SystemInfo/IWMI.cs b/Software/Visual_Studio/Tango.SystemInfo/IWMI.cs
new file mode 100644
index 000000000..06252731f
--- /dev/null
+++ b/Software/Visual_Studio/Tango.SystemInfo/IWMI.cs
@@ -0,0 +1,11 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Tango.SystemInfo
+{
+ interface IWMI
+ {
+ IList<SystemObject> GetHardwareInfoList();
+ }
+}
diff --git a/Software/Visual_Studio/Tango.SystemInfo/Properties/AssemblyInfo.cs b/Software/Visual_Studio/Tango.SystemInfo/Properties/AssemblyInfo.cs
new file mode 100644
index 000000000..1ff929bce
--- /dev/null
+++ b/Software/Visual_Studio/Tango.SystemInfo/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("Tango.SystemInfo")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("Tango.SystemInfo")]
+[assembly: AssemblyCopyright("Copyright © 2020")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("997a961c-beda-4b56-aa0f-c39e532f7ffa")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/Software/Visual_Studio/Tango.SystemInfo/SystemObject.cs b/Software/Visual_Studio/Tango.SystemInfo/SystemObject.cs
new file mode 100644
index 000000000..be1c74fe5
--- /dev/null
+++ b/Software/Visual_Studio/Tango.SystemInfo/SystemObject.cs
@@ -0,0 +1,31 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Tango.SystemInfo
+{
+ public class SystemObject
+ {
+ public String Name { get; set; }
+ public List<SystemObjectProperty> Properties { get; set; }
+
+ public SystemObject()
+ {
+ Properties = new List<SystemObjectProperty>();
+ }
+
+ public override string ToString()
+ {
+ String msg = String.Empty;
+
+ msg = $"Name: {Name}\n";
+
+ foreach (var prop in Properties)
+ {
+ msg += $"{prop.Name}: {prop.Value}\n";
+ }
+
+ return msg;
+ }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.SystemInfo/SystemObjectProperty.cs b/Software/Visual_Studio/Tango.SystemInfo/SystemObjectProperty.cs
new file mode 100644
index 000000000..c2d04e2a5
--- /dev/null
+++ b/Software/Visual_Studio/Tango.SystemInfo/SystemObjectProperty.cs
@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Tango.SystemInfo
+{
+ public class SystemObjectProperty
+ {
+ public String Name { get; set; }
+ public String Value { get; set; }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.SystemInfo/SystemObjectsCollection.cs b/Software/Visual_Studio/Tango.SystemInfo/SystemObjectsCollection.cs
new file mode 100644
index 000000000..0596b5745
--- /dev/null
+++ b/Software/Visual_Studio/Tango.SystemInfo/SystemObjectsCollection.cs
@@ -0,0 +1,119 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tango.SystemInfo
+{
+ public class SystemObjectsCollection
+ {
+ public String Name { get; set; }
+
+ public List<SystemObject> Objects { get; set; }
+
+ public SystemObjectsCollection()
+ {
+ Objects = new List<SystemObject>();
+ }
+
+ public override string ToString()
+ {
+ String str = String.Empty;
+
+ str += Name + "\n\n";
+
+ if (Objects != null)
+ {
+ foreach (var obj in Objects)
+ {
+ str += obj.ToString();
+ str += "\n";
+ }
+ }
+
+ str += "\n";
+
+ return str;
+ }
+
+ public static List<SystemObjectsCollection> Create()
+ {
+ List<SystemObjectsCollection> list = new List<SystemObjectsCollection>();
+
+ Connection wmiConnection = new Connection();
+
+ SystemObjectsCollection os = new SystemObjectsCollection() { Name = "Operation System" };
+ Win32_OperatingSystem osConnection = new Win32_OperatingSystem(wmiConnection);
+ os.Objects = osConnection.GetHardwareInfoList().ToList();
+ list.Add(os);
+
+ SystemObjectsCollection board = new SystemObjectsCollection() { Name = "Board" };
+ Win32_BaseBoard boardConnection = new Win32_BaseBoard(wmiConnection);
+ board.Objects = boardConnection.GetHardwareInfoList().ToList();
+ list.Add(board);
+
+ SystemObjectsCollection bios = new SystemObjectsCollection() { Name = "BIOS" };
+ Win32_BIOS biosConnection = new Win32_BIOS(wmiConnection);
+ bios.Objects = biosConnection.GetHardwareInfoList().ToList();
+ list.Add(bios);
+
+ SystemObjectsCollection processor = new SystemObjectsCollection() { Name = "Processor" };
+ Win32_Processor processorConnection = new Win32_Processor(wmiConnection);
+ processor.Objects = processorConnection.GetHardwareInfoList().ToList();
+ list.Add(processor);
+
+ SystemObjectsCollection memory = new SystemObjectsCollection() { Name = "Memory" };
+ Win32_PhysicalMemory memoryConnection = new Win32_PhysicalMemory(wmiConnection);
+ memory.Objects = memoryConnection.GetHardwareInfoList().ToList();
+ list.Add(memory);
+
+ SystemObjectsCollection video = new SystemObjectsCollection() { Name = "Video Controller" };
+ Win32_VideoController videoConnection = new Win32_VideoController(wmiConnection);
+ video.Objects = videoConnection.GetHardwareInfoList().ToList();
+ list.Add(video);
+
+ SystemObjectsCollection disk = new SystemObjectsCollection() { Name = "Disk Drives" };
+ Win32_DiskDrive diskConnection = new Win32_DiskDrive(wmiConnection);
+ disk.Objects = diskConnection.GetHardwareInfoList().ToList();
+ list.Add(disk);
+
+ SystemObjectsCollection network = new SystemObjectsCollection() { Name = "Network" };
+ Win32_NetworkAdapter networkConnection = new Win32_NetworkAdapter(wmiConnection);
+ network.Objects = networkConnection.GetHardwareInfoList().ToList();
+ list.Add(network);
+
+ SystemObjectsCollection sound = new SystemObjectsCollection() { Name = "Sound" };
+ Win32_SoundDevice soundConnection = new Win32_SoundDevice(wmiConnection);
+ sound.Objects = soundConnection.GetHardwareInfoList().ToList();
+ list.Add(sound);
+
+ SystemObjectsCollection serial = new SystemObjectsCollection() { Name = "Serial Ports" };
+ Win32_SerialPort serialConnection = new Win32_SerialPort(wmiConnection);
+ serial.Objects = serialConnection.GetHardwareInfoList().ToList();
+ list.Add(serial);
+
+ SystemObjectsCollection fan = new SystemObjectsCollection() { Name = "Fan" };
+ Win32_Fan fanConnection = new Win32_Fan(wmiConnection);
+ fan.Objects = fanConnection.GetHardwareInfoList().ToList();
+ list.Add(fan);
+
+ SystemObjectsCollection temp = new SystemObjectsCollection() { Name = "Temperature" };
+ Win32_TemperatureProbe tempConnection = new Win32_TemperatureProbe(wmiConnection);
+ temp.Objects = tempConnection.GetHardwareInfoList().ToList();
+ list.Add(temp);
+
+ SystemObjectsCollection power = new SystemObjectsCollection() { Name = "Power Supply" };
+ Win32_UninterruptiblePowerSupply powerConnection = new Win32_UninterruptiblePowerSupply(wmiConnection);
+ power.Objects = powerConnection.GetHardwareInfoList().ToList();
+ list.Add(power);
+
+ SystemObjectsCollection voltage = new SystemObjectsCollection() { Name = "Voltage" };
+ Win32_VoltageProbe voltageConnection = new Win32_VoltageProbe(wmiConnection);
+ voltage.Objects = voltageConnection.GetHardwareInfoList().ToList();
+ list.Add(voltage);
+
+ return list;
+ }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.SystemInfo/Tango.SystemInfo.csproj b/Software/Visual_Studio/Tango.SystemInfo/Tango.SystemInfo.csproj
new file mode 100644
index 000000000..90cf14c29
--- /dev/null
+++ b/Software/Visual_Studio/Tango.SystemInfo/Tango.SystemInfo.csproj
@@ -0,0 +1,102 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <ProjectGuid>{997A961C-BEDA-4B56-AA0F-C39E532F7FFA}</ProjectGuid>
+ <OutputType>Library</OutputType>
+ <AppDesignerFolder>Properties</AppDesignerFolder>
+ <RootNamespace>Tango.SystemInfo</RootNamespace>
+ <AssemblyName>Tango.SystemInfo</AssemblyName>
+ <TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
+ <FileAlignment>512</FileAlignment>
+ <Deterministic>true</Deterministic>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>bin\Debug\</OutputPath>
+ <DefineConstants>DEBUG;TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <DebugType>pdbonly</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>bin\Release\</OutputPath>
+ <DefineConstants>TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <ItemGroup>
+ <Reference Include="System" />
+ <Reference Include="System.Core" />
+ <Reference Include="System.Management" />
+ <Reference Include="System.Xml.Linq" />
+ <Reference Include="System.Data.DataSetExtensions" />
+ <Reference Include="Microsoft.CSharp" />
+ <Reference Include="System.Data" />
+ <Reference Include="System.Net.Http" />
+ <Reference Include="System.Xml" />
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="Connection.cs" />
+ <Compile Include="SystemObject.cs" />
+ <Compile Include="SystemObjectProperty.cs" />
+ <Compile Include="SystemObjectsCollection.cs" />
+ <Compile Include="WMIReader.cs" />
+ <Compile Include="XMLConfig.cs" />
+ <Compile Include="IWMI.cs" />
+ <Compile Include="Win32_BaseBoard.cs" />
+ <Compile Include="Win32_Battery.cs" />
+ <Compile Include="Win32_BIOS.cs" />
+ <Compile Include="Win32_Bus.cs" />
+ <Compile Include="Win32_CDROMDrive.cs" />
+ <Compile Include="Win32_DiskDrive.cs" />
+ <Compile Include="Win32_DMAChannel.cs" />
+ <Compile Include="Win32_Fan.cs" />
+ <Compile Include="Win32_FloppyController.cs" />
+ <Compile Include="Win32_FloppyDrive.cs" />
+ <Compile Include="Win32_IDEController.cs" />
+ <Compile Include="Win32_IRQResource.cs" />
+ <Compile Include="Win32_Keyboard.cs" />
+ <Compile Include="Win32_MemoryDevice.cs" />
+ <Compile Include="Win32_NetworkAdapter.cs" />
+ <Compile Include="Win32_NetworkAdapterConfiguration.cs" />
+ <Compile Include="Win32_OnBoardDevice.cs" />
+ <Compile Include="Win32_OperatingSystem.cs" />
+ <Compile Include="Win32_ParallelPort.cs" />
+ <Compile Include="Win32_PCMCIController.cs" />
+ <Compile Include="Win32_PhysicalMedia.cs" />
+ <Compile Include="Win32_PhysicalMemory.cs" />
+ <Compile Include="Win32_PortConnector.cs" />
+ <Compile Include="Win32_PortResource.cs" />
+ <Compile Include="Win32_POTSModem.cs" />
+ <Compile Include="Win32_Processor.cs" />
+ <Compile Include="Win32_SCSIController.cs" />
+ <Compile Include="Win32_SerialPort.cs" />
+ <Compile Include="Win32_SerialPortConfiguration.cs" />
+ <Compile Include="Win32_SoundDevice.cs" />
+ <Compile Include="Win32_SystemEnclosure.cs" />
+ <Compile Include="Win32_TapeDrive.cs" />
+ <Compile Include="Win32_TemperatureProbe.cs" />
+ <Compile Include="Win32_UninterruptiblePowerSupply.cs" />
+ <Compile Include="Win32_USBController.cs" />
+ <Compile Include="Win32_USBHub.cs" />
+ <Compile Include="Win32_VideoController.cs" />
+ <Compile Include="Win32_VoltageProbe.cs" />
+ <Compile Include="Properties\AssemblyInfo.cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <EmbeddedResource Include="settings.xml" />
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\Tango.Core\Tango.Core.csproj">
+ <Project>{a34ee0f0-649d-41c8-8489-b6f1cc6924ee}</Project>
+ <Name>Tango.Core</Name>
+ </ProjectReference>
+ </ItemGroup>
+ <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+</Project> \ No newline at end of file
diff --git a/Software/Visual_Studio/Tango.SystemInfo/WMIReader.cs b/Software/Visual_Studio/Tango.SystemInfo/WMIReader.cs
new file mode 100644
index 000000000..d7d909089
--- /dev/null
+++ b/Software/Visual_Studio/Tango.SystemInfo/WMIReader.cs
@@ -0,0 +1,60 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Management;
+
+namespace Tango.SystemInfo
+{
+ class WMIReader
+ {
+ public static IList<SystemObject> GetPropertyValues(Connection WMIConnection,
+ string SelectQuery,
+ string className)
+ {
+ List<SystemObject> hardwareList = new List<SystemObject>();
+
+ ManagementScope connectionScope = WMIConnection.GetConnectionScope;
+ List<string> alProperties = new List<string>();
+ SelectQuery msQuery = new SelectQuery(SelectQuery);
+ ManagementObjectSearcher searchProcedure = new ManagementObjectSearcher(connectionScope, msQuery);
+
+ try
+ {
+ foreach (ManagementObject item in searchProcedure.Get())
+ {
+ SystemObject hardware = new SystemObject();
+
+ try
+ {
+ hardware.Name = item["Name"].ToString();
+ }
+ catch
+ {
+ hardware.Name = item.ToString();
+ }
+
+ hardwareList.Add(hardware);
+
+ foreach (string property in XMLConfig.GetSettings(className))
+ {
+ try
+ {
+ hardware.Properties.Add(new SystemObjectProperty()
+ {
+ Name = property,
+ Value = item[property].ToString()
+ });
+ }
+ catch (SystemException) { /* ignore error */ }
+ }
+ }
+ }
+ catch (ManagementException e)
+ {
+ /* Do Nothing */
+ }
+
+ return hardwareList;
+ }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.SystemInfo/Win32_BIOS.cs b/Software/Visual_Studio/Tango.SystemInfo/Win32_BIOS.cs
new file mode 100644
index 000000000..f86c0d5ca
--- /dev/null
+++ b/Software/Visual_Studio/Tango.SystemInfo/Win32_BIOS.cs
@@ -0,0 +1,25 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Tango.SystemInfo
+{
+ class Win32_BIOS : IWMI
+ {
+ Connection WMIConnection;
+
+ public Win32_BIOS(Connection WMIConnection)
+ {
+ this.WMIConnection = WMIConnection;
+ }
+ public IList<SystemObject> GetHardwareInfoList()
+ {
+ string className = System.Text.RegularExpressions.Regex.Match(
+ this.GetType().ToString(), "Win32_.*").Value;
+
+ return WMIReader.GetPropertyValues(WMIConnection,
+ "SELECT * FROM " + className,
+ className);
+ }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.SystemInfo/Win32_BaseBoard.cs b/Software/Visual_Studio/Tango.SystemInfo/Win32_BaseBoard.cs
new file mode 100644
index 000000000..f9030ff54
--- /dev/null
+++ b/Software/Visual_Studio/Tango.SystemInfo/Win32_BaseBoard.cs
@@ -0,0 +1,27 @@
+using System;
+using System.Management;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Tango.SystemInfo
+{
+ class Win32_BaseBoard: IWMI
+ {
+ Connection WMIConnection;
+
+ public Win32_BaseBoard(Connection WMIConnection)
+ {
+ this.WMIConnection = WMIConnection;
+ }
+
+ public IList<SystemObject> GetHardwareInfoList()
+ {
+ string className = System.Text.RegularExpressions.Regex.Match(
+ this.GetType().ToString(), "Win32_.*").Value;
+
+ return WMIReader.GetPropertyValues(WMIConnection,
+ "SELECT * FROM " + className,
+ className);
+ }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.SystemInfo/Win32_Battery.cs b/Software/Visual_Studio/Tango.SystemInfo/Win32_Battery.cs
new file mode 100644
index 000000000..91700d678
--- /dev/null
+++ b/Software/Visual_Studio/Tango.SystemInfo/Win32_Battery.cs
@@ -0,0 +1,25 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Tango.SystemInfo
+{
+ class Win32_Battery : IWMI
+ {
+ Connection WMIConnection;
+
+ public Win32_Battery(Connection WMIConnection)
+ {
+ this.WMIConnection = WMIConnection;
+ }
+ public IList<SystemObject> GetHardwareInfoList()
+ {
+ string className = System.Text.RegularExpressions.Regex.Match(
+ this.GetType().ToString(), "Win32_.*").Value;
+
+ return WMIReader.GetPropertyValues(WMIConnection,
+ "SELECT * FROM " + className,
+ className);
+ }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.SystemInfo/Win32_Bus.cs b/Software/Visual_Studio/Tango.SystemInfo/Win32_Bus.cs
new file mode 100644
index 000000000..8aba00430
--- /dev/null
+++ b/Software/Visual_Studio/Tango.SystemInfo/Win32_Bus.cs
@@ -0,0 +1,25 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Tango.SystemInfo
+{
+ class Win32_Bus : IWMI
+ {
+ Connection WMIConnection;
+
+ public Win32_Bus(Connection WMIConnection)
+ {
+ this.WMIConnection = WMIConnection;
+ }
+ public IList<SystemObject> GetHardwareInfoList()
+ {
+ string className = System.Text.RegularExpressions.Regex.Match(
+ this.GetType().ToString(), "Win32_.*").Value;
+
+ return WMIReader.GetPropertyValues(WMIConnection,
+ "SELECT * FROM " + className,
+ className);
+ }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.SystemInfo/Win32_CDROMDrive.cs b/Software/Visual_Studio/Tango.SystemInfo/Win32_CDROMDrive.cs
new file mode 100644
index 000000000..e6eaa08a9
--- /dev/null
+++ b/Software/Visual_Studio/Tango.SystemInfo/Win32_CDROMDrive.cs
@@ -0,0 +1,25 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Tango.SystemInfo
+{
+ class Win32_CDROMDrive : IWMI
+ {
+ Connection WMIConnection;
+
+ public Win32_CDROMDrive(Connection WMIConnection)
+ {
+ this.WMIConnection = WMIConnection;
+ }
+ public IList<SystemObject> GetHardwareInfoList()
+ {
+ string className = System.Text.RegularExpressions.Regex.Match(
+ this.GetType().ToString(), "Win32_.*").Value;
+
+ return WMIReader.GetPropertyValues(WMIConnection,
+ "SELECT * FROM " + className,
+ className);
+ }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.SystemInfo/Win32_DMAChannel.cs b/Software/Visual_Studio/Tango.SystemInfo/Win32_DMAChannel.cs
new file mode 100644
index 000000000..12e0228fb
--- /dev/null
+++ b/Software/Visual_Studio/Tango.SystemInfo/Win32_DMAChannel.cs
@@ -0,0 +1,25 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Tango.SystemInfo
+{
+ class Win32_DMAChannel : IWMI
+ {
+ Connection WMIConnection;
+
+ public Win32_DMAChannel(Connection WMIConnection)
+ {
+ this.WMIConnection = WMIConnection;
+ }
+ public IList<SystemObject> GetHardwareInfoList()
+ {
+ string className = System.Text.RegularExpressions.Regex.Match(
+ this.GetType().ToString(), "Win32_.*").Value;
+
+ return WMIReader.GetPropertyValues(WMIConnection,
+ "SELECT * FROM " + className,
+ className);
+ }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.SystemInfo/Win32_DiskDrive.cs b/Software/Visual_Studio/Tango.SystemInfo/Win32_DiskDrive.cs
new file mode 100644
index 000000000..8600a663d
--- /dev/null
+++ b/Software/Visual_Studio/Tango.SystemInfo/Win32_DiskDrive.cs
@@ -0,0 +1,25 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Tango.SystemInfo
+{
+ class Win32_DiskDrive : IWMI
+ {
+ Connection WMIConnection;
+
+ public Win32_DiskDrive(Connection WMIConnection)
+ {
+ this.WMIConnection = WMIConnection;
+ }
+ public IList<SystemObject> GetHardwareInfoList()
+ {
+ string className = System.Text.RegularExpressions.Regex.Match(
+ this.GetType().ToString(), "Win32_.*").Value;
+
+ return WMIReader.GetPropertyValues(WMIConnection,
+ "SELECT * FROM " + className,
+ className);
+ }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.SystemInfo/Win32_Fan.cs b/Software/Visual_Studio/Tango.SystemInfo/Win32_Fan.cs
new file mode 100644
index 000000000..afc241c37
--- /dev/null
+++ b/Software/Visual_Studio/Tango.SystemInfo/Win32_Fan.cs
@@ -0,0 +1,25 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Tango.SystemInfo
+{
+ class Win32_Fan : IWMI
+ {
+ Connection WMIConnection;
+
+ public Win32_Fan(Connection WMIConnection)
+ {
+ this.WMIConnection = WMIConnection;
+ }
+ public IList<SystemObject> GetHardwareInfoList()
+ {
+ string className = System.Text.RegularExpressions.Regex.Match(
+ this.GetType().ToString(), "Win32_.*").Value;
+
+ return WMIReader.GetPropertyValues(WMIConnection,
+ "SELECT * FROM " + className,
+ className);
+ }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.SystemInfo/Win32_FloppyController.cs b/Software/Visual_Studio/Tango.SystemInfo/Win32_FloppyController.cs
new file mode 100644
index 000000000..5ac1f9d8f
--- /dev/null
+++ b/Software/Visual_Studio/Tango.SystemInfo/Win32_FloppyController.cs
@@ -0,0 +1,25 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Tango.SystemInfo
+{
+ class Win32_FloppyController : IWMI
+ {
+ Connection WMIConnection;
+
+ public Win32_FloppyController(Connection WMIConnection)
+ {
+ this.WMIConnection = WMIConnection;
+ }
+ public IList<SystemObject> GetHardwareInfoList()
+ {
+ string className = System.Text.RegularExpressions.Regex.Match(
+ this.GetType().ToString(), "Win32_.*").Value;
+
+ return WMIReader.GetPropertyValues(WMIConnection,
+ "SELECT * FROM " + className,
+ className);
+ }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.SystemInfo/Win32_FloppyDrive.cs b/Software/Visual_Studio/Tango.SystemInfo/Win32_FloppyDrive.cs
new file mode 100644
index 000000000..10b1b8fde
--- /dev/null
+++ b/Software/Visual_Studio/Tango.SystemInfo/Win32_FloppyDrive.cs
@@ -0,0 +1,25 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Tango.SystemInfo
+{
+ class Win32_FloppyDrive : IWMI
+ {
+ Connection WMIConnection;
+
+ public Win32_FloppyDrive(Connection WMIConnection)
+ {
+ this.WMIConnection = WMIConnection;
+ }
+ public IList<SystemObject> GetHardwareInfoList()
+ {
+ string className = System.Text.RegularExpressions.Regex.Match(
+ this.GetType().ToString(), "Win32_.*").Value;
+
+ return WMIReader.GetPropertyValues(WMIConnection,
+ "SELECT * FROM " + className,
+ className);
+ }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.SystemInfo/Win32_IDEController.cs b/Software/Visual_Studio/Tango.SystemInfo/Win32_IDEController.cs
new file mode 100644
index 000000000..899a4ad5e
--- /dev/null
+++ b/Software/Visual_Studio/Tango.SystemInfo/Win32_IDEController.cs
@@ -0,0 +1,25 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Tango.SystemInfo
+{
+ class Win32_IDEController : IWMI
+ {
+ Connection WMIConnection;
+
+ public Win32_IDEController(Connection WMIConnection)
+ {
+ this.WMIConnection = WMIConnection;
+ }
+ public IList<SystemObject> GetHardwareInfoList()
+ {
+ string className = System.Text.RegularExpressions.Regex.Match(
+ this.GetType().ToString(), "Win32_.*").Value;
+
+ return WMIReader.GetPropertyValues(WMIConnection,
+ "SELECT * FROM " + className,
+ className);
+ }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.SystemInfo/Win32_IRQResource.cs b/Software/Visual_Studio/Tango.SystemInfo/Win32_IRQResource.cs
new file mode 100644
index 000000000..ff39c68fd
--- /dev/null
+++ b/Software/Visual_Studio/Tango.SystemInfo/Win32_IRQResource.cs
@@ -0,0 +1,25 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Tango.SystemInfo
+{
+ class Win32_IRQResource : IWMI
+ {
+ Connection WMIConnection;
+
+ public Win32_IRQResource(Connection WMIConnection)
+ {
+ this.WMIConnection = WMIConnection;
+ }
+ public IList<SystemObject> GetHardwareInfoList()
+ {
+ string className = System.Text.RegularExpressions.Regex.Match(
+ this.GetType().ToString(), "Win32_.*").Value;
+
+ return WMIReader.GetPropertyValues(WMIConnection,
+ "SELECT * FROM " + className,
+ className);
+ }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.SystemInfo/Win32_Keyboard.cs b/Software/Visual_Studio/Tango.SystemInfo/Win32_Keyboard.cs
new file mode 100644
index 000000000..2657b5954
--- /dev/null
+++ b/Software/Visual_Studio/Tango.SystemInfo/Win32_Keyboard.cs
@@ -0,0 +1,25 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Tango.SystemInfo
+{
+ class Win32_Keyboard : IWMI
+ {
+ Connection WMIConnection;
+
+ public Win32_Keyboard(Connection WMIConnection)
+ {
+ this.WMIConnection = WMIConnection;
+ }
+ public IList<SystemObject> GetHardwareInfoList()
+ {
+ string className = System.Text.RegularExpressions.Regex.Match(
+ this.GetType().ToString(), "Win32_.*").Value;
+
+ return WMIReader.GetPropertyValues(WMIConnection,
+ "SELECT * FROM " + className,
+ className);
+ }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.SystemInfo/Win32_MemoryDevice.cs b/Software/Visual_Studio/Tango.SystemInfo/Win32_MemoryDevice.cs
new file mode 100644
index 000000000..8ed21c13d
--- /dev/null
+++ b/Software/Visual_Studio/Tango.SystemInfo/Win32_MemoryDevice.cs
@@ -0,0 +1,25 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Tango.SystemInfo
+{
+ class Win32_MemoryDevice : IWMI
+ {
+ Connection WMIConnection;
+
+ public Win32_MemoryDevice(Connection WMIConnection)
+ {
+ this.WMIConnection = WMIConnection;
+ }
+ public IList<SystemObject> GetHardwareInfoList()
+ {
+ string className = System.Text.RegularExpressions.Regex.Match(
+ this.GetType().ToString(), "Win32_.*").Value;
+
+ return WMIReader.GetPropertyValues(WMIConnection,
+ "SELECT * FROM " + className,
+ className);
+ }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.SystemInfo/Win32_NetworkAdapter.cs b/Software/Visual_Studio/Tango.SystemInfo/Win32_NetworkAdapter.cs
new file mode 100644
index 000000000..561919a2d
--- /dev/null
+++ b/Software/Visual_Studio/Tango.SystemInfo/Win32_NetworkAdapter.cs
@@ -0,0 +1,25 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Tango.SystemInfo
+{
+ class Win32_NetworkAdapter : IWMI
+ {
+ Connection WMIConnection;
+
+ public Win32_NetworkAdapter(Connection WMIConnection)
+ {
+ this.WMIConnection = WMIConnection;
+ }
+ public IList<SystemObject> GetHardwareInfoList()
+ {
+ string className = System.Text.RegularExpressions.Regex.Match(
+ this.GetType().ToString(), "Win32_.*").Value;
+
+ return WMIReader.GetPropertyValues(WMIConnection,
+ "SELECT * FROM " + className,
+ className);
+ }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.SystemInfo/Win32_NetworkAdapterConfiguration.cs b/Software/Visual_Studio/Tango.SystemInfo/Win32_NetworkAdapterConfiguration.cs
new file mode 100644
index 000000000..ba22e1d01
--- /dev/null
+++ b/Software/Visual_Studio/Tango.SystemInfo/Win32_NetworkAdapterConfiguration.cs
@@ -0,0 +1,25 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Tango.SystemInfo
+{
+ class Win32_NetworkAdapterConfiguration : IWMI
+ {
+ Connection WMIConnection;
+
+ public Win32_NetworkAdapterConfiguration(Connection WMIConnection)
+ {
+ this.WMIConnection = WMIConnection;
+ }
+ public IList<SystemObject> GetHardwareInfoList()
+ {
+ string className = System.Text.RegularExpressions.Regex.Match(
+ this.GetType().ToString(), "Win32_.*").Value;
+
+ return WMIReader.GetPropertyValues(WMIConnection,
+ "SELECT * FROM " + className,
+ className);
+ }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.SystemInfo/Win32_OnBoardDevice.cs b/Software/Visual_Studio/Tango.SystemInfo/Win32_OnBoardDevice.cs
new file mode 100644
index 000000000..7c4bea2a3
--- /dev/null
+++ b/Software/Visual_Studio/Tango.SystemInfo/Win32_OnBoardDevice.cs
@@ -0,0 +1,25 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Tango.SystemInfo
+{
+ class Win32_OnBoardDevice : IWMI
+ {
+ Connection WMIConnection;
+
+ public Win32_OnBoardDevice(Connection WMIConnection)
+ {
+ this.WMIConnection = WMIConnection;
+ }
+ public IList<SystemObject> GetHardwareInfoList()
+ {
+ string className = System.Text.RegularExpressions.Regex.Match(
+ this.GetType().ToString(), "Win32_.*").Value;
+
+ return WMIReader.GetPropertyValues(WMIConnection,
+ "SELECT * FROM " + className,
+ className);
+ }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.SystemInfo/Win32_OperatingSystem.cs b/Software/Visual_Studio/Tango.SystemInfo/Win32_OperatingSystem.cs
new file mode 100644
index 000000000..24b93d92c
--- /dev/null
+++ b/Software/Visual_Studio/Tango.SystemInfo/Win32_OperatingSystem.cs
@@ -0,0 +1,21 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Tango.SystemInfo
+{
+ class Win32_OperatingSystem : IWMI
+ {
+ Connection WMIConnection;
+
+ public Win32_OperatingSystem(Connection WMIConnection)
+ {
+ this.WMIConnection = WMIConnection;
+ }
+
+ public IList<SystemObject> GetHardwareInfoList()
+ {
+ return WMIReader.GetPropertyValues(WMIConnection, "select * from Win32_OperatingSystem", "Win32_OperatingSystem");
+ }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.SystemInfo/Win32_PCMCIController.cs b/Software/Visual_Studio/Tango.SystemInfo/Win32_PCMCIController.cs
new file mode 100644
index 000000000..772af0a28
--- /dev/null
+++ b/Software/Visual_Studio/Tango.SystemInfo/Win32_PCMCIController.cs
@@ -0,0 +1,25 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Tango.SystemInfo
+{
+ class Win32_PCMCIController : IWMI
+ {
+ Connection WMIConnection;
+
+ public Win32_PCMCIController(Connection WMIConnection)
+ {
+ this.WMIConnection = WMIConnection;
+ }
+ public IList<SystemObject> GetHardwareInfoList()
+ {
+ string className = System.Text.RegularExpressions.Regex.Match(
+ this.GetType().ToString(), "Win32_.*").Value;
+
+ return WMIReader.GetPropertyValues(WMIConnection,
+ "SELECT * FROM " + className,
+ className);
+ }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.SystemInfo/Win32_POTSModem.cs b/Software/Visual_Studio/Tango.SystemInfo/Win32_POTSModem.cs
new file mode 100644
index 000000000..29f9549ca
--- /dev/null
+++ b/Software/Visual_Studio/Tango.SystemInfo/Win32_POTSModem.cs
@@ -0,0 +1,25 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Tango.SystemInfo
+{
+ class Win32_POTSModem : IWMI
+ {
+ Connection WMIConnection;
+
+ public Win32_POTSModem(Connection WMIConnection)
+ {
+ this.WMIConnection = WMIConnection;
+ }
+ public IList<SystemObject> GetHardwareInfoList()
+ {
+ string className = System.Text.RegularExpressions.Regex.Match(
+ this.GetType().ToString(), "Win32_.*").Value;
+
+ return WMIReader.GetPropertyValues(WMIConnection,
+ "SELECT * FROM " + className,
+ className);
+ }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.SystemInfo/Win32_ParallelPort.cs b/Software/Visual_Studio/Tango.SystemInfo/Win32_ParallelPort.cs
new file mode 100644
index 000000000..81244c588
--- /dev/null
+++ b/Software/Visual_Studio/Tango.SystemInfo/Win32_ParallelPort.cs
@@ -0,0 +1,25 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Tango.SystemInfo
+{
+ class Win32_ParallelPort : IWMI
+ {
+ Connection WMIConnection;
+
+ public Win32_ParallelPort(Connection WMIConnection)
+ {
+ this.WMIConnection = WMIConnection;
+ }
+ public IList<SystemObject> GetHardwareInfoList()
+ {
+ string className = System.Text.RegularExpressions.Regex.Match(
+ this.GetType().ToString(), "Win32_.*").Value;
+
+ return WMIReader.GetPropertyValues(WMIConnection,
+ "SELECT * FROM " + className,
+ className);
+ }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.SystemInfo/Win32_PhysicalMedia.cs b/Software/Visual_Studio/Tango.SystemInfo/Win32_PhysicalMedia.cs
new file mode 100644
index 000000000..816944da4
--- /dev/null
+++ b/Software/Visual_Studio/Tango.SystemInfo/Win32_PhysicalMedia.cs
@@ -0,0 +1,25 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Tango.SystemInfo
+{
+ class Win32_PhysicalMedia : IWMI
+ {
+ Connection WMIConnection;
+
+ public Win32_PhysicalMedia(Connection WMIConnection)
+ {
+ this.WMIConnection = WMIConnection;
+ }
+ public IList<SystemObject> GetHardwareInfoList()
+ {
+ string className = System.Text.RegularExpressions.Regex.Match(
+ this.GetType().ToString(), "Win32_.*").Value;
+
+ return WMIReader.GetPropertyValues(WMIConnection,
+ "SELECT * FROM " + className,
+ className);
+ }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.SystemInfo/Win32_PhysicalMemory.cs b/Software/Visual_Studio/Tango.SystemInfo/Win32_PhysicalMemory.cs
new file mode 100644
index 000000000..d319a3fa7
--- /dev/null
+++ b/Software/Visual_Studio/Tango.SystemInfo/Win32_PhysicalMemory.cs
@@ -0,0 +1,25 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Tango.SystemInfo
+{
+ class Win32_PhysicalMemory : IWMI
+ {
+ Connection WMIConnection;
+
+ public Win32_PhysicalMemory(Connection WMIConnection)
+ {
+ this.WMIConnection = WMIConnection;
+ }
+ public IList<SystemObject> GetHardwareInfoList()
+ {
+ string className = System.Text.RegularExpressions.Regex.Match(
+ this.GetType().ToString(), "Win32_.*").Value;
+
+ return WMIReader.GetPropertyValues(WMIConnection,
+ "SELECT * FROM " + className,
+ className);
+ }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.SystemInfo/Win32_PortConnector.cs b/Software/Visual_Studio/Tango.SystemInfo/Win32_PortConnector.cs
new file mode 100644
index 000000000..20288cec7
--- /dev/null
+++ b/Software/Visual_Studio/Tango.SystemInfo/Win32_PortConnector.cs
@@ -0,0 +1,25 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Tango.SystemInfo
+{
+ class Win32_PortConnector : IWMI
+ {
+ Connection WMIConnection;
+
+ public Win32_PortConnector(Connection WMIConnection)
+ {
+ this.WMIConnection = WMIConnection;
+ }
+ public IList<SystemObject> GetHardwareInfoList()
+ {
+ string className = System.Text.RegularExpressions.Regex.Match(
+ this.GetType().ToString(), "Win32_.*").Value;
+
+ return WMIReader.GetPropertyValues(WMIConnection,
+ "SELECT * FROM " + className,
+ className);
+ }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.SystemInfo/Win32_PortResource.cs b/Software/Visual_Studio/Tango.SystemInfo/Win32_PortResource.cs
new file mode 100644
index 000000000..8a8da41e7
--- /dev/null
+++ b/Software/Visual_Studio/Tango.SystemInfo/Win32_PortResource.cs
@@ -0,0 +1,25 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Tango.SystemInfo
+{
+ class Win32_PortResource : IWMI
+ {
+ Connection WMIConnection;
+
+ public Win32_PortResource(Connection WMIConnection)
+ {
+ this.WMIConnection = WMIConnection;
+ }
+ public IList<SystemObject> GetHardwareInfoList()
+ {
+ string className = System.Text.RegularExpressions.Regex.Match(
+ this.GetType().ToString(), "Win32_.*").Value;
+
+ return WMIReader.GetPropertyValues(WMIConnection,
+ "SELECT * FROM " + className,
+ className);
+ }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.SystemInfo/Win32_Processor.cs b/Software/Visual_Studio/Tango.SystemInfo/Win32_Processor.cs
new file mode 100644
index 000000000..e8387c2bd
--- /dev/null
+++ b/Software/Visual_Studio/Tango.SystemInfo/Win32_Processor.cs
@@ -0,0 +1,25 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Tango.SystemInfo
+{
+ class Win32_Processor : IWMI
+ {
+ Connection WMIConnection;
+
+ public Win32_Processor(Connection WMIConnection)
+ {
+ this.WMIConnection = WMIConnection;
+ }
+ public IList<SystemObject> GetHardwareInfoList()
+ {
+ string className = System.Text.RegularExpressions.Regex.Match(
+ this.GetType().ToString(), "Win32_.*").Value;
+
+ return WMIReader.GetPropertyValues(WMIConnection,
+ "SELECT * FROM " + className,
+ className);
+ }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.SystemInfo/Win32_SCSIController.cs b/Software/Visual_Studio/Tango.SystemInfo/Win32_SCSIController.cs
new file mode 100644
index 000000000..dfe689951
--- /dev/null
+++ b/Software/Visual_Studio/Tango.SystemInfo/Win32_SCSIController.cs
@@ -0,0 +1,25 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Tango.SystemInfo
+{
+ class Win32_SCSIController : IWMI
+ {
+ Connection WMIConnection;
+
+ public Win32_SCSIController(Connection WMIConnection)
+ {
+ this.WMIConnection = WMIConnection;
+ }
+ public IList<SystemObject> GetHardwareInfoList()
+ {
+ string className = System.Text.RegularExpressions.Regex.Match(
+ this.GetType().ToString(), "Win32_.*").Value;
+
+ return WMIReader.GetPropertyValues(WMIConnection,
+ "SELECT * FROM " + className,
+ className);
+ }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.SystemInfo/Win32_SerialPort.cs b/Software/Visual_Studio/Tango.SystemInfo/Win32_SerialPort.cs
new file mode 100644
index 000000000..6b20763f9
--- /dev/null
+++ b/Software/Visual_Studio/Tango.SystemInfo/Win32_SerialPort.cs
@@ -0,0 +1,25 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Tango.SystemInfo
+{
+ class Win32_SerialPort : IWMI
+ {
+ Connection WMIConnection;
+
+ public Win32_SerialPort(Connection WMIConnection)
+ {
+ this.WMIConnection = WMIConnection;
+ }
+ public IList<SystemObject> GetHardwareInfoList()
+ {
+ string className = System.Text.RegularExpressions.Regex.Match(
+ this.GetType().ToString(), "Win32_.*").Value;
+
+ return WMIReader.GetPropertyValues(WMIConnection,
+ "SELECT * FROM " + className,
+ className);
+ }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.SystemInfo/Win32_SerialPortConfiguration.cs b/Software/Visual_Studio/Tango.SystemInfo/Win32_SerialPortConfiguration.cs
new file mode 100644
index 000000000..d1efe826b
--- /dev/null
+++ b/Software/Visual_Studio/Tango.SystemInfo/Win32_SerialPortConfiguration.cs
@@ -0,0 +1,25 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Tango.SystemInfo
+{
+ class Win32_SerialPortConfiguration : IWMI
+ {
+ Connection WMIConnection;
+
+ public Win32_SerialPortConfiguration(Connection WMIConnection)
+ {
+ this.WMIConnection = WMIConnection;
+ }
+ public IList<SystemObject> GetHardwareInfoList()
+ {
+ string className = System.Text.RegularExpressions.Regex.Match(
+ this.GetType().ToString(), "Win32_.*").Value;
+
+ return WMIReader.GetPropertyValues(WMIConnection,
+ "SELECT * FROM " + className,
+ className);
+ }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.SystemInfo/Win32_SoundDevice.cs b/Software/Visual_Studio/Tango.SystemInfo/Win32_SoundDevice.cs
new file mode 100644
index 000000000..bece1f169
--- /dev/null
+++ b/Software/Visual_Studio/Tango.SystemInfo/Win32_SoundDevice.cs
@@ -0,0 +1,25 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Tango.SystemInfo
+{
+ class Win32_SoundDevice : IWMI
+ {
+ Connection WMIConnection;
+
+ public Win32_SoundDevice(Connection WMIConnection)
+ {
+ this.WMIConnection = WMIConnection;
+ }
+ public IList<SystemObject> GetHardwareInfoList()
+ {
+ string className = System.Text.RegularExpressions.Regex.Match(
+ this.GetType().ToString(), "Win32_.*").Value;
+
+ return WMIReader.GetPropertyValues(WMIConnection,
+ "SELECT * FROM " + className,
+ className);
+ }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.SystemInfo/Win32_SystemEnclosure.cs b/Software/Visual_Studio/Tango.SystemInfo/Win32_SystemEnclosure.cs
new file mode 100644
index 000000000..184ee3b99
--- /dev/null
+++ b/Software/Visual_Studio/Tango.SystemInfo/Win32_SystemEnclosure.cs
@@ -0,0 +1,25 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Tango.SystemInfo
+{
+ class Win32_SystemEnclosure : IWMI
+ {
+ Connection WMIConnection;
+
+ public Win32_SystemEnclosure(Connection WMIConnection)
+ {
+ this.WMIConnection = WMIConnection;
+ }
+ public IList<SystemObject> GetHardwareInfoList()
+ {
+ string className = System.Text.RegularExpressions.Regex.Match(
+ this.GetType().ToString(), "Win32_.*").Value;
+
+ return WMIReader.GetPropertyValues(WMIConnection,
+ "SELECT * FROM " + className,
+ className);
+ }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.SystemInfo/Win32_TapeDrive.cs b/Software/Visual_Studio/Tango.SystemInfo/Win32_TapeDrive.cs
new file mode 100644
index 000000000..94600d518
--- /dev/null
+++ b/Software/Visual_Studio/Tango.SystemInfo/Win32_TapeDrive.cs
@@ -0,0 +1,25 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Tango.SystemInfo
+{
+ class Win32_TapeDrive : IWMI
+ {
+ Connection WMIConnection;
+
+ public Win32_TapeDrive(Connection WMIConnection)
+ {
+ this.WMIConnection = WMIConnection;
+ }
+ public IList<SystemObject> GetHardwareInfoList()
+ {
+ string className = System.Text.RegularExpressions.Regex.Match(
+ this.GetType().ToString(), "Win32_.*").Value;
+
+ return WMIReader.GetPropertyValues(WMIConnection,
+ "SELECT * FROM " + className,
+ className);
+ }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.SystemInfo/Win32_TemperatureProbe.cs b/Software/Visual_Studio/Tango.SystemInfo/Win32_TemperatureProbe.cs
new file mode 100644
index 000000000..579203237
--- /dev/null
+++ b/Software/Visual_Studio/Tango.SystemInfo/Win32_TemperatureProbe.cs
@@ -0,0 +1,25 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Tango.SystemInfo
+{
+ class Win32_TemperatureProbe : IWMI
+ {
+ Connection WMIConnection;
+
+ public Win32_TemperatureProbe(Connection WMIConnection)
+ {
+ this.WMIConnection = WMIConnection;
+ }
+ public IList<SystemObject> GetHardwareInfoList()
+ {
+ string className = System.Text.RegularExpressions.Regex.Match(
+ this.GetType().ToString(), "Win32_.*").Value;
+
+ return WMIReader.GetPropertyValues(WMIConnection,
+ "SELECT * FROM " + className,
+ className);
+ }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.SystemInfo/Win32_USBController.cs b/Software/Visual_Studio/Tango.SystemInfo/Win32_USBController.cs
new file mode 100644
index 000000000..6df7b4799
--- /dev/null
+++ b/Software/Visual_Studio/Tango.SystemInfo/Win32_USBController.cs
@@ -0,0 +1,25 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Tango.SystemInfo
+{
+ class Win32_USBController : IWMI
+ {
+ Connection WMIConnection;
+
+ public Win32_USBController(Connection WMIConnection)
+ {
+ this.WMIConnection = WMIConnection;
+ }
+ public IList<SystemObject> GetHardwareInfoList()
+ {
+ string className = System.Text.RegularExpressions.Regex.Match(
+ this.GetType().ToString(), "Win32_.*").Value;
+
+ return WMIReader.GetPropertyValues(WMIConnection,
+ "SELECT * FROM " + className,
+ className);
+ }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.SystemInfo/Win32_USBHub.cs b/Software/Visual_Studio/Tango.SystemInfo/Win32_USBHub.cs
new file mode 100644
index 000000000..b0f812ebe
--- /dev/null
+++ b/Software/Visual_Studio/Tango.SystemInfo/Win32_USBHub.cs
@@ -0,0 +1,25 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Tango.SystemInfo
+{
+ class Win32_USBHub : IWMI
+ {
+ Connection WMIConnection;
+
+ public Win32_USBHub(Connection WMIConnection)
+ {
+ this.WMIConnection = WMIConnection;
+ }
+ public IList<SystemObject> GetHardwareInfoList()
+ {
+ string className = System.Text.RegularExpressions.Regex.Match(
+ this.GetType().ToString(), "Win32_.*").Value;
+
+ return WMIReader.GetPropertyValues(WMIConnection,
+ "SELECT * FROM " + className,
+ className);
+ }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.SystemInfo/Win32_UninterruptiblePowerSupply.cs b/Software/Visual_Studio/Tango.SystemInfo/Win32_UninterruptiblePowerSupply.cs
new file mode 100644
index 000000000..d8dc7bacd
--- /dev/null
+++ b/Software/Visual_Studio/Tango.SystemInfo/Win32_UninterruptiblePowerSupply.cs
@@ -0,0 +1,25 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Tango.SystemInfo
+{
+ class Win32_UninterruptiblePowerSupply : IWMI
+ {
+ Connection WMIConnection;
+
+ public Win32_UninterruptiblePowerSupply(Connection WMIConnection)
+ {
+ this.WMIConnection = WMIConnection;
+ }
+ public IList<SystemObject> GetHardwareInfoList()
+ {
+ string className = System.Text.RegularExpressions.Regex.Match(
+ this.GetType().ToString(), "Win32_.*").Value;
+
+ return WMIReader.GetPropertyValues(WMIConnection,
+ "SELECT * FROM " + className,
+ className);
+ }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.SystemInfo/Win32_VideoController.cs b/Software/Visual_Studio/Tango.SystemInfo/Win32_VideoController.cs
new file mode 100644
index 000000000..973303b98
--- /dev/null
+++ b/Software/Visual_Studio/Tango.SystemInfo/Win32_VideoController.cs
@@ -0,0 +1,25 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Tango.SystemInfo
+{
+ class Win32_VideoController : IWMI
+ {
+ Connection WMIConnection;
+
+ public Win32_VideoController(Connection WMIConnection)
+ {
+ this.WMIConnection = WMIConnection;
+ }
+ public IList<SystemObject> GetHardwareInfoList()
+ {
+ string className = System.Text.RegularExpressions.Regex.Match(
+ this.GetType().ToString(), "Win32_.*").Value;
+
+ return WMIReader.GetPropertyValues(WMIConnection,
+ "SELECT * FROM " + className,
+ className);
+ }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.SystemInfo/Win32_VoltageProbe.cs b/Software/Visual_Studio/Tango.SystemInfo/Win32_VoltageProbe.cs
new file mode 100644
index 000000000..818898aa8
--- /dev/null
+++ b/Software/Visual_Studio/Tango.SystemInfo/Win32_VoltageProbe.cs
@@ -0,0 +1,25 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Tango.SystemInfo
+{
+ class Win32_VoltageProbe : IWMI
+ {
+ Connection WMIConnection;
+
+ public Win32_VoltageProbe(Connection WMIConnection)
+ {
+ this.WMIConnection = WMIConnection;
+ }
+ public IList<SystemObject> GetHardwareInfoList()
+ {
+ string className = System.Text.RegularExpressions.Regex.Match(
+ this.GetType().ToString(), "Win32_.*").Value;
+
+ return WMIReader.GetPropertyValues(WMIConnection,
+ "SELECT * FROM " + className,
+ className);
+ }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.SystemInfo/XMLConfig.cs b/Software/Visual_Studio/Tango.SystemInfo/XMLConfig.cs
new file mode 100644
index 000000000..a6113a0cb
--- /dev/null
+++ b/Software/Visual_Studio/Tango.SystemInfo/XMLConfig.cs
@@ -0,0 +1,29 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Xml;
+
+namespace Tango.SystemInfo
+{
+ class XMLConfig
+ {
+ private static List<string> propNames;
+
+ public static List<string> GetSettings(string WMIClassName)
+ {
+ if (propNames == null)
+ {
+ propNames = new List<string>();
+ System.Xml.XmlDocument xmldoc = new System.Xml.XmlDocument();
+ xmldoc.Load(Tango.Core.Helpers.EmbeddedResourceHelper.GetEmbeddedResourceStream("Tango.SystemInfo.settings.xml"));
+ System.Xml.XmlNode properties = xmldoc.SelectSingleNode("//" + WMIClassName);
+
+ for (int i = 0; i < properties.ChildNodes.Count; i++)
+ propNames.Add(properties.ChildNodes[i].InnerText);
+
+ }
+
+ return propNames;
+ }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.SystemInfo/settings.xml b/Software/Visual_Studio/Tango.SystemInfo/settings.xml
new file mode 100644
index 000000000..ae3059407
--- /dev/null
+++ b/Software/Visual_Studio/Tango.SystemInfo/settings.xml
@@ -0,0 +1,1347 @@
+<?xmlversion = "1.0"encoding="utf-8"?>
+<AppSettings>
+ <WMI>
+ <Win32_OperatingSystem>
+ <property>BootDevice</property>
+ <property>BuildNumber</property>
+ <property>BuildType</property>
+ <property>Caption</property>
+ <property>CodeSet</property>
+ <property>CountryCode</property>
+ <property>CreationClassName</property>
+ <property>CSCreationClassName</property>
+ <property>CSDVersion</property>
+ <property>CSName</property>
+ <property>CurrentTimeZone</property>
+ <property>DataExecutionPrevention_Available</property>
+ <property>DataExecutionPrevention_32BitApplications</property>
+ <property>DataExecutionPrevention_Drivers</property>
+ <property>DataExecutionPrevention_SupportPolicy</property>
+ <property>Debug</property>
+ <property>Description</property>
+ <property>Distributed</property>
+ <property>EncryptionLevel</property>
+ <property>FreePhysicalMemory</property>
+ <property>FreeSpaceInPagingFiles</property>
+ <property>FreeVirtualMemory</property>
+ <property>InstallDate</property>
+ <property>LargeSystemCache</property>
+ <property>LastBootUpTime</property>
+ <property>LocalDateTime</property>
+ <property>Locale</property>
+ <property>Manufacturer</property>
+ <property>MaxNumberOfProcesses</property>
+ <property>MaxProcessMemorySize</property>
+ <property>Name</property>
+ <property>NumberOfLicensedUsers</property>
+ <property>NumberOfProcesses</property>
+ <property>NumberOfUsers</property>
+ <property>OperatingSystemSKU</property>
+ <property>Organization</property>
+ <property>OSArchitecture</property>
+ <property>OSLanguage</property>
+ <property>OSProductSuite</property>
+ <property>OSType</property>
+ <property>OtherTypeDescription</property>
+ <property>PAEEnabled</property>
+ <property>PlusProductID</property>
+ <property>PlusVersionNumber</property>
+ <property>PortableOperatingSystem</property>
+ <property>Primary</property>
+ <property>ProductType</property>
+ <property>RegisteredUser</property>
+ <property>SerialNumber</property>
+ <property>ServicePackMajorVersion</property>
+ <property>ServicePackMinorVersion</property>
+ <property>SizeStoredInPagingFiles</property>
+ <property>Status</property>
+ <property>SuiteMask</property>
+ <property>SystemDevice</property>
+ <property>SystemDirectory</property>
+ <property>SystemDrive</property>
+ <property>TotalSwapSpaceSize</property>
+ <property>TotalVirtualMemorySize</property>
+ <property>TotalVisibleMemorySize</property>
+ <property>Version</property>
+ <property>WindowsDirectory</property>
+ <property>QuantumLength</property>
+ <property>QuantumType</property>
+ </Win32_OperatingSystem>
+ <Win32_BaseBoard>
+ <property>Caption</property>
+ <property>CreationClassName</property>
+ <property>Depth</property>
+ <property>Description</property>
+ <property>Height</property>
+ <property>HostingBoard</property>
+ <property>HotSwappable</property>
+ <property>InstallDate</property>
+ <property>Manufacturer</property>
+ <property>Model</property>
+ <property>Name</property>
+ <property>OtherIdentifyingInfo</property>
+ <property>PartNumber</property>
+ <property>PoweredOn</property>
+ <property>Product</property>
+ <property>Removable</property>
+ <property>Replaceable</property>
+ <property>RequirementsDescription</property>
+ <property>RequiresDaughterBoard</property>
+ <property>SerialNumber</property>
+ <property>SKU</property>
+ <property>SlotLayout</property>
+ <property>SpecialRequirements</property>
+ <property>Status</property>
+ <property>Tag</property>
+ <property>Version</property>
+ <property>Weight</property>
+ <property>Width</property>
+ </Win32_BaseBoard>
+ <Win32_Battery>
+ <property>Availability</property>
+ <property>BatteryRechargeTime</property>
+ <property>BatteryStatus</property>
+ <property>Caption</property>
+ <property>Chemistry</property>
+ <property>ConfigManagerErrorCode</property>
+ <property>ConfigManagerUserConfig</property>
+ <property>CreationClassName</property>
+ <property>Description</property>
+ <property>DesignCapacity</property>
+ <property>DesignVoltage</property>
+ <property>DeviceID</property>
+ <property>ErrorCleared</property>
+ <property>ErrorDescription</property>
+ <property>EstimatedChargeRemaining</property>
+ <property>EstimatedRunTime</property>
+ <property>ExpectedBatteryLife</property>
+ <property>ExpectedLife</property>
+ <property>FullChargeCapacity</property>
+ <property>InstallDate</property>
+ <property>LastErrorCode</property>
+ <property>MaxRechargeTime</property>
+ <property>Name</property>
+ <property>PNPDeviceID</property>
+ <property>PowerManagementSupported</property>
+ <property>SmartBatteryVersion</property>
+ <property>Status</property>
+ <property>StatusInfo</property>
+ <property>SystemCreationClassName</property>
+ <property>SystemName</property>
+ <property>TimeOnBattery</property>
+ <property>TimeToFullCharge</property>
+ </Win32_Battery>
+ <Win32_BIOS>
+ <property>BuildNumber</property>
+ <property>Caption</property>
+ <property>CodeSet</property>
+ <property>CurrentLanguage</property>
+ <property>Description</property>
+ <property>IdentificationCode</property>
+ <property>InstallableLanguages</property>
+ <property>InstallDate</property>
+ <property>LanguageEdition</property>
+ <property>Manufacturer</property>
+ <property>Name</property>
+ <property>OtherTargetOS</property>
+ <property>PrimaryBIOS</property>
+ <property>ReleaseDate</property>
+ <property>SerialNumber</property>
+ <property>SMBIOSBIOSVersion</property>
+ <property>SMBIOSMajorVersion</property>
+ <property>SMBIOSMinorVersion</property>
+ <property>SMBIOSPresent</property>
+ <property>SoftwareElementID</property>
+ <property>SoftwareElementState</property>
+ <property>Status</property>
+ <property>TargetOperatingSystem</property>
+ <property>Version</property>
+ </Win32_BIOS>
+ <Win32_Bus>
+ <property>Availability</property>
+ <property>BusNum</property>
+ <property>BusType</property>
+ <property>Caption</property>
+ <property>ConfigManagerErrorCode</property>
+ <property>ConfigManagerUserConfig</property>
+ <property>CreationClassName</property>
+ <property>Description</property>
+ <property>DeviceID</property>
+ <property>ErrorCleared</property>
+ <property>ErrorDescription</property>
+ <property>InstallDate</property>
+ <property>LastErrorCode</property>
+ <property>Name</property>
+ <property>PNPDeviceID</property>
+ <property>PowerManagementSupported</property>
+ <property>Status</property>
+ <property>StatusInfo</property>
+ <property>SystemCreationClassName</property>
+ <property>SystemName</property>
+ </Win32_Bus>
+ <Win32_CDROMDrive>
+ <property>Availability</property>
+ <property>Caption</property>
+ <property>CompressionMethod</property>
+ <property>ConfigManagerErrorCode</property>
+ <property>ConfigManagerUserConfig</property>
+ <property>CreationClassName</property>
+ <property>DefaultBlockSize</property>
+ <property>Description</property>
+ <property>DeviceID</property>
+ <property>Drive</property>
+ <property>DriveIntegrity</property>
+ <property>ErrorCleared</property>
+ <property>ErrorDescription</property>
+ <property>ErrorMethodology</property>
+ <property>FileSystemFlags</property>
+ <property>FileSystemFlagsEx</property>
+ <property>InstallDate</property>
+ <property>LastErrorCode</property>
+ <property>Manufacturer</property>
+ <property>MaxBlockSize</property>
+ <property>MaximumComponentLength</property>
+ <property>MaxMediaSize</property>
+ <property>MediaLoaded</property>
+ <property>MediaType</property>
+ <property>MfrAssignedRevisionLevel</property>
+ <property>MinBlockSize</property>
+ <property>Name</property>
+ <property>NeedsCleaning</property>
+ <property>NumberOfMediaSupported</property>
+ <property>PNPDeviceID</property>
+ <property>PowerManagementSupported</property>
+ <property>RevisionLevel</property>
+ <property>SCSIBus</property>
+ <property>SCSILogicalUnit</property>
+ <property>SCSIPort</property>
+ <property>SCSITargetId</property>
+ <property>SerialNumber</property>
+ <property>Size</property>
+ <property>Status</property>
+ <property>StatusInfo</property>
+ <property>SystemCreationClassName</property>
+ <property>SystemName</property>
+ <property>TransferRate</property>
+ <property>VolumeName</property>
+ <property>VolumeSerialNumber</property>
+ </Win32_CDROMDrive>
+ <Win32_DiskDrive>
+ <property>Availability</property>
+ <property>BytesPerSector</property>
+ <property>Capabilities[]</property>
+ <property>CapabilityDescriptions[]</property>
+ <property>Caption</property>
+ <property>CompressionMethod</property>
+ <property>ConfigManagerErrorCode</property>
+ <property>ConfigManagerUserConfig</property>
+ <property>CreationClassName</property>
+ <property>DefaultBlockSize</property>
+ <property>Description</property>
+ <property>DeviceID</property>
+ <property>ErrorCleared</property>
+ <property>ErrorDescription</property>
+ <property>ErrorMethodology</property>
+ <property>FirmwareRevision</property>
+ <property>Index</property>
+ <property>InstallDate</property>
+ <property>InterfaceType</property>
+ <property>LastErrorCode</property>
+ <property>Manufacturer</property>
+ <property>MaxBlockSize</property>
+ <property>MaxMediaSize</property>
+ <property>MediaLoaded</property>
+ <property>MediaType</property>
+ <property>MinBlockSize</property>
+ <property>Model</property>
+ <property>Name</property>
+ <property>NeedsCleaning</property>
+ <property>NumberOfMediaSupported</property>
+ <property>Partitions</property>
+ <property>PNPDeviceID</property>
+ <property>PowerManagementCapabilities[]</property>
+ <property>PowerManagementSupported</property>
+ <property>SCSIBus</property>
+ <property>SCSILogicalUnit</property>
+ <property>SCSIPort</property>
+ <property>SCSITargetId</property>
+ <property>SectorsPerTrack</property>
+ <property>SerialNumber</property>
+ <property>Signature</property>
+ <property>Size</property>
+ <property>Status</property>
+ <property>StatusInfo</property>
+ <property>SystemCreationClassName</property>
+ <property>SystemName</property>
+ <property>TotalCylinders</property>
+ <property>TotalHeads</property>
+ <property>TotalSectors</property>
+ <property>TotalTracks</property>
+ <property>TracksPerCylinder</property>
+ </Win32_DiskDrive>
+ <Win32_DMAChannel>
+ <property>16AddressSize</property>
+ <property>16Availability</property>
+ <property>BurstMode</property>
+ <property>16ByteMode</property>
+ <property>Caption</property>
+ <property>16ChannelTiming</property>
+ <property>CreationClassName</property>
+ <property>CSCreationClassName</property>
+ <property>CSName</property>
+ <property>Description</property>
+ <property>32DMAChannel</property>
+ <property>InstallDate</property>
+ <property>32MaxTransferSize</property>
+ <property>Name</property>
+ <property>32Port</property>
+ <property>Status</property>
+ <property>16TransferWidths[]</property>
+ <property>16TypeCTiming</property>
+ <property>16WordMode</property>
+ </Win32_DMAChannel>
+ <Win32_Fan>
+ <property>ActiveCooling</property>
+ <property>Availability</property>
+ <property>Caption</property>
+ <property>ConfigManagerErrorCode</property>
+ <property>ConfigManagerUserConfig</property>
+ <property>CreationClassName</property>
+ <property>Description</property>
+ <property>DesiredSpeed</property>
+ <property>DeviceID</property>
+ <property>ErrorCleared</property>
+ <property>ErrorDescription</property>
+ <property>InstallDate</property>
+ <property>LastErrorCode</property>
+ <property>Name</property>
+ <property>PNPDeviceID</property>
+ <property>PowerManagementCapabilities[]</property>
+ <property>PowerManagementSupported</property>
+ <property>Status</property>
+ <property>StatusInfo</property>
+ <property>SystemCreationClassName</property>
+ <property>SystemName</property>
+ <property>VariableSpeed</property>
+ </Win32_Fan>
+ <Win32_FloppyController>
+ <property>Availability</property>
+ <property>Caption</property>
+ <property>ConfigManagerErrorCode</property>
+ <property>ConfigManagerUserConfig</property>
+ <property>CreationClassName</property>
+ <property>Description</property>
+ <property>DeviceID</property>
+ <property>ErrorCleared</property>
+ <property>ErrorDescription</property>
+ <property>InstallDate</property>
+ <property>LastErrorCode</property>
+ <property>Manufacturer</property>
+ <property>MaxNumberControlled</property>
+ <property>Name</property>
+ <property>PNPDeviceID</property>
+ <property>PowerManagementSupported</property>
+ <property>ProtocolSupported</property>
+ <property>Status</property>
+ <property>StatusInfo</property>
+ <property>SystemCreationClassName</property>
+ <property>SystemName</property>
+ <property>TimeOfLastReset</property>
+ </Win32_FloppyController>
+ <Win32_FloppyDrive>
+ <property>Availability</property>
+ <property>Caption</property>
+ <property>CompressionMethod</property>
+ <property>ConfigManagerErrorCode</property>
+ <property>ConfigManagerUserConfig</property>
+ <property>CreationClassName</property>
+ <property>DefaultBlockSize</property>
+ <property>Description</property>
+ <property>DeviceID</property>
+ <property>ErrorCleared</property>
+ <property>ErrorDescription</property>
+ <property>ErrorMethodology</property>
+ <property>InstallDate</property>
+ <property>LastErrorCode</property>
+ <property>Manufacturer</property>
+ <property>MaxBlockSize</property>
+ <property>MaxMediaSize</property>
+ <property>MinBlockSize</property>
+ <property>Name</property>
+ <property>NeedsCleaning</property>
+ <property>NumberOfMediaSupported</property>
+ <property>PNPDeviceID</property>
+ <property>PowerManagementCapabilities[]</property>
+ <property>PowerManagementSupported</property>
+ <property>Status</property>
+ <property>StatusInfo</property>
+ <property>SystemCreationClassName</property>
+ <property>SystemName</property>
+ </Win32_FloppyDrive>
+ <Win32_IDEController>
+ <property>Availability</property>
+ <property>Caption</property>
+ <property>ConfigManagerErrorCode</property>
+ <property>ConfigManagerUserConfig</property>
+ <property>CreationClassName</property>
+ <property>Description</property>
+ <property>DeviceID</property>
+ <property>ErrorCleared</property>
+ <property>ErrorDescription</property>
+ <property>InstallDate</property>
+ <property>LastErrorCode</property>
+ <property>Manufacturer</property>
+ <property>MaxNumberControlled</property>
+ <property>Name</property>
+ <property>PNPDeviceID</property>
+ <property>PowerManagementCapabilities[]</property>
+ <property>PowerManagementSupported</property>
+ <property>ProtocolSupported</property>
+ <property>Status</property>
+ <property>StatusInfo</property>
+ <property>SystemCreationClassName</property>
+ <property>SystemName</property>
+ <property>TimeOfLastReset</property>
+ </Win32_IDEController>
+ <Win32_IRQResource>
+ <property>Availability</property>
+ <property>Caption</property>
+ <property>CreationClassName</property>
+ <property>CSCreationClassName</property>
+ <property>CSName</property>
+ <property>Description</property>
+ <property>Hardware</property>
+ <property>InstallDate</property>
+ <property>IRQNumber</property>
+ <property>Name</property>
+ <property>Shareable</property>
+ <property>Status</property>
+ <property>TriggerLevel</property>
+ <property>TriggerType</property>
+ <property>Vector</property>
+ </Win32_IRQResource>
+ <Win32_Keyboard>
+ <property>Availability</property>
+ <property>Caption</property>
+ <property>ConfigManagerErrorCode</property>
+ <property>ConfigManagerUserConfig</property>
+ <property>CreationClassName</property>
+ <property>Description</property>
+ <property>DeviceID</property>
+ <property>ErrorCleared</property>
+ <property>ErrorDescription</property>
+ <property>InstallDate</property>
+ <property>IsLocked</property>
+ <property>LastErrorCode</property>
+ <property>Layout</property>
+ <property>Name</property>
+ <property>NumberOfFunctionKeys</property>
+ <property>Password</property>
+ <property>PNPDeviceID</property>
+ <property>PowerManagementSupported</property>
+ <property>Status</property>
+ <property>StatusInfo</property>
+ <property>SystemCreationClassName</property>
+ <property>SystemName</property>
+ </Win32_Keyboard>
+ <Win32_MemoryDevice>
+ <property>Access</property>
+ <property>AdditionalErrorData[]</property>
+ <property>Availability</property>
+ <property>BlockSize</property>
+ <property>Caption</property>
+ <property>ConfigManagerErrorCode</property>
+ <property>ConfigManagerUserConfig</property>
+ <property>CorrectableError</property>
+ <property>CreationClassName</property>
+ <property>Description</property>
+ <property>DeviceID</property>
+ <property>EndingAddress</property>
+ <property>ErrorAccess</property>
+ <property>ErrorAddress</property>
+ <property>ErrorCleared</property>
+ <property>ErrorDataOrder</property>
+ <property>ErrorDescription</property>
+ <property>ErrorGranularity</property>
+ <property>ErrorInfo</property>
+ <property>ErrorMethodology</property>
+ <property>ErrorResolution</property>
+ <property>ErrorTime</property>
+ <property>ErrorTransferSize</property>
+ <property>InstallDate</property>
+ <property>LastErrorCode</property>
+ <property>Name</property>
+ <property>NumberOfBlocks</property>
+ <property>OtherErrorDescription</property>
+ <property>PNPDeviceID</property>
+ <property>PowerManagementCapabilities[]</property>
+ <property>PowerManagementSupported</property>
+ <property>Purpose</property>
+ <property>StartingAddress</property>
+ <property>Status</property>
+ <property>StatusInfo</property>
+ <property>SystemCreationClassName</property>
+ <property>SystemLevelAddress</property>
+ <property>SystemName</property>
+ </Win32_MemoryDevice>
+ <Win32_NetworkAdapter>
+ <property>AdapterType</property>
+ <property>AdapterTypeID</property>
+ <property>AutoSense</property>
+ <property>Availability</property>
+ <property>Caption</property>
+ <property>ConfigManagerErrorCode</property>
+ <property>ConfigManagerUserConfig</property>
+ <property>CreationClassName</property>
+ <property>Description</property>
+ <property>DeviceID</property>
+ <property>ErrorCleared</property>
+ <property>ErrorDescription</property>
+ <property>GUID</property>
+ <property>Index</property>
+ <property>InstallDate</property>
+ <property>Installed</property>
+ <property>InterfaceIndex</property>
+ <property>LastErrorCode</property>
+ <property>MACAddress</property>
+ <property>Manufacturer</property>
+ <property>MaxNumberControlled</property>
+ <property>MaxSpeed</property>
+ <property>Name</property>
+ <property>NetConnectionID</property>
+ <property>NetConnectionStatus</property>
+ <property>NetEnabled</property>
+ <property>NetworkAddresses[]</property>
+ <property>PermanentAddress</property>
+ <property>PhysicalAdapter</property>
+ <property>PNPDeviceID</property>
+ <property>PowerManagementCapabilities[]</property>
+ <property>PowerManagementSupported</property>
+ <property>ProductName</property>
+ <property>ServiceName</property>
+ <property>Speed</property>
+ <property>Status</property>
+ <property>StatusInfo</property>
+ <property>SystemCreationClassName</property>
+ <property>SystemName</property>
+ <property>TimeOfLastReset</property>
+ </Win32_NetworkAdapter>
+ <Win32_NetworkAdapterConfiguration>
+ <property>ArpAlwaysSourceRoute</property>
+ <property>ArpUseEtherSNAP</property>
+ <property>Caption</property>
+ <property>DatabasePath</property>
+ <property>DeadGWDetectEnabled</property>
+ <property>DefaultIPGateway[]</property>
+ <property>DefaultTOS</property>
+ <property>DefaultTTL</property>
+ <property>Description</property>
+ <property>DHCPEnabled</property>
+ <property>DHCPLeaseExpires</property>
+ <property>DHCPLeaseObtained</property>
+ <property>DHCPServer</property>
+ <property>DNSDomain</property>
+ <property>DNSDomainSuffixSearchOrder[]</property>
+ <property>DNSEnabledForWINSResolution</property>
+ <property>DNSHostName</property>
+ <property>DNSServerSearchOrder[]</property>
+ <property>DomainDNSRegistrationEnabled</property>
+ <property>ForwardBufferMemory</property>
+ <property>FullDNSRegistrationEnabled</property>
+ <property>GatewayCostMetric[]</property>
+ <property>IGMPLevel</property>
+ <property>Index</property>
+ <property>InterfaceIndex</property>
+ <property>IPAddress[]</property>
+ <property>IPConnectionMetric</property>
+ <property>IPEnabled</property>
+ <property>IPFilterSecurityEnabled</property>
+ <property>IPPortSecurityEnabled</property>
+ <property>IPSecPermitIPProtocols[]</property>
+ <property>IPSecPermitTCPPorts[]</property>
+ <property>IPSecPermitUDPPorts[]</property>
+ <property>IPSubnet[]</property>
+ <property>IPUseZeroBroadcast</property>
+ <property>IPXAddress</property>
+ <property>IPXEnabled</property>
+ <property>IPXFrameType[]</property>
+ <property>IPXMediaType</property>
+ <property>IPXNetworkNumber[]</property>
+ <property>IPXVirtualNetNumber</property>
+ <property>KeepAliveInterval</property>
+ <property>KeepAliveTime</property>
+ <property>MACAddress</property>
+ <property>MTU</property>
+ <property>NumForwardPackets</property>
+ <property>PMTUBHDetectEnabled</property>
+ <property>PMTUDiscoveryEnabled</property>
+ <property>ServiceName</property>
+ <property>SettingID</property>
+ <property>TcpipNetbiosOptions</property>
+ <property>TcpMaxConnectRetransmissions</property>
+ <property>TcpMaxDataRetransmissions</property>
+ <property>TcpNumConnections</property>
+ <property>TcpUseRFC1122UrgentPointer</property>
+ <property>TcpWindowSize</property>
+ <property>WINSEnableLMHostsLookup</property>
+ <property>WINSHostLookupFile</property>
+ <property>WINSPrimaryServer</property>
+ <property>WINSScopeID</property>
+ <property>WINSSecondaryServer</property>
+ </Win32_NetworkAdapterConfiguration>
+ <Win32_OnBoardDevice>
+ <property>Caption</property>
+ <property>CreationClassName</property>
+ <property>Description</property>
+ <property>DeviceType</property>
+ <property>Enabled</property>
+ <property>HotSwappable</property>
+ <property>InstallDate</property>
+ <property>Manufacturer</property>
+ <property>Model</property>
+ <property>Name</property>
+ <property>OtherIdentifyingInfo</property>
+ <property>PartNumber</property>
+ <property>PoweredOn</property>
+ <property>Removable</property>
+ <property>Replaceable</property>
+ <property>SerialNumber</property>
+ <property>SKU</property>
+ <property>Status</property>
+ <property>Tag</property>
+ <property>Version</property>
+ </Win32_OnBoardDevice>
+ <Win32_ParallelPort>
+ <property>Availability</property>
+ <property>Capabilities[]</property>
+ <property>CapabilityDescriptions[]</property>
+ <property>Caption</property>
+ <property>ConfigManagerErrorCode</property>
+ <property>ConfigManagerUserConfig</property>
+ <property>CreationClassName</property>
+ <property>Description</property>
+ <property>DeviceID</property>
+ <property>DMASupport</property>
+ <property>ErrorCleared</property>
+ <property>ErrorDescription</property>
+ <property>InstallDate</property>
+ <property>LastErrorCode</property>
+ <property>MaxNumberControlled</property>
+ <property>Name</property>
+ <property>OSAutoDiscovered</property>
+ <property>PNPDeviceID</property>
+ <property>PowerManagementCapabilities[]</property>
+ <property>PowerManagementSupported</property>
+ <property>ProtocolSupported</property>
+ <property>Status</property>
+ <property>StatusInfo</property>
+ <property>SystemCreationClassName</property>
+ <property>SystemName</property>
+ <property>TimeOfLastReset</property>
+ </Win32_ParallelPort>
+ <Win32_PCMCIAController>
+ <property>Availability</property>
+ <property>Caption</property>
+ <property>ConfigManagerErrorCode</property>
+ <property>ConfigManagerUserConfig</property>
+ <property>CreationClassName</property>
+ <property>Description</property>
+ <property>DeviceID</property>
+ <property>ErrorCleared</property>
+ <property>ErrorDescription</property>
+ <property>InstallDate</property>
+ <property>LastErrorCode</property>
+ <property>Manufacturer</property>
+ <property>MaxNumberControlled</property>
+ <property>Name</property>
+ <property>PNPDeviceID</property>
+ <property>PowerManagementCapabilities[]</property>
+ <property>PowerManagementSupported</property>
+ <property>ProtocolSupported</property>
+ <property>Status</property>
+ <property>StatusInfo</property>
+ <property>SystemCreationClassName</property>
+ <property>SystemName</property>
+ <property>TimeOfLastReset</property>
+ </Win32_PCMCIAController>
+ <Win32_PhysicalMedia>
+ <property>Caption</property>
+ <property>Description</property>
+ <property>InstallDate</property>
+ <property>Name</property>
+ <property>Status</property>
+ <property>CreationClassName</property>
+ <property>Manufacturer</property>
+ <property>Model</property>
+ <property>SKU</property>
+ <property>SerialNumber</property>
+ <property>Tag</property>
+ <property>Version</property>
+ <property>PartNumber</property>
+ <property>OtherIdentifyingInfo</property>
+ <property>PoweredOn</property>
+ <property>Removable</property>
+ <property>Replaceable</property>
+ <property>HotSwappable</property>
+ <property>Capacity</property>
+ <property>MediaType</property>
+ <property>MediaDescription</property>
+ <property>WriteProtectOn</property>
+ <property>CleanerMedia</property>
+ </Win32_PhysicalMedia>
+ <Win32_PhysicalMemory>
+ <property>BankLabel</property>
+ <property>Capacity</property>
+ <property>Caption</property>
+ <property>CreationClassName</property>
+ <property>DataWidth</property>
+ <property>Description</property>
+ <property>DeviceLocator</property>
+ <property>FormFactor</property>
+ <property>HotSwappable</property>
+ <property>InstallDate</property>
+ <property>InterleaveDataDepth</property>
+ <property>InterleavePosition</property>
+ <property>Manufacturer</property>
+ <property>MemoryType</property>
+ <property>Model</property>
+ <property>Name</property>
+ <property>OtherIdentifyingInfo</property>
+ <property>PartNumber</property>
+ <property>PositionInRow</property>
+ <property>PoweredOn</property>
+ <property>Removable</property>
+ <property>Replaceable</property>
+ <property>SerialNumber</property>
+ <property>SKU</property>
+ <property>Speed</property>
+ <property>Status</property>
+ <property>Tag</property>
+ <property>TotalWidth</property>
+ <property>TypeDetail</property>
+ <property>Version</property>
+ </Win32_PhysicalMemory>
+ <Win32_PortConnector>
+ <property>Caption</property>
+ <property>ConnectorPinout</property>
+ <property>ConnectorType[]</property>
+ <property>CreationClassName</property>
+ <property>Description</property>
+ <property>ExternalReferenceDesignator</property>
+ <property>InstallDate</property>
+ <property>InternalReferenceDesignator</property>
+ <property>Manufacturer</property>
+ <property>Model</property>
+ <property>Name</property>
+ <property>OtherIdentifyingInfo</property>
+ <property>PartNumber</property>
+ <property>PortType</property>
+ <property>PoweredOn</property>
+ <property>SerialNumber</property>
+ <property>SKU</property>
+ <property>Status</property>
+ <property>Tag</property>
+ <property>Version</property>
+ </Win32_PortConnector>
+ <Win32_PortResource>
+ <property>Alias</property>
+ <property>Caption</property>
+ <property>CreationClassName</property>
+ <property>CSCreationClassName</property>
+ <property>CSName</property>
+ <property>Description</property>
+ <property>EndingAddress</property>
+ <property>InstallDate</property>
+ <property>Name</property>
+ <property>StartingAddress</property>
+ <property>Status</property>
+ </Win32_PortResource>
+ <Win32_POTSModem>
+ <property>AnswerMode</property>
+ <property>AttachedTo</property>
+ <property>Availability</property>
+ <property>BlindOff</property>
+ <property>BlindOn</property>
+ <property>Caption</property>
+ <property>CompatibilityFlags</property>
+ <property>CompressionInfo</property>
+ <property>CompressionOff</property>
+ <property>CompressionOn</property>
+ <property>ConfigManagerErrorCode</property>
+ <property>ConfigManagerUserConfig</property>
+ <property>ConfigurationDialog</property>
+ <property>CountriesSupported[]</property>
+ <property>CountrySelected</property>
+ <property>CreationClassName</property>
+ <property>CurrentPasswords[]</property>
+ <property>DCB[]</property>
+ <property>Default[]</property>
+ <property>Description</property>
+ <property>DeviceID</property>
+ <property>DeviceLoader</property>
+ <property>DeviceType</property>
+ <property>DialType</property>
+ <property>DriverDate</property>
+ <property>ErrorCleared</property>
+ <property>ErrorControlForced</property>
+ <property>ErrorControlInfo</property>
+ <property>ErrorControlOff</property>
+ <property>ErrorControlOn</property>
+ <property>ErrorDescription</property>
+ <property>FlowControlHard</property>
+ <property>FlowControlOff</property>
+ <property>FlowControlSoft</property>
+ <property>InactivityScale</property>
+ <property>InactivityTimeout</property>
+ <property>Index</property>
+ <property>InstallDate</property>
+ <property>LastErrorCode</property>
+ <property>MaxBaudRateToPhone</property>
+ <property>MaxBaudRateToSerialPort</property>
+ <property>MaxNumberOfPasswords</property>
+ <property>Model</property>
+ <property>ModemInfPath</property>
+ <property>ModemInfSection</property>
+ <property>ModulationBell</property>
+ <property>ModulationCCITT</property>
+ <property>ModulationScheme</property>
+ <property>Name</property>
+ <property>PNPDeviceID</property>
+ <property>PortSubClass</property>
+ <property>PowerManagementCapabilities[]</property>
+ <property>PowerManagementSupported</property>
+ <property>Prefix</property>
+ <property>Properties[]</property>
+ <property>ProviderName</property>
+ <property>Pulse</property>
+ <property>Reset</property>
+ <property>ResponsesKeyName</property>
+ <property>RingsBeforeAnswer</property>
+ <property>SpeakerModeDial</property>
+ <property>SpeakerModeOff</property>
+ <property>SpeakerModeOn</property>
+ <property>SpeakerModeSetup</property>
+ <property>SpeakerVolumeHigh</property>
+ <property>SpeakerVolumeInfo</property>
+ <property>SpeakerVolumeLow</property>
+ <property>SpeakerVolumeMed</property>
+ <property>Status</property>
+ <property>StatusInfo</property>
+ <property>StringFormat</property>
+ <property>SupportsCallback</property>
+ <property>SupportsSynchronousConnect</property>
+ <property>SystemCreationClassName</property>
+ <property>SystemName</property>
+ <property>Terminator</property>
+ <property>TimeOfLastReset</property>
+ <property>Tone</property>
+ <property>VoiceSwitchFeature</property>
+ </Win32_POTSModem>
+ <Win32_Processor>
+ <property>AddressWidth</property>
+ <property>Architecture</property>
+ <property>Availability</property>
+ <property>Caption</property>
+ <property>ConfigManagerErrorCode</property>
+ <property>ConfigManagerUserConfig</property>
+ <property>CpuStatus</property>
+ <property>CreationClassName</property>
+ <property>CurrentClockSpeed</property>
+ <property>CurrentVoltage</property>
+ <property>DataWidth</property>
+ <property>Description</property>
+ <property>DeviceID</property>
+ <property>ErrorCleared</property>
+ <property>ErrorDescription</property>
+ <property>ExtClock</property>
+ <property>Family</property>
+ <property>InstallDate</property>
+ <property>L2CacheSize</property>
+ <property>L2CacheSpeed</property>
+ <property>L3CacheSize</property>
+ <property>L3CacheSpeed</property>
+ <property>LastErrorCode</property>
+ <property>Level</property>
+ <property>LoadPercentage</property>
+ <property>Manufacturer</property>
+ <property>MaxClockSpeed</property>
+ <property>Name</property>
+ <property>NumberOfCores</property>
+ <property>NumberOfLogicalProcessors</property>
+ <property>OtherFamilyDescription</property>
+ <property>PNPDeviceID</property>
+ <property>PowerManagementSupported</property>
+ <property>ProcessorId</property>
+ <property>ProcessorType</property>
+ <property>Revision</property>
+ <property>Role</property>
+ <property>SocketDesignation</property>
+ <property>Status</property>
+ <property>StatusInfo</property>
+ <property>Stepping</property>
+ <property>SystemCreationClassName</property>
+ <property>SystemName</property>
+ <property>UniqueId</property>
+ <property>UpgradeMethod</property>
+ <property>Version</property>
+ <property>VoltageCaps</property>
+ </Win32_Processor>
+ <Win32_SCSIController>
+ <property>Availability</property>
+ <property>Caption</property>
+ <property>ConfigManagerErrorCode</property>
+ <property>ConfigManagerUserConfig</property>
+ <property>ControllerTimeouts</property>
+ <property>CreationClassName</property>
+ <property>Description</property>
+ <property>DeviceID</property>
+ <property>DeviceMap</property>
+ <property>DriverName</property>
+ <property>ErrorCleared</property>
+ <property>ErrorDescription</property>
+ <property>HardwareVersion</property>
+ <property>Index</property>
+ <property>InstallDate</property>
+ <property>LastErrorCode</property>
+ <property>Manufacturer</property>
+ <property>MaxDataWidth</property>
+ <property>MaxNumberControlled</property>
+ <property>MaxTransferRate</property>
+ <property>Name</property>
+ <property>PNPDeviceID</property>
+ <property>PowerManagementCapabilities[]</property>
+ <property>PowerManagementSupported</property>
+ <property>ProtectionManagement</property>
+ <property>ProtocolSupported</property>
+ <property>Status</property>
+ <property>StatusInfo</property>
+ <property>SystemCreationClassName</property>
+ <property>SystemName</property>
+ <property>TimeOfLastReset</property>
+ </Win32_SCSIController>
+ <Win32_SerialPort>
+ <property>Availability</property>
+ <property>Binary</property>
+ <property>Capabilities[]</property>
+ <property>CapabilityDescriptions[]</property>
+ <property>Caption</property>
+ <property>ConfigManagerErrorCode</property>
+ <property>ConfigManagerUserConfig</property>
+ <property>CreationClassName</property>
+ <property>Description</property>
+ <property>DeviceID</property>
+ <property>ErrorCleared</property>
+ <property>ErrorDescription</property>
+ <property>InstallDate</property>
+ <property>LastErrorCode</property>
+ <property>MaxBaudRate</property>
+ <property>MaximumInputBufferSize</property>
+ <property>MaximumOutputBufferSize</property>
+ <property>MaxNumberControlled</property>
+ <property>Name</property>
+ <property>OSAutoDiscovered</property>
+ <property>PNPDeviceID</property>
+ <property>PowerManagementCapabilities[]</property>
+ <property>PowerManagementSupported</property>
+ <property>ProtocolSupported</property>
+ <property>ProviderType</property>
+ <property>SettableBaudRate</property>
+ <property>SettableDataBits</property>
+ <property>SettableFlowControl</property>
+ <property>SettableParity</property>
+ <property>SettableParityCheck</property>
+ <property>SettableRLSD</property>
+ <property>SettableStopBits</property>
+ <property>Status</property>
+ <property>StatusInfo</property>
+ <property>Supports16BitMode</property>
+ <property>SupportsDTRDSR</property>
+ <property>SupportsElapsedTimeouts</property>
+ <property>SupportsIntTimeouts</property>
+ <property>SupportsParityCheck</property>
+ <property>SupportsRLSD</property>
+ <property>SupportsRTSCTS</property>
+ <property>SupportsSpecialCharacters</property>
+ <property>SupportsXOnXOff</property>
+ <property>SupportsXOnXOffSet</property>
+ <property>SystemCreationClassName</property>
+ <property>SystemName</property>
+ <property>TimeOfLastReset</property>
+ </Win32_SerialPort>
+ <Win32_SerialPortConfiguration>
+ <property>AbortReadWriteOnError</property>
+ <property>BaudRate</property>
+ <property>BinaryModeEnabled</property>
+ <property>BitsPerByte</property>
+ <property>Caption</property>
+ <property>ContinueXMitOnXOff</property>
+ <property>CTSOutflowControl</property>
+ <property>Description</property>
+ <property>DiscardNULLBytes</property>
+ <property>DSROutflowControl</property>
+ <property>DSRSensitivity</property>
+ <property>DTRFlowControlType</property>
+ <property>EOFCharacter</property>
+ <property>ErrorReplaceCharacter</property>
+ <property>ErrorReplacementEnabled</property>
+ <property>EventCharacter</property>
+ <property>IsBusy</property>
+ <property>Name</property>
+ <property>Parity</property>
+ <property>ParityCheckEnabled</property>
+ <property>RTSFlowControlType</property>
+ <property>SettingID</property>
+ <property>StopBits</property>
+ <property>XOffCharacter</property>
+ <property>XOffXMitThreshold</property>
+ <property>XOnCharacter</property>
+ <property>XOnXMitThreshold</property>
+ <property>XOnXOffInFlowControl</property>
+ <property>XOnXOffOutFlowControl</property>
+ </Win32_SerialPortConfiguration>
+ <Win32_SoundDevice>
+ <property>Availability</property>
+ <property>Caption</property>
+ <property>ConfigManagerErrorCode</property>
+ <property>ConfigManagerUserConfig</property>
+ <property>CreationClassName</property>
+ <property>Description</property>
+ <property>DeviceID</property>
+ <property>DMABufferSize</property>
+ <property>ErrorCleared</property>
+ <property>ErrorDescription</property>
+ <property>InstallDate</property>
+ <property>LastErrorCode</property>
+ <property>Manufacturer</property>
+ <property>MPU401Address</property>
+ <property>Name</property>
+ <property>PNPDeviceID</property>
+ <property>PowerManagementCapabilities[]</property>
+ <property>PowerManagementSupported</property>
+ <property>ProductName</property>
+ <property>Status</property>
+ <property>StatusInfo</property>
+ <property>SystemCreationClassName</property>
+ <property>SystemName</property>
+ </Win32_SoundDevice>
+ <Win32_SystemEnclosure>
+ <property>AudibleAlarm</property>
+ <property>BreachDescription</property>
+ <property>CableManagementStrategy</property>
+ <property>Caption</property>
+ <property>ChassisTypes[]</property>
+ <property>CreationClassName</property>
+ <property>CurrentRequiredOrProduced</property>
+ <property>Depth</property>
+ <property>Description</property>
+ <property>HeatGeneration</property>
+ <property>Height</property>
+ <property>HotSwappable</property>
+ <property>InstallDate</property>
+ <property>LockPresent</property>
+ <property>Manufacturer</property>
+ <property>Model</property>
+ <property>Name</property>
+ <property>NumberOfPowerCords</property>
+ <property>OtherIdentifyingInfo</property>
+ <property>PartNumber</property>
+ <property>PoweredOn</property>
+ <property>Removable</property>
+ <property>Replaceable</property>
+ <property>SecurityBreach</property>
+ <property>SecurityStatus</property>
+ <property>SerialNumber</property>
+ <property>ServiceDescriptions[]</property>
+ <property>ServicePhilosophy[]</property>
+ <property>SKU</property>
+ <property>SMBIOSAssetTag</property>
+ <property>Status</property>
+ <property>Tag</property>
+ <property>TypeDescriptions[]</property>
+ <property>Version</property>
+ <property>VisibleAlarm</property>
+ <property>Weight</property>
+ <property>Width</property>
+ </Win32_SystemEnclosure>
+ <Win32_TapeDrive>
+ <property>Availability</property>
+ <property>Capabilities[]</property>
+ <property>CapabilityDescriptions[]</property>
+ <property>Caption</property>
+ <property>Compression</property>
+ <property>CompressionMethod</property>
+ <property>ConfigManagerErrorCode</property>
+ <property>ConfigManagerUserConfig</property>
+ <property>CreationClassName</property>
+ <property>DefaultBlockSize</property>
+ <property>Description</property>
+ <property>DeviceID</property>
+ <property>ECC</property>
+ <property>EOTWarningZoneSize</property>
+ <property>ErrorCleared</property>
+ <property>ErrorDescription</property>
+ <property>ErrorMethodology</property>
+ <property>FeaturesHigh</property>
+ <property>FeaturesLow</property>
+ <property>Id</property>
+ <property>InstallDate</property>
+ <property>LastErrorCode</property>
+ <property>Manufacturer</property>
+ <property>MaxBlockSize</property>
+ <property>MaxMediaSize</property>
+ <property>MaxPartitionCount</property>
+ <property>MediaType</property>
+ <property>MinBlockSize</property>
+ <property>Name</property>
+ <property>NeedsCleaning</property>
+ <property>NumberOfMediaSupported</property>
+ <property>Padding</property>
+ <property>PNPDeviceID</property>
+ <property>PowerManagementCapabilities[]</property>
+ <property>PowerManagementSupported</property>
+ <property>ReportSetMarks</property>
+ <property>Status</property>
+ <property>StatusInfo</property>
+ <property>SystemCreationClassName</property>
+ <property>SystemName</property>
+ </Win32_TapeDrive>
+ <Win32_TemperatureProbe>
+ <property>Accuracy</property>
+ <property>Availability</property>
+ <property>Caption</property>
+ <property>ConfigManagerErrorCode</property>
+ <property>ConfigManagerUserConfig</property>
+ <property>CreationClassName</property>
+ <property>CurrentReading</property>
+ <property>Description</property>
+ <property>DeviceID</property>
+ <property>ErrorCleared</property>
+ <property>ErrorDescription</property>
+ <property>InstallDate</property>
+ <property>IsLinear</property>
+ <property>LastErrorCode</property>
+ <property>LowerThresholdCritical</property>
+ <property>LowerThresholdFatal</property>
+ <property>LowerThresholdNonCritical</property>
+ <property>MaxReadable</property>
+ <property>MinReadable</property>
+ <property>Name</property>
+ <property>NominalReading</property>
+ <property>NormalMax</property>
+ <property>NormalMin</property>
+ <property>PNPDeviceID</property>
+ <property>PowerManagementCapabilities[]</property>
+ <property>PowerManagementSupported</property>
+ <property>Resolution</property>
+ <property>Status</property>
+ <property>StatusInfo</property>
+ <property>SystemCreationClassName</property>
+ <property>SystemName</property>
+ <property>Tolerance</property>
+ <property>UpperThresholdCritical</property>
+ <property>UpperThresholdFatal</property>
+ <property>UpperThresholdNonCritical</property>
+ </Win32_TemperatureProbe>
+ <Win32_UninterruptiblePowerSupply>
+ <property>ActiveInputVoltage</property>
+ <property>Availability</property>
+ <property>BatteryInstalled</property>
+ <property>CanTurnOffRemotely</property>
+ <property>Caption</property>
+ <property>CommandFile</property>
+ <property>ConfigManagerErrorCode</property>
+ <property>ConfigManagerUserConfig</property>
+ <property>CreationClassName</property>
+ <property>Description</property>
+ <property>DeviceID</property>
+ <property>ErrorCleared</property>
+ <property>ErrorDescription</property>
+ <property>EstimatedChargeRemaining</property>
+ <property>EstimatedRunTime</property>
+ <property>FirstMessageDelay</property>
+ <property>InstallDate</property>
+ <property>IsSwitchingSupply</property>
+ <property>LastErrorCode</property>
+ <property>LowBatterySignal</property>
+ <property>MessageInterval</property>
+ <property>Name</property>
+ <property>PNPDeviceID</property>
+ <property>PowerFailSignal</property>
+ <property>PowerManagementCapabilities[]</property>
+ <property>PowerManagementSupported</property>
+ <property>Range1InputFrequencyHigh</property>
+ <property>Range1InputFrequencyLow</property>
+ <property>Range1InputVoltageHigh</property>
+ <property>Range1InputVoltageLow</property>
+ <property>Range2InputFrequencyHigh</property>
+ <property>Range2InputFrequencyLow</property>
+ <property>Range2InputVoltageHigh</property>
+ <property>Range2InputVoltageLow</property>
+ <property>RemainingCapacityStatus</property>
+ <property>Status</property>
+ <property>StatusInfo</property>
+ <property>SystemCreationClassName</property>
+ <property>SystemName</property>
+ <property>TimeOnBackup</property>
+ <property>TotalOutputPower</property>
+ <property>TypeOfRangeSwitching</property>
+ <property>UPSPort</property>
+ </Win32_UninterruptiblePowerSupply>
+ <Win32_USBController>
+ <property>Availability</property>
+ <property>Caption</property>
+ <property>ConfigManagerErrorCode</property>
+ <property>ConfigManagerUserConfig</property>
+ <property>CreationClassName</property>
+ <property>Description</property>
+ <property>DeviceID</property>
+ <property>ErrorCleared</property>
+ <property>ErrorDescription</property>
+ <property>InstallDate</property>
+ <property>LastErrorCode</property>
+ <property>Manufacturer</property>
+ <property>MaxNumberControlled</property>
+ <property>Name</property>
+ <property>PNPDeviceID</property>
+ <property>PowerManagementCapabilities[]</property>
+ <property>PowerManagementSupported</property>
+ <property>ProtocolSupported</property>
+ <property>Status</property>
+ <property>StatusInfo</property>
+ <property>SystemCreationClassName</property>
+ <property>SystemName</property>
+ <property>TimeOfLastReset</property>
+ </Win32_USBController>
+ <Win32_USBHub>
+ <property>Availability</property>
+ <property>Caption</property>
+ <property>ClassCode</property>
+ <property>ConfigManagerErrorCode</property>
+ <property>ConfigManagerUserCode</property>
+ <property>CreationClassName</property>
+ <property>CurrentAlternativeSettings</property>
+ <property>CurrentConfigValue</property>
+ <property>Description</property>
+ <property>DeviceID</property>
+ <property>ErrorCleared</property>
+ <property>ErrorDescription</property>
+ <property>GangSwitched</property>
+ <property>InstallDate</property>
+ <property>LastErrorCode</property>
+ <property>Name</property>
+ <property>NumberOfConfigs</property>
+ <property>NumberOfPorts</property>
+ <property>PNPDeviceID</property>
+ <property>PowerManagementCapabilities[]</property>
+ <property>PowerManagementSupported</property>
+ <property>ProtocolCode</property>
+ <property>Status</property>
+ <property>StatusInfo</property>
+ <property>SubclassCode</property>
+ <property>SystemCreationClassName</property>
+ <property>SystemName</property>
+ <property>USBVersion</property>
+ </Win32_USBHub>
+ <Win32_VideoController>
+ <property>AcceleratorCapabilities[]</property>
+ <property>AdapterCompatibility</property>
+ <property>AdapterDACType</property>
+ <property>AdapterRAM</property>
+ <property>Availability</property>
+ <property>CapabilityDescriptions[]</property>
+ <property>Caption</property>
+ <property>ColorTableEntries</property>
+ <property>ConfigManagerErrorCode</property>
+ <property>ConfigManagerUserConfig</property>
+ <property>CreationClassName</property>
+ <property>CurrentBitsPerPixel</property>
+ <property>CurrentHorizontalResolution</property>
+ <property>CurrentNumberOfColors</property>
+ <property>CurrentNumberOfColumns</property>
+ <property>CurrentNumberOfRows</property>
+ <property>CurrentRefreshRate</property>
+ <property>CurrentScanMode</property>
+ <property>CurrentVerticalResolution</property>
+ <property>Description</property>
+ <property>DeviceID</property>
+ <property>DeviceSpecificPens</property>
+ <property>DitherType</property>
+ <property>DriverDate</property>
+ <property>DriverVersion</property>
+ <property>ErrorCleared</property>
+ <property>ErrorDescription</property>
+ <property>ICMIntent</property>
+ <property>ICMMethod</property>
+ <property>InfFilename</property>
+ <property>InfSection</property>
+ <property>InstallDate</property>
+ <property>InstalledDisplayDrivers</property>
+ <property>LastErrorCode</property>
+ <property>MaxMemorySupported</property>
+ <property>MaxNumberControlled</property>
+ <property>MaxRefreshRate</property>
+ <property>MinRefreshRate</property>
+ <property>Monochrome</property>
+ <property>Name</property>
+ <property>NumberOfColorPlanes</property>
+ <property>NumberOfVideoPages</property>
+ <property>PNPDeviceID</property>
+ <property>PowerManagementCapabilities[]</property>
+ <property>PowerManagementSupported</property>
+ <property>ProtocolSupported</property>
+ <property>ReservedSystemPaletteEntries</property>
+ <property>SpecificationVersion</property>
+ <property>Status</property>
+ <property>StatusInfo</property>
+ <property>SystemCreationClassName</property>
+ <property>SystemName</property>
+ <property>SystemPaletteEntries</property>
+ <property>TimeOfLastReset</property>
+ <property>VideoArchitecture</property>
+ <property>VideoMemoryType</property>
+ <property>VideoMode</property>
+ <property>VideoModeDescription</property>
+ <property>VideoProcessor</property>
+ </Win32_VideoController>
+ <Win32_VoltageProbe>
+ <property>Accuracy</property>
+ <property>Availability</property>
+ <property>Caption</property>
+ <property>ConfigManagerErrorCode</property>
+ <property>ConfigManagerUserConfig</property>
+ <property>CreationClassName</property>
+ <property>CurrentReading</property>
+ <property>Description</property>
+ <property>DeviceID</property>
+ <property>ErrorCleared</property>
+ <property>ErrorDescription</property>
+ <property>InstallDate</property>
+ <property>IsLinear</property>
+ <property>LastErrorCode</property>
+ <property>LowerThresholdCritical</property>
+ <property>LowerThresholdFatal</property>
+ <property>LowerThresholdNonCritical</property>
+ <property>MaxReadable</property>
+ <property>MinReadable</property>
+ <property>Name</property>
+ <property>NominalReading</property>
+ <property>NormalMax</property>
+ <property>NormalMin</property>
+ <property>PNPDeviceID</property>
+ <property>PowerManagementCapabilities[]</property>
+ <property>PowerManagementSupported</property>
+ <property>Resolution</property>
+ <property>Status</property>
+ <property>StatusInfo</property>
+ <property>SystemCreationClassName</property>
+ <property>SystemName</property>
+ <property>Tolerance</property>
+ <property>UpperThresholdCritical</property>
+ <property>UpperThresholdFatal</property>
+ <property>UpperThresholdNonCritical</property>
+ </Win32_VoltageProbe>
+
+ </WMI>
+
+</AppSettings>
+
diff --git a/Software/Visual_Studio/Utilities/Tango.DispenserAnalyzer.UI/Analyzers/ReliabilityTestAnalyser.cs b/Software/Visual_Studio/Utilities/Tango.DispenserAnalyzer.UI/Analyzers/ReliabilityTestAnalyser.cs
new file mode 100644
index 000000000..05a0aa32c
--- /dev/null
+++ b/Software/Visual_Studio/Utilities/Tango.DispenserAnalyzer.UI/Analyzers/ReliabilityTestAnalyser.cs
@@ -0,0 +1,15 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Tango.DispenserAnalyzer.UI.Analysis;
+
+namespace Tango.DispenserAnalyzer.UI.Analyzers
+{
+ [Analyzer("Reliability")]
+ public class ReliabilityTestAnalyser: FlowAnalyser
+ {
+ }
+}