diff options
| author | Roy Ben-Shabat <Roy@Twine-s.com> | 2019-11-26 17:33:02 +0200 |
|---|---|---|
| committer | Roy Ben-Shabat <Roy@Twine-s.com> | 2019-11-26 17:33:02 +0200 |
| commit | ca29510e1e336c4d68aaa926cfea6eb72ce42779 (patch) | |
| tree | 298c10a1567df22cf594054271dd5ce656f09c12 /Software/Visual_Studio/PPC | |
| parent | 6c43f97559613443e781917a827c3b644db03490 (diff) | |
| download | Tango-ca29510e1e336c4d68aaa926cfea6eb72ce42779.tar.gz Tango-ca29510e1e336c4d68aaa926cfea6eb72ce42779.zip | |
Working on backup/restore...
Diffstat (limited to 'Software/Visual_Studio/PPC')
20 files changed, 789 insertions, 39 deletions
diff --git a/Software/Visual_Studio/PPC/Modules/Tango.PPC.BackupRestore/Tango.PPC.BackupRestore.csproj b/Software/Visual_Studio/PPC/Modules/Tango.PPC.BackupRestore/Tango.PPC.BackupRestore.csproj index d32415e17..5e538ec89 100644 --- a/Software/Visual_Studio/PPC/Modules/Tango.PPC.BackupRestore/Tango.PPC.BackupRestore.csproj +++ b/Software/Visual_Studio/PPC/Modules/Tango.PPC.BackupRestore/Tango.PPC.BackupRestore.csproj @@ -69,6 +69,10 @@ <Generator>MSBuild:Compile</Generator> <SubType>Designer</SubType> </Page> + <Page Include="Views\RestoreProgressView.xaml"> + <Generator>MSBuild:Compile</Generator> + <SubType>Designer</SubType> + </Page> <Page Include="Views\BackupProgressView.xaml"> <Generator>MSBuild:Compile</Generator> <SubType>Designer</SubType> @@ -113,6 +117,9 @@ <Compile Include="ViewModels\MainViewVM.cs" /> <Compile Include="ViewModels\RestoreViewVM.cs" /> <Compile Include="ViewModels\WelcomeViewVM.cs" /> + <Compile Include="Views\RestoreProgressView.xaml.cs"> + <DependentUpon>RestoreProgressView.xaml</DependentUpon> + </Compile> <Compile Include="Views\BackupProgressView.xaml.cs"> <DependentUpon>BackupProgressView.xaml</DependentUpon> </Compile> diff --git a/Software/Visual_Studio/PPC/Modules/Tango.PPC.BackupRestore/ViewModels/BackupViewVM.cs b/Software/Visual_Studio/PPC/Modules/Tango.PPC.BackupRestore/ViewModels/BackupViewVM.cs index dd561c5be..ab8d0248f 100644 --- a/Software/Visual_Studio/PPC/Modules/Tango.PPC.BackupRestore/ViewModels/BackupViewVM.cs +++ b/Software/Visual_Studio/PPC/Modules/Tango.PPC.BackupRestore/ViewModels/BackupViewVM.cs @@ -71,14 +71,14 @@ namespace Tango.PPC.BackupRestore.ViewModels public String BackupLocation { get { return _backupLocation; } - set { _backupLocation = value; RaisePropertyChangedAuto(); } + set { _backupLocation = value; RaisePropertyChangedAuto(); InvalidateRelayCommands(); } } private String _backupName; public String BackupName { get { return _backupName; } - set { _backupName = value; RaisePropertyChangedAuto(); } + set { _backupName = value; RaisePropertyChangedAuto(); InvalidateRelayCommands(); } } public RelayCommand BackupCommand { get; set; } @@ -88,7 +88,7 @@ namespace Tango.PPC.BackupRestore.ViewModels public BackupViewVM() { BrowseBackupLocationCommand = new RelayCommand(BrowseBackupLocation); - BackupCommand = new RelayCommand(StartBackup); + BackupCommand = new RelayCommand(StartBackup, () => !String.IsNullOrWhiteSpace(BackupName) && BackupLocation != null); IsBackupJobs = true; } diff --git a/Software/Visual_Studio/PPC/Modules/Tango.PPC.BackupRestore/ViewModels/RestoreViewVM.cs b/Software/Visual_Studio/PPC/Modules/Tango.PPC.BackupRestore/ViewModels/RestoreViewVM.cs index 904fd9e52..ec6083436 100644 --- a/Software/Visual_Studio/PPC/Modules/Tango.PPC.BackupRestore/ViewModels/RestoreViewVM.cs +++ b/Software/Visual_Studio/PPC/Modules/Tango.PPC.BackupRestore/ViewModels/RestoreViewVM.cs @@ -3,15 +3,126 @@ using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; +using Tango.Core.Commands; +using Tango.Core.DI; +using Tango.Explorer; +using Tango.PPC.BackupRestore.Views; using Tango.PPC.Common; +using Tango.PPC.Common.BackupRestore; +using Tango.PPC.Storage; namespace Tango.PPC.BackupRestore.ViewModels { public class RestoreViewVM : PPCViewModel { + private string _backupFileLocation; + + [TangoInject] + public IBackupManager BackupManager { get; set; } + + private String _backupFileName; + public String BackupFileName + { + get { return _backupFileName; } + set { _backupFileName = value; RaisePropertyChangedAuto(); InvalidateRelayCommands(); } + } + + private RestoreSettings _restoreSettings; + public RestoreSettings RestoreSettings + { + get { return _restoreSettings; } + set { _restoreSettings = value; RaisePropertyChangedAuto(); } + } + + private BackupFile _backupFile; + public BackupFile BackupFile + { + get { return _backupFile; } + set { _backupFile = value; RaisePropertyChangedAuto(); } + } + + private BackupRestoreProgressEventArgs _currentRestoreProgress; + public BackupRestoreProgressEventArgs CurrentRestoreProgress + { + get { return _currentRestoreProgress; } + set { _currentRestoreProgress = value; RaisePropertyChangedAuto(); } + } + + public RelayCommand BrowseForBackupCommand { get; set; } + + public RelayCommand RestoreCommand { get; set; } + + public RestoreViewVM() + { + RestoreSettings = new RestoreSettings(); + RestoreCommand = new RelayCommand(StartRestore, () => BackupFileName != null); + BrowseForBackupCommand = new RelayCommand(BrowseForBackup); + } + + private async void StartRestore() + { + await NavigationManager.NavigateTo<BackupRestoreModule>(nameof(RestoreProgressView)); + + if (IsFree) + { + IsFree = false; + var result = await BackupManager.Restore(_backupFileLocation, RestoreSettings); + IsFree = true; + } + } + + private async void BrowseForBackup() + { + var result = await NavigationManager. + NavigateForResult<StorageModule, + Storage.Views.MainView, ExplorerFileItem, + Storage.Models.StorageNavigationRequest>( + new Storage.Models.StorageNavigationRequest() + { + Intent = Storage.Models.StorageNavigationIntent.LoadFile, + Filter = ExplorerFileDefinition.Backup.Extension, + Title = "Select Backup File", + }); + + if (result != null) + { + _backupFileLocation = result.Path; + + try + { + BackupFile = await BackupManager.ExtractBackupConfiguration(_backupFileLocation); + BackupFileName = System.IO.Path.GetFileName(result.Path); + } + catch (Exception ex) + { + LogManager.Log(ex, $"Error extracting backup configuration from file '{_backupFileLocation}'."); + await NotificationProvider.ShowError($"Error occurred while trying to extract the backup file information\n{ex.FlattenMessage()}"); + } + } + } + + public override void OnNavigatedFrom() + { + base.OnNavigatedFrom(); + BackupFileName = null; + BackupFile = null; + _backupFileLocation = null; + } + + public override void OnApplicationReady() + { + base.OnApplicationReady(); + BackupManager.Progress += BackupManager_Progress; + } + + private void BackupManager_Progress(object sender, BackupRestoreProgressEventArgs e) + { + CurrentRestoreProgress = e; + } + public override void OnApplicationStarted() { - + } } } diff --git a/Software/Visual_Studio/PPC/Modules/Tango.PPC.BackupRestore/Views/BackupView.xaml b/Software/Visual_Studio/PPC/Modules/Tango.PPC.BackupRestore/Views/BackupView.xaml index 68c022766..52dd87d92 100644 --- a/Software/Visual_Studio/PPC/Modules/Tango.PPC.BackupRestore/Views/BackupView.xaml +++ b/Software/Visual_Studio/PPC/Modules/Tango.PPC.BackupRestore/Views/BackupView.xaml @@ -34,7 +34,7 @@ <Run FontSize="{StaticResource TangoTitleFontSize}">Backup your system</Run> <LineBreak/> <LineBreak/> - <Run>Select whether you would like to backup your jobs or the entire system and press 'Start Backup'.</Run> + <Run>Please specify the location and settings of your backup and press 'START'.</Run> </TextBlock> <touch:TouchDropShadowBorder Padding="10" Margin="0 50 0 0"> diff --git a/Software/Visual_Studio/PPC/Modules/Tango.PPC.BackupRestore/Views/MainView.xaml b/Software/Visual_Studio/PPC/Modules/Tango.PPC.BackupRestore/Views/MainView.xaml index 0f7667c4e..d9c4c06a3 100644 --- a/Software/Visual_Studio/PPC/Modules/Tango.PPC.BackupRestore/Views/MainView.xaml +++ b/Software/Visual_Studio/PPC/Modules/Tango.PPC.BackupRestore/Views/MainView.xaml @@ -25,6 +25,7 @@ <local:BackupView/> <local:RestoreView/> <local:BackupProgressView/> + <local:RestoreProgressView/> </controls:NavigationControl> </DockPanel> diff --git a/Software/Visual_Studio/PPC/Modules/Tango.PPC.BackupRestore/Views/RestoreProgressView.xaml b/Software/Visual_Studio/PPC/Modules/Tango.PPC.BackupRestore/Views/RestoreProgressView.xaml new file mode 100644 index 000000000..f83bb0bd5 --- /dev/null +++ b/Software/Visual_Studio/PPC/Modules/Tango.PPC.BackupRestore/Views/RestoreProgressView.xaml @@ -0,0 +1,38 @@ +<UserControl x:Class="Tango.PPC.BackupRestore.Views.RestoreProgressView" + 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:touch="clr-namespace:Tango.Touch.Controls;assembly=Tango.Touch" + xmlns:vm="clr-namespace:Tango.PPC.BackupRestore.ViewModels" + xmlns:controls="clr-namespace:Tango.SharedUI.Controls;assembly=Tango.SharedUI" + xmlns:global="clr-namespace:Tango.PPC.BackupRestore" + xmlns:local="clr-namespace:Tango.PPC.BackupRestore.Views" + mc:Ignorable="d" + d:DesignHeight="700" d:DesignWidth="800" d:DataContext="{d:DesignInstance Type=vm:RestoreViewVM, IsDesignTimeCreatable=False}" DataContext="{x:Static global:ViewModelLocator.RestoreViewVM}"> + + <Grid Background="{StaticResource TangoPrimaryBackgroundBrush}"> + <DockPanel Margin="30 0 30 30"> + <Grid DockPanel.Dock="Top"> + <StackPanel> + <TextBlock TextWrapping="Wrap" FontSize="{StaticResource TangoDefaultFontSize}"> + <Run FontSize="{StaticResource TangoTitleFontSize}">Restoring your system</Run> + <LineBreak/> + <LineBreak/> + <Run>This process may take several minutes, please wait.</Run> + </TextBlock> + + + </StackPanel> + </Grid> + + <Grid> + <StackPanel VerticalAlignment="Center"> + <TextBlock Text="{Binding CurrentRestoreProgress.Stage,Converter={StaticResource EnumToDescriptionConverter},FallbackValue='Restoring data'}" HorizontalAlignment="Center" FontSize="{StaticResource TangoTitleFontSize}"></TextBlock> + <touch:TouchProgressBar Margin="0 20" Height="10" IsIndeterminate="{Binding CurrentRestoreProgress.IsIntermediate}" Maximum="{Binding CurrentRestoreProgress.Total}" Value="{Binding CurrentRestoreProgress.Progress,Mode=OneWay}" /> + </StackPanel> + </Grid> + + </DockPanel> + </Grid> +</UserControl> diff --git a/Software/Visual_Studio/PPC/Modules/Tango.PPC.BackupRestore/Views/RestoreProgressView.xaml.cs b/Software/Visual_Studio/PPC/Modules/Tango.PPC.BackupRestore/Views/RestoreProgressView.xaml.cs new file mode 100644 index 000000000..3b8b19fa9 --- /dev/null +++ b/Software/Visual_Studio/PPC/Modules/Tango.PPC.BackupRestore/Views/RestoreProgressView.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.PPC.BackupRestore.Views +{ + /// <summary> + /// Interaction logic for BackupView.xaml + /// </summary> + public partial class RestoreProgressView : UserControl + { + public RestoreProgressView() + { + InitializeComponent(); + } + } +} diff --git a/Software/Visual_Studio/PPC/Modules/Tango.PPC.BackupRestore/Views/RestoreView.xaml b/Software/Visual_Studio/PPC/Modules/Tango.PPC.BackupRestore/Views/RestoreView.xaml index ef7dcbb58..0f91f8ae6 100644 --- a/Software/Visual_Studio/PPC/Modules/Tango.PPC.BackupRestore/Views/RestoreView.xaml +++ b/Software/Visual_Studio/PPC/Modules/Tango.PPC.BackupRestore/Views/RestoreView.xaml @@ -9,22 +9,160 @@ xmlns:global="clr-namespace:Tango.PPC.BackupRestore" xmlns:local="clr-namespace:Tango.PPC.BackupRestore.Views" mc:Ignorable="d" - d:DesignHeight="1280" d:DesignWidth="800" d:DataContext="{d:DesignInstance Type=vm:RestoreViewVM, IsDesignTimeCreatable=False}" DataContext="{x:Static global:ViewModelLocator.RestoreViewVM}"> - - <Grid> - <StackPanel HorizontalAlignment="Left" Margin="50 20 50 0"> - <TextBlock TextWrapping="Wrap" FontSize="{StaticResource TangoDefaultFontSize}"> + d:DesignHeight="700" d:DesignWidth="800" d:DataContext="{d:DesignInstance Type=vm:RestoreViewVM, IsDesignTimeCreatable=False}" DataContext="{x:Static global:ViewModelLocator.RestoreViewVM}"> + + <Grid Background="{StaticResource TangoPrimaryBackgroundBrush}"> + <DockPanel Margin="30 0 30 30"> + <Grid DockPanel.Dock="Bottom"> + <DockPanel> + <touch:TouchButton Command="{Binding RestoreCommand}" HorizontalAlignment="Right" Height="80" Width="300" CornerRadius="40"> + <touch:TouchButton.Style> + <Style TargetType="touch:TouchButton" BasedOn="{StaticResource {x:Type touch:TouchButton}}"> + <Setter Property="Content" Value="START"></Setter> + <Style.Triggers> + <DataTrigger Binding="{Binding IsFree}" Value="False"> + <Setter Property="Content" Value="IN PROGRESS"></Setter> + </DataTrigger> + </Style.Triggers> + </Style> + </touch:TouchButton.Style> + </touch:TouchButton> + </DockPanel> + </Grid> + <StackPanel IsEnabled="{Binding IsFree}"> + <TextBlock TextWrapping="Wrap" FontSize="{StaticResource TangoDefaultFontSize}"> <Run FontSize="{StaticResource TangoTitleFontSize}">Restore your system</Run> <LineBreak/> <LineBreak/> - <Run>This wizard allows you to create a complete backup of your current machine state including software, firmware, data and user settings.</Run> - <LineBreak/> - <LineBreak/> - <LineBreak/> - <Run>For creating a complete backup of your system please press 'Backup'.</Run> - <LineBreak/> - <Run>In case you want to restore your system to a previous state, please press 'Restore'.</Run> - </TextBlock> - </StackPanel> + <Run>Please specify the location of your backup file and other options. Press 'START' when you are ready.</Run> + </TextBlock> + + <touch:TouchDropShadowBorder Padding="10 10 10 20" Margin="0 50 0 0"> + <StackPanel> + <StackPanel Orientation="Horizontal" VerticalAlignment="Center"> + <TextBlock VerticalAlignment="Center" Foreground="{StaticResource TangoPrimaryAccentBrush}">Location</TextBlock> + </StackPanel> + + <StackPanel Margin="0 20 0 0"> + + <TextBlock>Please insert a storage device and select your backup file</TextBlock> + <DockPanel Height="50" Margin="0 20 0 0"> + <touch:TouchButton Command="{Binding BrowseForBackupCommand}" Margin="20 0 0 0" Width="150" DockPanel.Dock="Right" Foreground="{StaticResource TangoDarkForegroundBrush}" BorderBrush="{StaticResource TangoDarkForegroundBrush}" Style="{StaticResource TangoHollowButton}">BROWSE</touch:TouchButton> + <touch:TouchTextBox Text="{Binding BackupFileName}" IsReadOnly="True" /> + </DockPanel> + </StackPanel> + </StackPanel> + </touch:TouchDropShadowBorder> + + <touch:TouchDropShadowBorder Padding="10" Margin="0 10 0 0"> + <StackPanel> + <StackPanel Orientation="Horizontal" VerticalAlignment="Center"> + <TextBlock VerticalAlignment="Center" Foreground="{StaticResource TangoPrimaryAccentBrush}">Information</TextBlock> + </StackPanel> + + <StackPanel Margin="0 20 0 0"> + + <StackPanel> + <controls:TableGrid RowHeight="20"> + <TextBlock Text="Name:"></TextBlock> + <TextBlock Text="{Binding BackupFile.Name}"></TextBlock> + + <TextBlock Text="Date:"></TextBlock> + <TextBlock Text="{Binding BackupFile.Date}"></TextBlock> + + <TextBlock Text="Mode:"></TextBlock> + <TextBlock Text="{Binding BackupFile.Settings.Mode}"></TextBlock> + </controls:TableGrid> + </StackPanel> + + <StackPanel Margin="0 -20 0 0"> + <StackPanel.Style> + <Style TargetType="StackPanel"> + <Setter Property="Visibility" Value="Collapsed"></Setter> + <Style.Triggers> + <DataTrigger Binding="{Binding BackupFile.Settings.Mode}" Value="Jobs"> + <Setter Property="Visibility" Value="Visible"></Setter> + </DataTrigger> + </Style.Triggers> + </Style> + </StackPanel.Style> + <controls:TableGrid RowHeight="20"> + <TextBlock Text="Jobs:"></TextBlock> + <TextBlock Text="{Binding BackupFile.JobFiles.Count}"></TextBlock> + </controls:TableGrid> + </StackPanel> + + <StackPanel> + <StackPanel.Style> + <Style TargetType="StackPanel"> + <Setter Property="Visibility" Value="Collapsed"></Setter> + <Style.Triggers> + <DataTrigger Binding="{Binding BackupFile.Settings.Mode}" Value="Full"> + <Setter Property="Visibility" Value="Visible"></Setter> + </DataTrigger> + </Style.Triggers> + </Style> + </StackPanel.Style> + <controls:TableGrid RowHeight="20"> + <TextBlock Text="Application Version:"></TextBlock> + <TextBlock Text="{Binding BackupFile.ApplicationVersion}"></TextBlock> + + <TextBlock Text="Firmware Version:"></TextBlock> + <TextBlock Text="{Binding BackupFile.FirmwareVersion}"></TextBlock> + </controls:TableGrid> + </StackPanel> + </StackPanel> + </StackPanel> + </touch:TouchDropShadowBorder> + + <touch:TouchDropShadowBorder Padding="10" Margin="0 10 0 0"> + <touch:TouchDropShadowBorder.Style> + <Style TargetType="touch:TouchDropShadowBorder"> + <Setter Property="Visibility" Value="Visible"></Setter> + <Style.Triggers> + <DataTrigger Binding="{Binding BackupFile.Settings.Mode}" Value="Full"> + <Setter Property="Visibility" Value="Collapsed"></Setter> + </DataTrigger> + </Style.Triggers> + </Style> + </touch:TouchDropShadowBorder.Style> + <StackPanel> + <StackPanel Orientation="Horizontal" VerticalAlignment="Center"> + <TextBlock VerticalAlignment="Center" Foreground="{StaticResource TangoPrimaryAccentBrush}">Options</TextBlock> + </StackPanel> + + <StackPanel Margin="0 20 0 0" TextElement.Foreground="{StaticResource TangoDarkForegroundBrush}" TextElement.FontSize="{StaticResource TangoTitleFontSize}"> + <StackPanel.Resources> + <Style TargetType="touch:TouchCheckBox" BasedOn="{StaticResource {x:Type touch:TouchCheckBox}}"> + <Setter Property="Margin" Value="0 0 0 10"></Setter> + <Setter Property="Foreground" Value="{StaticResource TangoDarkForegroundBrush}"></Setter> + </Style> + + <Style x:Key="run" TargetType="Run"> + <Setter Property="Foreground" Value="{StaticResource TangoGrayTextBrush}"></Setter> + <Setter Property="FontSize" Value="{StaticResource TangoDefaultFontSize}"></Setter> + </Style> + </StackPanel.Resources> + <touch:TouchCheckBox IsChecked="{Binding RestoreSettings.OverwriteExistingJobs,Mode=TwoWay}"> + <touch:TouchCheckBox.Content> + <TextBlock> + <Run>Overwrite existing jobs</Run> + <Run Style="{StaticResource run}">(existing jobs will not change)</Run> + </TextBlock> + </touch:TouchCheckBox.Content> + </touch:TouchCheckBox> + <touch:TouchCheckBox IsChecked="{Binding RestoreSettings.AllowDeleteJobs,Converter={StaticResource BooleanInverseConverter},Mode=TwoWay}"> + <touch:TouchCheckBox.Content> + <TextBlock> + <Run>Do not remove existing jobs</Run> + <Run Style="{StaticResource run}">(existing jobs will not be deleted)</Run> + </TextBlock> + </touch:TouchCheckBox.Content> + </touch:TouchCheckBox> + </StackPanel> + </StackPanel> + </touch:TouchDropShadowBorder> + </StackPanel> + </DockPanel> </Grid> </UserControl> diff --git a/Software/Visual_Studio/PPC/Modules/Tango.PPC.Storage/ViewModels/MainViewVM.cs b/Software/Visual_Studio/PPC/Modules/Tango.PPC.Storage/ViewModels/MainViewVM.cs index 4a756e7ea..507110942 100644 --- a/Software/Visual_Studio/PPC/Modules/Tango.PPC.Storage/ViewModels/MainViewVM.cs +++ b/Software/Visual_Studio/PPC/Modules/Tango.PPC.Storage/ViewModels/MainViewVM.cs @@ -63,6 +63,14 @@ namespace Tango.PPC.Storage.ViewModels } } + private bool _displayItems; + public bool DisplayItems + { + get { return _displayItems; } + set { _displayItems = value; RaisePropertyChangedAuto(); } + } + + public RelayCommand<ExplorerFileItem> FileSelectedCommand { get; set; } public RelayCommand SaveCommand { get; set; } @@ -104,6 +112,8 @@ namespace Tango.PPC.Storage.ViewModels { View.EditFileName(); } + + DisplayItems = true; } else { @@ -116,6 +126,8 @@ namespace Tango.PPC.Storage.ViewModels public override void OnNavigatedFrom() { base.OnNavigatedFrom(); + DisplayItems = false; + Request = null; Request = new StorageNavigationRequest(); } @@ -158,7 +170,6 @@ namespace Tango.PPC.Storage.ViewModels { if (_allow_exit || CurrentPath == StorageProvider.Drive.RootDirectory.FullName) { - Request = null; return Task.FromResult(true); } else @@ -170,6 +181,7 @@ namespace Tango.PPC.Storage.ViewModels private async void OnFileSelected(ExplorerFileItem fileItem) { + _selectedItem = fileItem; _allow_exit = true; await NavigationManager.NavigateBack(); StorageProvider.SubmitFileSelection(fileItem); diff --git a/Software/Visual_Studio/PPC/Modules/Tango.PPC.Storage/Views/MainView.xaml b/Software/Visual_Studio/PPC/Modules/Tango.PPC.Storage/Views/MainView.xaml index 74307c9ce..25538a525 100644 --- a/Software/Visual_Studio/PPC/Modules/Tango.PPC.Storage/Views/MainView.xaml +++ b/Software/Visual_Studio/PPC/Modules/Tango.PPC.Storage/Views/MainView.xaml @@ -38,7 +38,7 @@ <StackPanel> <TextBlock Text="{Binding Request.Title}" FontSize="{StaticResource TangoHeaderFontSize}"></TextBlock> <DockPanel Margin="0 10 0 0"> - <touch:TouchButton Command="{Binding SaveCommand}" Margin="20 0 0 0" Height="50" Width="200" Style="{StaticResource TangoHollowButton}" CornerRadius="0" DockPanel.Dock="Right"> + <touch:TouchButton Command="{Binding SaveCommand}" Margin="20 0 0 0" Height="55" Width="200" Style="{StaticResource TangoHollowButton}" CornerRadius="25" DockPanel.Dock="Right"> <touch:TouchButton.Content> SAVE </touch:TouchButton.Content> @@ -49,7 +49,7 @@ </Border> </Grid> - <explorer:ExplorerControl x:Name="explorer" CurrentPath="{Binding CurrentPath,Mode=TwoWay}" FileSelectedCommand="{Binding FileSelectedCommand}" Filter="{Binding Request.Filter}"> + <explorer:ExplorerControl x:Name="explorer" CurrentPath="{Binding CurrentPath,Mode=TwoWay}" FileSelectedCommand="{Binding FileSelectedCommand}" Filter="{Binding Request.Filter}" Visibility="{Binding DisplayItems,Converter={StaticResource BooleanToVisibilityConverter}}"> <explorer:ExplorerControl.Style> <Style TargetType="explorer:ExplorerControl" BasedOn="{StaticResource {x:Type explorer:ExplorerControl}}"> <Setter Property="EnableFileSelection" Value="True"></Setter> diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/BackupRestore/BackupFile.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/BackupRestore/BackupFile.cs index ebb7b9fcd..c687377a6 100644 --- a/Software/Visual_Studio/PPC/Tango.PPC.Common/BackupRestore/BackupFile.cs +++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/BackupRestore/BackupFile.cs @@ -1,4 +1,5 @@ -using System; +using Newtonsoft.Json; +using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -14,6 +15,11 @@ namespace Tango.PPC.Common.BackupRestore public class BackupFile { /// <summary> + /// Gets or sets the backup file version. + /// </summary> + public int Version { get; set; } + + /// <summary> /// Gets or sets the backup name. /// </summary> public String Name { get; set; } @@ -61,5 +67,15 @@ namespace Tango.PPC.Common.BackupRestore Settings = new BackupSettings(); JobFiles = new List<JobFile>(); } + + public String ToJson() + { + return JsonConvert.SerializeObject(this); + } + + public static BackupFile FromJson(String json) + { + return JsonConvert.DeserializeObject<BackupFile>(json); + } } } diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/BackupRestore/BackupRestoreStage.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/BackupRestore/BackupRestoreStage.cs index 3d5de1122..27fc06ec4 100644 --- a/Software/Visual_Studio/PPC/Tango.PPC.Common/BackupRestore/BackupRestoreStage.cs +++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/BackupRestore/BackupRestoreStage.cs @@ -30,12 +30,22 @@ namespace Tango.PPC.Common.BackupRestore CompressingFiles, //Restore - [Description("Validating backup...")] - ValidatingBackup, + [Description("Extracting backup configuration...")] + ExtractingBackupConfiguration, [Description("Extracting content...")] ExtractingContent, + [Description("Restoring user settings...")] + RestoringSettings, + [Description("Restoring jobs...")] + RestoringJobs, [Description("Restoring data...")] RestoringDatabase, + [Description("Removing temporary files...")] + RemovingTemporaryFiles, + [Description("Restoring firmware version...")] + RestoringFirmware, + [Description("Rolling back changes...")] + RollingBackChanges, [Description("Done")] diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/BackupRestore/DefaultBackupManager.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/BackupRestore/DefaultBackupManager.cs index 31bdefd5d..05693c794 100644 --- a/Software/Visual_Studio/PPC/Tango.PPC.Common/BackupRestore/DefaultBackupManager.cs +++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/BackupRestore/DefaultBackupManager.cs @@ -16,6 +16,9 @@ using Tango.Settings; using Tango.Core.DB; using System.Data.SqlClient; using Ionic.Zip; +using Tango.BL.Entities; +using Tango.PPC.Common.Authentication; +using Tango.Integration.Upgrade; namespace Tango.PPC.Common.BackupRestore { @@ -23,6 +26,7 @@ namespace Tango.PPC.Common.BackupRestore { private const string BACKUP_FILE_NAME = "Backup.json"; private const string DATABASE_FILE_NAME = "Tango.bak"; + private const int VERSION = 1; [TangoInject(TangoInjectMode.WhenAvailable)] private IPPCApplicationManager _applicationManager; @@ -30,6 +34,9 @@ namespace Tango.PPC.Common.BackupRestore [TangoInject(TangoInjectMode.WhenAvailable)] private IMachineProvider _machineProvider; + [TangoInject(TangoInjectMode.WhenAvailable)] + private IAuthenticationProvider _authenticationProvider; + public DefaultBackupManager() { TangoIOC.Default.Inject(this); @@ -41,17 +48,19 @@ namespace Tango.PPC.Common.BackupRestore { return Task.Factory.StartNew(() => { + var tempFolder = TemporaryManager.CreateFolder(); + try { //Basic - LogManager.Log($"Starting backup operation to file {filePath}."); + LogManager.Log($"Starting backup operation to file '{filePath}'..."); LogManager.Log($"Backup settings:\n{settings.ToJsonString()}"); OnProgress(BackupRestoreStage.Initializing); - var tempFolder = TemporaryManager.CreateFolder(); LogManager.Log($"Temporary folder created on {tempFolder.Path}."); BackupFile backupFile = new BackupFile(); + backupFile.Version = VERSION; backupFile.Date = DateTime.Now; backupFile.Settings = settings; backupFile.Name = name; @@ -134,7 +143,14 @@ namespace Tango.PPC.Common.BackupRestore var dataSource = ObservablesContext.GetActualDataSource(); using (var dbManager = DbManager.FromDataSource(dataSource)) { - dbManager.Backup(dataSource.Catalog, Path.Combine(tempFolder, DATABASE_FILE_NAME)); + Directory.CreateDirectory("C:\\Backups"); + var dbBackupFile = $"C:\\Backups\\{DATABASE_FILE_NAME}"; + if (File.Exists(dbBackupFile)) + { + File.Delete(dbBackupFile); + } + dbManager.Backup(dataSource.Catalog, dbBackupFile); + File.Move(dbBackupFile, Path.Combine(tempFolder, DATABASE_FILE_NAME)); } } catch (Exception ex) @@ -145,7 +161,7 @@ namespace Tango.PPC.Common.BackupRestore LogManager.Log("Database backup completed."); } - //Compression + //Backup.json try { OnProgress(BackupRestoreStage.WritingConfiguration); @@ -158,6 +174,7 @@ namespace Tango.PPC.Common.BackupRestore throw new IOException("Error writing backup configuration file.", ex); } + //Compression LogManager.Log($"Generating {filePath}..."); using (ZipFile zip = new ZipFile()) { @@ -176,22 +193,361 @@ namespace Tango.PPC.Common.BackupRestore zip.Save(filePath); } + tempFolder.Delete(); + + //Done LogManager.Log("Backup operation completed!!!"); - OnProgress(BackupRestoreStage.Done); + OnProgress(BackupRestoreStage.Done, 100, 100, false); } catch (Exception ex) { - OnProgress(BackupRestoreStage.Error); + tempFolder.Delete(); + + OnProgress(BackupRestoreStage.Error, 100, 100, false); LogManager.Log(ex, "Could not complete the backup operation."); throw ex; } }); } - public Task Restore(string filePath) + /// <summary> + /// Extracts the backup configuration from the specified backup file. + /// </summary> + /// <param name="filePath">The file path.</param> + /// <returns></returns> + public Task<BackupFile> ExtractBackupConfiguration(string filePath) + { + return Task.Factory.StartNew<BackupFile>(() => + { + using (ZipFile zip = ZipFile.Read(filePath)) + { + var reader = zip.Entries.SingleOrDefault(x => x.FileName == BACKUP_FILE_NAME).OpenReader(); + String json = String.Empty; + + using (StreamReader stReader = new StreamReader(reader)) + { + json = stReader.ReadToEnd(); + } + + var backupFile = BackupFile.FromJson(json); + reader.Close(); + reader.Dispose(); + return backupFile; + } + }); + } + + public Task<RestoreResult> Restore(string filePath, RestoreSettings settings) { - throw new NotImplementedException(); + TaskCompletionSource<RestoreResult> completionSource = new TaskCompletionSource<RestoreResult>(); + + String dbRollbackFile = null; + + Task.Factory.StartNew(() => + { + LogManager.Log($"Starting restore operation from file '{filePath}'..."); + OnProgress(BackupRestoreStage.Initializing); + + var tempFolder = TemporaryManager.CreateFolder(); + + var restoreResult = new RestoreResult() { FolderPath = tempFolder }; + + try + { + try + { + LogManager.Log("Creating database rollback file..."); + var dataSource = ObservablesContext.GetActualDataSource(); + using (var dbManager = DbManager.FromDataSource(dataSource)) + { + Directory.CreateDirectory("C:\\Backups"); + dbRollbackFile = $"C:\\Backups\\{Path.GetRandomFileName()}.bak"; + LogManager.Log($"Creating database rollback to '{dbRollbackFile}'..."); + dbManager.Backup(dataSource.Catalog, dbRollbackFile); + LogManager.Log("Database rollback created successfully."); + } + } + catch (Exception ex) + { + throw new InvalidDataException("Error creating database rollback file.", ex); + } + + //Basic + LogManager.Log("Extracting backup file configuration..."); + + BackupFile backupFile = null; + + //Extract Configuration + try + { + OnProgress(BackupRestoreStage.ExtractingBackupConfiguration); + backupFile = ExtractBackupConfiguration(filePath).Result; + } + catch (Exception ex) + { + throw new IOException("Error extracting backup configuration.", ex); + } + + //Validate Version + if (backupFile.Version > VERSION) + { + throw new NotSupportedException($"Backup file version {backupFile} is not supported."); + } + + LogManager.Log($"Backup settings:\n{backupFile.Settings.ToJsonString()}"); + + if (backupFile.Settings.Mode == BackupMode.Jobs) + { + //Restore Jobs + OnProgress(BackupRestoreStage.RestoringJobs); + LogManager.Log("Starting jobs restore..."); + + using (ObservablesContext db = ObservablesContext.CreateDefault()) + { + var jobs = db.Jobs.ToList(); + var jobFiles = backupFile.JobFiles; + + if (settings.AllowDeleteJobs) + { + try + { + LogManager.Log("Removing existing jobs..."); + foreach (var job in jobs.ToList()) + { + LogManager.Log($"Removing job '{job.Name}'..."); + job.Delete(db); + jobs.Remove(job); + } + + db.SaveChanges(); + } + catch (Exception ex) + { + throw new Exception("Error removing existing jobs from database.", ex); + } + } + + foreach (var jobFile in jobFiles) + { + LogManager.Log($"Importing job '{jobFile.Name}'..."); + + try + { + var existingJob = jobs.FirstOrDefault(x => x.Name == jobFile.Name); + + if (existingJob != null) + { + if (settings.OverwriteExistingJobs) + { + try + { + LogManager.Log("Job already exist, overwriting..."); + + var newJob = Job.FromJobFile(jobFile, _machineProvider.Machine.Guid, _authenticationProvider.CurrentUser.Guid).Result; + newJob.Guid = existingJob.Guid; + + existingJob.Delete(db); + jobs.Remove(existingJob); + + db.SaveChanges(); + db.Jobs.Add(newJob); + db.SaveChanges(); + } + catch (Exception ex) + { + throw new InvalidOperationException("Error overwriting job.", ex); + } + } + } + else + { + var newJob = Job.FromJobFile(jobFile, _machineProvider.Machine.Guid, _authenticationProvider.CurrentUser.Guid).Result; + db.Jobs.Add(newJob); + } + } + catch (Exception ex) + { + throw new InvalidOperationException("Error importing job.", ex); + } + + OnProgress(BackupRestoreStage.RestoringJobs, jobFiles.IndexOf(jobFile) + 1, jobFiles.Count, false); + } + + OnProgress(BackupRestoreStage.RestoringJobs); + db.SaveChanges(); + } + } + else + { + //Extract zip file + LogManager.Log("Starting backup file extraction..."); + OnProgress(BackupRestoreStage.ExtractingContent); + try + { + using (ZipFile zip = new ZipFile(filePath)) + { + zip.ExtractProgress += (x, e) => + { + if (e.EventType == ZipProgressEventType.Extracting_AfterExtractEntry) + { + LogManager.Log($"Extracting '{e.CurrentEntry.FileName}'..."); + OnProgress(BackupRestoreStage.ExtractingContent, e.EntriesExtracted + 1, e.EntriesTotal, false); + } + }; + + zip.ParallelDeflateThreshold = -1; + zip.ExtractAll(tempFolder); + } + } + catch (Exception ex) + { + throw new IOException("Error extracting backup content.", ex); + } + + //Overwrite settings + LogManager.Log("Validating user settings..."); + if (backupFile.SettingsFile != null) + { + try + { + LogManager.Log("Overwriting settings file..."); + OnProgress(BackupRestoreStage.RestoringSettings); + File.WriteAllText(SettingsManager.Default.FilePath, backupFile.SettingsFile); + } + catch (Exception ex) + { + throw new IOException("Error overwriting user settings.", ex); + } + } + else + { + LogManager.Log("No user settings, skipping..."); + } + + //Restore database + var backupFilePath = Path.Combine(tempFolder, DATABASE_FILE_NAME); + LogManager.Log($"Looking for file database backup on '{backupFilePath}'..."); + if (File.Exists(backupFilePath)) + { + LogManager.Log("Restoring database..."); + OnProgress(BackupRestoreStage.RestoringDatabase); + try + { + var dataSource = ObservablesContext.GetActualDataSource(); + using (var dbManager = DbManager.FromDataSource(dataSource)) + { + Directory.CreateDirectory("C:\\Backups"); + var dbBackupFile = $"C:\\Backups\\{DATABASE_FILE_NAME}"; + File.Copy(backupFilePath, dbBackupFile, true); + dbManager.Restore(dataSource.Catalog, dbBackupFile); + File.Delete(dbBackupFile); + } + } + catch (Exception ex) + { + throw new IOException("Error restoring database backup", ex); + } + + LogManager.Log("Database backup completed."); + } + else + { + LogManager.Log("Database backup file not found, skipping..."); + } + + //Remove extra files from application temp folder + OnProgress(BackupRestoreStage.RemovingTemporaryFiles); + LogManager.Log("Removing redundant files from temp folder..."); + try + { + File.Delete(backupFilePath); + } + catch { } + try + { + File.Delete(Path.Combine(tempFolder, BACKUP_FILE_NAME)); + } + catch { } + + //Update firmware + var tfpFile = Path.Combine(tempFolder, "firmware_package.tfp"); + LogManager.Log($"Looking for tfp file on '{tfpFile}'..."); + if (File.Exists(tfpFile)) + { + OnProgress(BackupRestoreStage.RestoringFirmware); + LogManager.Log("Restoring firmware version..."); + + var stream = new FileStream(tfpFile, FileMode.Open); + + _machineProvider.MachineOperator.FirmwareUpgradeMode = FirmwareUpgradeModes.DFU | FirmwareUpgradeModes.TFP_PACKAGE; + + var handler = _machineProvider.MachineOperator.UpgradeFirmware(stream).Result; + handler.Failed += (_, ex) => + { + stream.Dispose(); + throw ex; + }; + handler.Completed += (_, __) => + { + OnProgress(BackupRestoreStage.RestoringFirmware, 100, 100, false); + stream.Dispose(); + OnProgress(BackupRestoreStage.Done, 100, 100, false); + completionSource.SetResult(restoreResult); + }; + handler.Canceled += (_, __) => + { + stream.Dispose(); + throw new Exception("The operation has been canceled."); + }; + handler.Progress += (_, e) => + { + OnProgress(BackupRestoreStage.RestoringFirmware, e.Current, e.Total, false); + }; + } + else + { + LogManager.Log("Firmware package file not found, skipping..."); + OnProgress(BackupRestoreStage.Done, 100, 100, false); + completionSource.SetResult(restoreResult); + } + } + } + catch (Exception ex) + { + LogManager.Log("Rolling back database changes..."); + + var dataSource = ObservablesContext.GetActualDataSource(); + using (var dbManager = DbManager.FromDataSource(dataSource)) + { + try + { + OnProgress(BackupRestoreStage.RollingBackChanges); + dbManager.Restore(dataSource.Catalog, dbRollbackFile); + LogManager.Log("Database restored successfully."); + } + catch (Exception e) + { + LogManager.Log(e, "Error rolling back database."); + } + finally + { + try + { + File.Delete(dbRollbackFile); + } + catch { } + } + } + + tempFolder.Delete(); + OnProgress(BackupRestoreStage.Error, 100, 100, false); + LogManager.Log(ex, "Could not complete the restore operation."); + completionSource.SetException(ex); + } + }); + + return completionSource.Task; } protected virtual void OnProgress(BackupRestoreStage stage, double progress = 0, double maxProgress = 100, bool isIntermediate = true) diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/BackupRestore/IBackupManager.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/BackupRestore/IBackupManager.cs index 8ff8a434c..ae1884677 100644 --- a/Software/Visual_Studio/PPC/Tango.PPC.Common/BackupRestore/IBackupManager.cs +++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/BackupRestore/IBackupManager.cs @@ -29,7 +29,15 @@ namespace Tango.PPC.Common.BackupRestore /// Restores a backup located in the specified file path. /// </summary> /// <param name="filePath">The file path.</param> + /// <param name="settings">The restore settings</param> /// <returns></returns> - Task Restore(String filePath); + Task<RestoreResult> Restore(String filePath, RestoreSettings settings); + + /// <summary> + /// Extracts the backup configuration from the specified backup file. + /// </summary> + /// <param name="filePath">The file path.</param> + /// <returns></returns> + Task<BackupFile> ExtractBackupConfiguration(String filePath); } } diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/BackupRestore/RestoreResult.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/BackupRestore/RestoreResult.cs index 0e1ac1a13..34d7c6298 100644 --- a/Software/Visual_Studio/PPC/Tango.PPC.Common/BackupRestore/RestoreResult.cs +++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/BackupRestore/RestoreResult.cs @@ -6,7 +6,8 @@ using System.Threading.Tasks; namespace Tango.PPC.Common.BackupRestore { - class RestoreResult + public class RestoreResult { + public String FolderPath { get; set; } } } diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/BackupRestore/RestoreSettings.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/BackupRestore/RestoreSettings.cs index 4e7bb2a55..a5b343302 100644 --- a/Software/Visual_Studio/PPC/Tango.PPC.Common/BackupRestore/RestoreSettings.cs +++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/BackupRestore/RestoreSettings.cs @@ -3,10 +3,29 @@ using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; +using Tango.Core; namespace Tango.PPC.Common.BackupRestore { - class RestoreSettings + public class RestoreSettings : ExtendedObject { + private bool _allowDeleteJobs; + public bool AllowDeleteJobs + { + get { return _allowDeleteJobs; } + set { _allowDeleteJobs = value; RaisePropertyChangedAuto(); } + } + + private bool _overwriteExistingJobs; + public bool OverwriteExistingJobs + { + get { return _overwriteExistingJobs; } + set { _overwriteExistingJobs = value; RaisePropertyChangedAuto(); } + } + + public RestoreSettings() + { + OverwriteExistingJobs = true; + } } } diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/Tango.PPC.Common.csproj b/Software/Visual_Studio/PPC/Tango.PPC.Common/Tango.PPC.Common.csproj index cff1e5a71..6375140d7 100644 --- a/Software/Visual_Studio/PPC/Tango.PPC.Common/Tango.PPC.Common.csproj +++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/Tango.PPC.Common.csproj @@ -127,6 +127,8 @@ <Compile Include="BackupRestore\BackupRestoreStage.cs" /> <Compile Include="BackupRestore\DefaultBackupManager.cs" /> <Compile Include="BackupRestore\IBackupManager.cs" /> + <Compile Include="BackupRestore\RestoreResult.cs" /> + <Compile Include="BackupRestore\RestoreSettings.cs" /> <Compile Include="Connection\DefaultMachineProvider.cs" /> <Compile Include="Connection\IMachineProvider.cs" /> <Compile Include="Connection\MachineOperatorChangedEventArgs.cs" /> diff --git a/Software/Visual_Studio/PPC/Tango.PPC.UI/Navigation/DefaultNavigationManager.cs b/Software/Visual_Studio/PPC/Tango.PPC.UI/Navigation/DefaultNavigationManager.cs index fe4be700c..8914d55a5 100644 --- a/Software/Visual_Studio/PPC/Tango.PPC.UI/Navigation/DefaultNavigationManager.cs +++ b/Software/Visual_Studio/PPC/Tango.PPC.UI/Navigation/DefaultNavigationManager.cs @@ -287,7 +287,7 @@ namespace Tango.PPC.UI.Navigation } else { - await Task.Delay(500); + await Task.Delay(navigationControl.TransitionDuration.TimeSpan); if (fromVM is PPCViewModel) { @@ -304,7 +304,7 @@ namespace Tango.PPC.UI.Navigation } else { - await Task.Delay(500); + await Task.Delay(navigationControl.TransitionDuration.TimeSpan); if (fromVM is PPCViewModel) { diff --git a/Software/Visual_Studio/PPC/Tango.PPC.UI/Tango.PPC.UI.csproj b/Software/Visual_Studio/PPC/Tango.PPC.UI/Tango.PPC.UI.csproj index c28a49584..e71e5794d 100644 --- a/Software/Visual_Studio/PPC/Tango.PPC.UI/Tango.PPC.UI.csproj +++ b/Software/Visual_Studio/PPC/Tango.PPC.UI/Tango.PPC.UI.csproj @@ -359,6 +359,9 @@ <Resource Include="Images\GlobalStatus\service.png" /> <Content Include="Manifests\release.xml" /> <Content Include="Manifests\debug.xml" /> + <None Include="firmware_package.tfp"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </None> <None Include="packages.config" /> <None Include="Properties\Settings.settings"> <Generator>SettingsSingleFileGenerator</Generator> @@ -596,7 +599,7 @@ copy /Y "$(SolutionDir)Referenced Assemblies\vcruntime140.dll" "$(TargetDir)" copy /Y "$(SolutionDir)Referenced Assemblies\vcruntime140d.dll" "$(TargetDir)" copy /Y "$(SolutionDir)Referenced Assemblies\Microsoft.WITDataStore32.dll" "$(TargetDir)" -del "$(TargetDir)firmware_package.tfp" +if $(ConfigurationName) == Release del "$(TargetDir)firmware_package.tfp" if $(ConfigurationName) == Release del *.xml</PostBuildEvent> </PropertyGroup> diff --git a/Software/Visual_Studio/PPC/Tango.PPC.UI/firmware_package.tfp b/Software/Visual_Studio/PPC/Tango.PPC.UI/firmware_package.tfp Binary files differnew file mode 100644 index 000000000..bc33e385a --- /dev/null +++ b/Software/Visual_Studio/PPC/Tango.PPC.UI/firmware_package.tfp |
