diff options
| author | Roy Ben Shabat <Roy.mail.net@gmail.com> | 2019-12-09 16:19:41 +0200 |
|---|---|---|
| committer | Roy Ben Shabat <Roy.mail.net@gmail.com> | 2019-12-09 16:19:41 +0200 |
| commit | c7fa53084c674586ceee773ccbdc6b4c0a2ec7d4 (patch) | |
| tree | 1642092f35d6e02de502909c3db97131e66bfaca /Software/Visual_Studio | |
| parent | aeabb681d16451e191a1620d4eebf40549771491 (diff) | |
| download | Tango-c7fa53084c674586ceee773ccbdc6b4c0a2ec7d4.tar.gz Tango-c7fa53084c674586ceee773ccbdc6b4c0a2ec7d4.zip | |
Implemented full TUP package update !!!
Diffstat (limited to 'Software/Visual_Studio')
32 files changed, 1257 insertions, 144 deletions
diff --git a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.MachineDesigner/Tango.MachineStudio.MachineDesigner.csproj b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.MachineDesigner/Tango.MachineStudio.MachineDesigner.csproj index 6225bd7ad..69cc75461 100644 --- a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.MachineDesigner/Tango.MachineStudio.MachineDesigner.csproj +++ b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.MachineDesigner/Tango.MachineStudio.MachineDesigner.csproj @@ -93,6 +93,7 @@ <Compile Include="ViewModels\MachineUpdatesViewVM.cs" /> <Compile Include="ViewModels\MachineVersionDialogVM.cs" /> <Compile Include="ViewModels\MainViewVM.cs" /> + <Compile Include="ViewModels\TupViewVM.cs" /> <Compile Include="Views\ColorCalibrationView.xaml.cs"> <DependentUpon>ColorCalibrationView.xaml</DependentUpon> </Compile> @@ -130,6 +131,9 @@ <Compile Include="Views\MachineUpdatesView.xaml.cs"> <DependentUpon>MachineUpdatesView.xaml</DependentUpon> </Compile> + <Compile Include="Views\TupView.xaml.cs"> + <DependentUpon>TupView.xaml</DependentUpon> + </Compile> <Compile Include="Views\SpoolsView.xaml.cs"> <DependentUpon>SpoolsView.xaml</DependentUpon> </Compile> @@ -185,6 +189,10 @@ <Generator>MSBuild:Compile</Generator> <SubType>Designer</SubType> </Page> + <Page Include="Views\TupView.xaml"> + <Generator>MSBuild:Compile</Generator> + <SubType>Designer</SubType> + </Page> <Page Include="Views\SpoolsView.xaml"> <SubType>Designer</SubType> <Generator>MSBuild:Compile</Generator> diff --git a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.MachineDesigner/ViewModels/MainViewVM.cs b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.MachineDesigner/ViewModels/MainViewVM.cs index 820950290..768f93de1 100644 --- a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.MachineDesigner/ViewModels/MainViewVM.cs +++ b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.MachineDesigner/ViewModels/MainViewVM.cs @@ -140,6 +140,12 @@ namespace Tango.MachineStudio.MachineDesigner.ViewModels set { _machineUpdatesViewVM = value; RaisePropertyChangedAuto(); } } + private TupViewVM _tupViewVM; + public TupViewVM TupViewVM + { + get { return _tupViewVM; } + set { _tupViewVM = value; RaisePropertyChangedAuto(); } + } #endregion @@ -233,6 +239,7 @@ namespace Tango.MachineStudio.MachineDesigner.ViewModels ResetDeviceRegistrationCommand = new RelayCommand(ResetDeviceRegistration); MachineUpdatesViewVM = new MachineUpdatesViewVM(_notification); + TupViewVM = new TupViewVM(_notification); } #endregion @@ -478,6 +485,7 @@ namespace Tango.MachineStudio.MachineDesigner.ViewModels HardwareConfigurationViewVM.Init(ActiveMachine.Configuration); await MachineUpdatesViewVM.Init(ActiveMachine, ActiveMachineAdapter.Context); + TupViewVM.Init(ActiveMachine); ActiveMachine.Configuration.HardwareVersionChanged += Configuration_HardwareVersionChanged; diff --git a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.MachineDesigner/ViewModels/TupViewVM.cs b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.MachineDesigner/ViewModels/TupViewVM.cs new file mode 100644 index 000000000..12ed09c75 --- /dev/null +++ b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.MachineDesigner/ViewModels/TupViewVM.cs @@ -0,0 +1,129 @@ +using Microsoft.Win32; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Tango.BL.Entities; +using Tango.Core.Commands; +using Tango.MachineStudio.Common.Notifications; +using Tango.MachineStudio.Common.Tup; +using Tango.SharedUI; + +namespace Tango.MachineStudio.MachineDesigner.ViewModels +{ + public class TupViewVM : ViewModel + { + private INotificationProvider _notification; + + private String _latestVersion; + public String LatestVersion + { + get { return _latestVersion; } + set { _latestVersion = value; RaisePropertyChangedAuto(); } + } + + private Machine _machine; + public Machine Machine + { + get { return _machine; } + set { _machine = value; RaisePropertyChangedAuto(); } + } + + private String _filePath; + public String FilePath + { + get { return _filePath; } + set { _filePath = value; RaisePropertyChangedAuto(); InvalidateRelayCommands(); } + } + + private TupFileBuilderProgressEventArgs _progress; + public TupFileBuilderProgressEventArgs Progress + { + get { return _progress; } + set { _progress = value; RaisePropertyChangedAuto(); } + } + + public RelayCommand CreateTupFileCommand { get; set; } + + public RelayCommand SelectFileCommand { get; set; } + + public TupViewVM() + { + + } + + public TupViewVM(INotificationProvider notification) : this() + { + _notification = notification; + CreateTupFileCommand = new RelayCommand(CreateTupFile, () => FilePath != null && IsFree); + SelectFileCommand = new RelayCommand(SelectFile); + } + + public void Init(Machine machine) + { + Machine = machine; + DisplayLatestPPCVersion(); + } + + private async void DisplayLatestPPCVersion() + { + TupFileBuilder builder = new TupFileBuilder(); + + try + { + LatestVersion = await builder.GetLatestPPCVersion(Machine.SerialNumber); + } + catch (Exception ex) + { + LogManager.Log(ex, "Error retrieving latest PPC version."); + await Task.Delay(5000); + DisplayLatestPPCVersion(); + } + } + + private void SelectFile() + { + SaveFileDialog dlg = new SaveFileDialog(); + dlg.Title = "Select package location"; + dlg.Filter = "Tango Update Package Files|*.tup"; + dlg.DefaultExt = ".tup"; + dlg.FileName = LatestVersion == null ? $"{Machine.SerialNumber}_Update.tup" : $"{Machine.SerialNumber}_Update_v{LatestVersion}.tup"; + + if (dlg.ShowDialog().Value) + { + FilePath = dlg.FileName; + } + } + + private async void CreateTupFile() + { + try + { + LogManager.Log($"Generating TUP file to '{FilePath}'..."); + + IsFree = false; + TupFileBuilder builder = new TupFileBuilder(); + builder.Progress += Builder_Progress; + await builder.Build(Machine.SerialNumber, FilePath); + + LogManager.Log("TUP file generated successfully."); + _notification.ShowInfo("Tango update package created successfuly."); + } + catch (Exception ex) + { + LogManager.Log(ex, "Error generating tup file."); + _notification.ShowError($"An error occurred while generating the .tup file.\n{ex.FlattenMessage()}"); + } + finally + { + IsFree = true; + } + } + + private void Builder_Progress(object sender, TupFileBuilderProgressEventArgs e) + { + Progress = e; + } + } +} diff --git a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.MachineDesigner/Views/MachineDetailsView.xaml b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.MachineDesigner/Views/MachineDetailsView.xaml index 666a4ee4a..bcca83ca5 100644 --- a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.MachineDesigner/Views/MachineDetailsView.xaml +++ b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.MachineDesigner/Views/MachineDetailsView.xaml @@ -64,6 +64,9 @@ <TabItem Header="UPDATES"> <local:MachineUpdatesView/> </TabItem> + <TabItem Header="TUP"> + <local:TupView/> + </TabItem> </TabControl> </Grid> </DockPanel> diff --git a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.MachineDesigner/Views/TupView.xaml b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.MachineDesigner/Views/TupView.xaml new file mode 100644 index 000000000..895a26ca0 --- /dev/null +++ b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.MachineDesigner/Views/TupView.xaml @@ -0,0 +1,60 @@ +<UserControl x:Class="Tango.MachineStudio.MachineDesigner.Views.TupView" + 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:vm="clr-namespace:Tango.MachineStudio.MachineDesigner.ViewModels" + xmlns:global="clr-namespace:Tango.MachineStudio.MachineDesigner" + xmlns:mahapps="http://metro.mahapps.com/winfx/xaml/controls" + xmlns:material="http://materialdesigninxaml.net/winfx/xaml/themes" + xmlns:controls="clr-namespace:Tango.SharedUI.Controls;assembly=Tango.SharedUI" + xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes" + xmlns:local="clr-namespace:Tango.MachineStudio.MachineDesigner.Views" + mc:Ignorable="d" + d:DesignHeight="720" d:DesignWidth="1280" d:DataContext="{d:DesignInstance Type=vm:MainViewVM, IsDesignTimeCreatable=False}" DataContext="{x:Static global:ViewModelLocator.MainViewVM}" Background="Transparent"> + + <Grid Margin="20" DataContext="{Binding TupViewVM}"> + <DockPanel IsEnabled="{Binding IsFree}"> + <Grid DockPanel.Dock="Bottom"> + + </Grid> + + <Grid Margin="0 20"> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="38*"/> + <ColumnDefinition Width="117*"/> + </Grid.ColumnDefinitions> + + <local:MachineView Margin="30 80 0 0" DataContext="{Binding Machine}" IsHitTestVisible="False" VerticalAlignment="Top" Height="241" /> + + <DockPanel Grid.Column="1" Margin="50 100 300 100"> + <TextBlock DockPanel.Dock="Top"> + <Run>Create a complete update package (.tup) in order to update this machine offline using a removable storage device.</Run> + <LineBreak/> + <Run>The latest PPC version is </Run> + <Run Foreground="{StaticResource AccentColorBrush}" FontWeight="SemiBold" Text="{Binding LatestVersion,FallbackValue='loading...',TargetNullValue='loading...'}"></Run> + </TextBlock> + <Grid Margin="0 20 0 0"> + <Border Background="{StaticResource Transparent200}" CornerRadius="5" Padding="60" BorderThickness="1" BorderBrush="Silver"> + <DockPanel> + <DockPanel DockPanel.Dock="Top" Height="40"> + <Button Command="{Binding SelectFileCommand}" DockPanel.Dock="Right" BorderBrush="Silver" BorderThickness="1" Padding="0" Height="Auto" Width="40" Margin="5 0 0 0" Style="{StaticResource MaterialDesignFlatButton}"> + <material:PackIcon Kind="Folder" /> + </Button> + <TextBox materialDesign:HintAssist.Hint="Select package location" BorderBrush="Silver" BorderThickness="1" Text="{Binding FilePath}" IsReadOnly="True" VerticalContentAlignment="Center" Padding="10 0 0 0"></TextBox> + </DockPanel> + <StackPanel DockPanel.Dock="Bottom"> + <TextBlock HorizontalAlignment="Center" Text="{Binding Progress.Message,FallbackValue='Ready',TargetNullValue='Ready'}"></TextBlock> + <ProgressBar Margin="0 10 0 0" Height="15" IsIndeterminate="{Binding Progress.IsIntermediate}" Value="{Binding Progress.Progress}" Maximum="{Binding Progress.Total}"></ProgressBar> + </StackPanel> + <Grid> + <Button Command="{Binding CreateTupFileCommand}" HorizontalAlignment="Center" VerticalAlignment="Center" Padding="20 10" Height="60" Width="300">GENERATE UPDATE PACKAGE</Button> + </Grid> + </DockPanel> + </Border> + </Grid> + </DockPanel> + </Grid> + </DockPanel> + </Grid> +</UserControl> diff --git a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.MachineDesigner/Views/TupView.xaml.cs b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.MachineDesigner/Views/TupView.xaml.cs new file mode 100644 index 000000000..fe01296d8 --- /dev/null +++ b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.MachineDesigner/Views/TupView.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.MachineStudio.MachineDesigner.Views +{ + /// <summary> + /// Interaction logic for SpoolsView.xaml + /// </summary> + public partial class TupView : UserControl + { + public TupView() + { + InitializeComponent(); + } + } +} diff --git a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.Common/Tango.MachineStudio.Common.csproj b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.Common/Tango.MachineStudio.Common.csproj index 7c0851d01..3ce667afe 100644 --- a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.Common/Tango.MachineStudio.Common.csproj +++ b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.Common/Tango.MachineStudio.Common.csproj @@ -59,6 +59,9 @@ <HintPath>..\..\Referenced Assemblies\SMO\Microsoft.SqlServer.AzureStorageEnum.dll</HintPath> </Reference> <Reference Include="Microsoft.WindowsAzure.Storage, Version=4.3.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL" /> + <Reference Include="Newtonsoft.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL"> + <HintPath>..\..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll</HintPath> + </Reference> <Reference Include="System" /> <Reference Include="System.ComponentModel.DataAnnotations" /> <Reference Include="System.Data" /> @@ -83,6 +86,9 @@ <Reference Include="PresentationFramework" /> </ItemGroup> <ItemGroup> + <Compile Include="..\..\PPC\Tango.PPC.Common\Publish\PublishInfo.cs"> + <Link>Tup\PublishInfo.cs</Link> + </Compile> <Compile Include="..\..\Versioning\GlobalVersionInfo.cs"> <Link>GlobalVersionInfo.cs</Link> </Compile> @@ -95,6 +101,10 @@ <Compile Include="Converters\UserRoleToVisibilityConverter.cs" /> <Compile Include="MachineStudioTheme.cs" /> <Compile Include="Resources\SharedResourceDictionary.cs" /> + <Compile Include="Tup\TupFileBuilder.cs" /> + <Compile Include="Tup\TupFileBuilderProgressEventArgs.cs" /> + <Compile Include="Web\DownloadLatestPPCVersionRequest.cs" /> + <Compile Include="Web\DownloadLatestPPCVersionResponse.cs" /> <Compile Include="Web\LoginRequest.cs" /> <Compile Include="Web\LoginResponse.cs" /> <Compile Include="AutoComplete\MachinesProvider.cs" /> @@ -291,6 +301,10 @@ <Project>{8491d07b-c1f6-4b62-a412-41b9fd2d6538}</Project> <Name>Tango.SharedUI</Name> </ProjectReference> + <ProjectReference Include="..\..\Tango.SQLExaminer\Tango.SQLExaminer.csproj"> + <Project>{e1e66ed9-597d-45fa-8048-de90a6930484}</Project> + <Name>Tango.SQLExaminer</Name> + </ProjectReference> <ProjectReference Include="..\..\Tango.Transport\Tango.Transport.csproj"> <Project>{74e700b0-1156-4126-be40-ee450d3c3026}</Project> <Name>Tango.Transport</Name> @@ -387,10 +401,11 @@ <ItemGroup> <Resource Include="Images\ti-tm4c129x.png" /> </ItemGroup> + <ItemGroup /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <ProjectExtensions> <VisualStudio> - <UserProperties BuildVersion_StartDate="2000/1/1" BuildVersion_UseGlobalSettings="False" BuildVersion_BuildVersioningStyle="None.None.Increment.TimeStamp" BuildVersion_UpdateAssemblyVersion="True" BuildVersion_AssemblyInfoFilename="Properties\AssemblyInfo.cs" /> + <UserProperties BuildVersion_AssemblyInfoFilename="Properties\AssemblyInfo.cs" BuildVersion_UpdateAssemblyVersion="True" BuildVersion_BuildVersioningStyle="None.None.Increment.TimeStamp" BuildVersion_UseGlobalSettings="False" BuildVersion_StartDate="2000/1/1" /> </VisualStudio> </ProjectExtensions> </Project>
\ No newline at end of file diff --git a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.Common/Tup/TupFileBuilder.cs b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.Common/Tup/TupFileBuilder.cs new file mode 100644 index 000000000..6dd8b82f7 --- /dev/null +++ b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.Common/Tup/TupFileBuilder.cs @@ -0,0 +1,285 @@ +using Ionic.Zip; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Tango.BL; +using Tango.Core; +using Tango.Core.DB; +using Tango.Core.DI; +using Tango.MachineStudio.Common.Web; +using Tango.SQLExaminer; +using Tango.Transport.Web; +using Tango.Core.ExtensionMethods; +using Tango.PPC.Common.Publish; +using Tango.Settings; +using Tango.Core.Components; +using System.Text.RegularExpressions; + +namespace Tango.MachineStudio.Common.Tup +{ + public class TupFileBuilder : ExtendedObject + { + public event EventHandler<TupFileBuilderProgressEventArgs> Progress; + + public Task Build(String serialNumber, String filePath) + { + return Task.Factory.StartNew(() => + { + String tempDbName = "Tango_TUP"; + var tempPackageFolder = TemporaryManager.CreateFolder(); + String tempBackupFolder = "C:\\MachineStudioTUP"; + String tempBackupFile = Path.Combine(tempBackupFolder, tempDbName + ".bak"); + var tempZipFile = TemporaryManager.CreateImaginaryFile(); + DbManager dbManager = null; + + LogManager.Log("Generating tup file..."); + LogManager.Log($"Tup file: '{filePath}.'"); + LogManager.Log($"Temporary db name: '{tempDbName}'."); + LogManager.Log($"Temporary package folder: '{tempPackageFolder}'."); + LogManager.Log($"Temporary db backup folder: '{tempBackupFolder}'."); + LogManager.Log($"Temporary db backup file: '{tempBackupFile}'."); + LogManager.Log($"Temporary zip file: '{tempZipFile}'."); + + try + { + LogManager.Log("Initializing..."); + + OnProgress("Initializing..."); + + Core.DataSource localDataSource = new Core.DataSource() + { + Address = "localhost\\SQLEXPRESS", + IntegratedSecurity = true, + Type = DataSourceType.SQLServer, + Catalog = null, + }; + + try + { + LogManager.Log($"Trying to connect via SQLEXPRESS:\n{localDataSource.ToJsonString()}"); + dbManager = DbManager.FromDataSource(localDataSource); + } + catch (Exception ex) + { + try + { + LogManager.Log(ex, "Could not connect using SQLEXPRESS. Trying local DB..."); + + CmdCommand command = new CmdCommand("sqllocaldb", "info \"MSSQLLocalDB\""); + var result = command.Run().Result; + + String pattern = "np:.+"; + Regex reg = new Regex(pattern); + var match = reg.Match(result.StandardOutput); + String address = match.ToString(); + if (address.Contains("np:")) + { + localDataSource.Address = address; + address = address.Trim().Replace("\r", ""); + } + else + { + throw new ArgumentException("Could not parse LocalDB address string."); + } + + LogManager.Log($"Trying to connect via LocalDB:\n{localDataSource.ToJsonString()}"); + dbManager = DbManager.FromDataSource(localDataSource); + } + catch (Exception x) + { + LogManager.Log(x, "Could not find any database service for this operation."); + throw x; + } + } + + + + OnProgress("Downloading latest PPC version..."); + + LogManager.Log("Connecting to machine service..."); + MachineStudioWebClient client = TangoIOC.Default.GetInstance<MachineStudioWebClient>(); + + LogManager.Log("Requesting latest PPC version from machine service..."); + var response = client.DownloadLatestPPCVersion(new DownloadLatestPPCVersionRequest() { SerialNumber = serialNumber }).Result; + + LogManager.Log($"Machine service response:\n{response.ToJsonString()}"); + + var remoteDataSource = response.DataSource; + + using (AutoFileDownloader downloader = new AutoFileDownloader(response.BlobAddress, response.CdnAddress, tempZipFile)) + { + downloader.Progress += (x, e) => + { + OnProgress($"Downloading latest PPC version '{response.Version}'...", false, e.Current, e.Total); + }; + + downloader.ResolveMode().GetAwaiter().GetResult(); + + LogManager.Log($"Downloading latest PPC version from: '{downloader.Address}'"); + + downloader.Download().Wait(); + } + + LogManager.Log("Extracting PPC version package..."); + + OnProgress("Extracting PPC package..."); + + using (ZipFile zip = new ZipFile(tempZipFile)) + { + int currentEntry = 0; + + zip.ExtractProgress += (x, args) => + { + if (args.EventType == ZipProgressEventType.Extracting_AfterExtractEntry) + { + OnProgress("Extracting PPC package...", false, currentEntry++, zip.Entries.Count); + } + }; + + zip.ExtractAll(tempPackageFolder); + } + + OnProgress("Extracting version information..."); + LogManager.Log("Extracting publish information..."); + PublishInfo publishInfo = PublishInfo.FromJson(File.ReadAllText(Path.Combine(tempPackageFolder, "version.json"))); + LogManager.Log($"Publish Information:\n{publishInfo}"); + + LogManager.Log("Modifying publish information to custom tup file..."); + publishInfo.IsMachineTupPackage = true; + publishInfo.MachineSerialNumber = serialNumber; + publishInfo.MachineDeploymentSlot = SettingsManager.Default.GetOrCreate<MachineStudioSettings>().DeploymentSlot; + + OnProgress("Creating temporary database..."); + + LogManager.Log($"Creating temporary db backup directory '{tempBackupFolder}'"); + + Directory.CreateDirectory(tempBackupFolder); + + LogManager.Log($"Creating new database: '{tempDbName}'"); + + //Create temp db + dbManager.Create(tempDbName, Path.Combine(tempBackupFolder, tempDbName + ".mdf")); + + OnProgress("Generating database snapshot..."); + + LogManager.Log("Starting database synchronization..."); + + Thread.Sleep(2000); + + localDataSource.Catalog = tempDbName; + + ExaminerSequenceConfigurationRunner runner = new ExaminerSequenceConfigurationRunner( + Path.Combine(tempPackageFolder, "Provision Scripts", "config.xml"), + Path.Combine(tempPackageFolder, "Provision Scripts"), + remoteDataSource, + localDataSource, + serialNumber); + + runner.ScriptExecuting += (x, item) => + { + LogManager.Log($"Executing script '{item.FileName}'..."); + OnProgress($"{item.Name}..."); + }; + + runner.Log += (x, log) => + { + LogManager.Log(log); + }; + + runner.Run().GetAwaiter().GetResult(); + + OnProgress("Generating database snapshot..."); + + if (File.Exists(tempBackupFile)) + { + LogManager.Log($"Deleting file '{tempBackupFile}'"); + File.Delete(tempBackupFile); + } + + LogManager.Log($"Generating backup for '{tempDbName}' to '{tempBackupFile}'..."); + + dbManager.Backup(tempDbName, tempBackupFile); + + OnProgress("Injecting database snapshot to PPC package..."); + + using (ZipFile zip = new ZipFile(tempZipFile)) + { + LogManager.Log($"Injecting file '{tempBackupFile}' to original package at '{tempZipFile}'..."); + zip.AddFile(tempBackupFile, "/"); + + LogManager.Log($"Injecting modified publish information..."); + zip.UpdateEntry("version.json", publishInfo.ToJson()); + + zip.Save(); + } + + LogManager.Log($"Copying '{tempZipFile}' to '{filePath}'..."); + + File.Copy(tempZipFile, filePath, true); + + OnProgress("Completed", false, 100, 100); + + LogManager.Log("TUP file generation completed successfully."); + } + catch (Exception ex) + { + LogManager.Log(ex, "TUP file generation failed."); + OnProgress("Failed", false, 0, 100); + throw ex; + } + finally + { + LogManager.Log($"Removing '{tempZipFile}'."); + tempZipFile.Delete(); + LogManager.Log($"Removing '{tempPackageFolder}'."); + tempPackageFolder.Delete(); + + try + { + LogManager.Log($"Removing database '{tempDbName}'."); + dbManager.SetOffline(tempDbName); + dbManager.SetOnline(tempDbName); + dbManager.Delete(tempDbName); + dbManager.Dispose(); + } + catch (Exception ex) + { + LogManager.Log(ex, $"Error removing temp database '{tempDbName}'."); + } + + try + { + LogManager.Log($"Removing '{tempBackupFolder}'."); + Directory.Delete(tempBackupFolder, true); + } + catch (Exception ex) + { + LogManager.Log(ex, $"Error removing folder '{tempBackupFolder}'."); + } + } + }); + } + + public async Task<String> GetLatestPPCVersion(String serialNumber) + { + MachineStudioWebClient client = TangoIOC.Default.GetInstance<MachineStudioWebClient>(); + var response = await client.DownloadLatestPPCVersion(new DownloadLatestPPCVersionRequest() { SerialNumber = serialNumber }); + return response.Version; + } + + private void OnProgress(String message, bool isIntermediate = true, double progress = 0, double total = 100) + { + Progress?.Invoke(this, new TupFileBuilderProgressEventArgs() + { + Message = message, + IsIntermediate = isIntermediate, + Progress = progress, + Total = total, + }); + } + } +} diff --git a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.Common/Tup/TupFileBuilderProgressEventArgs.cs b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.Common/Tup/TupFileBuilderProgressEventArgs.cs new file mode 100644 index 000000000..ada69dd40 --- /dev/null +++ b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.Common/Tup/TupFileBuilderProgressEventArgs.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Tango.MachineStudio.Common.Tup +{ + public class TupFileBuilderProgressEventArgs + { + public double Progress { get; set; } + public double Total { get; set; } + public bool IsIntermediate { get; set; } + public String Message { get; set; } + } +} diff --git a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.Common/Web/DownloadLatestPPCVersionRequest.cs b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.Common/Web/DownloadLatestPPCVersionRequest.cs new file mode 100644 index 000000000..24d465e69 --- /dev/null +++ b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.Common/Web/DownloadLatestPPCVersionRequest.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Tango.Transport.Web; + +namespace Tango.MachineStudio.Common.Web +{ + public class DownloadLatestPPCVersionRequest : WebRequestMessage + { + public String SerialNumber { get; set; } + } +} diff --git a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.Common/Web/DownloadLatestPPCVersionResponse.cs b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.Common/Web/DownloadLatestPPCVersionResponse.cs new file mode 100644 index 000000000..2cc6b731a --- /dev/null +++ b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.Common/Web/DownloadLatestPPCVersionResponse.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Tango.Core; +using Tango.Transport.Web; + +namespace Tango.MachineStudio.Common.Web +{ + public class DownloadLatestPPCVersionResponse : WebResponseMessage + { + public String Version { get; set; } + + public String BlobAddress { get; set; } + + public String CdnAddress { get; set; } + + public DataSource DataSource { get; set; } + } +} diff --git a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.Common/Web/MachineStudioWebClientBase.cs b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.Common/Web/MachineStudioWebClientBase.cs index 131f89515..c2d0dd3e6 100644 --- a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.Common/Web/MachineStudioWebClientBase.cs +++ b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.Common/Web/MachineStudioWebClientBase.cs @@ -94,5 +94,14 @@ namespace Tango.MachineStudio.Common.Web return Post<Tango.MachineStudio.Common.Web.RefreshTokenRequest, Tango.MachineStudio.Common.Web.RefreshTokenResponse>("RefreshToken", request); } + /// <summary> + /// Executes the DownloadLatestPPCVersion action and returns Tango.MachineStudio.Common.Web.DownloadLatestPPCVersionResponse. + /// </summary> + /// <returns></returns> + public Task<Tango.MachineStudio.Common.Web.DownloadLatestPPCVersionResponse> DownloadLatestPPCVersion(Tango.MachineStudio.Common.Web.DownloadLatestPPCVersionRequest request) + { + return Post<Tango.MachineStudio.Common.Web.DownloadLatestPPCVersionRequest, Tango.MachineStudio.Common.Web.DownloadLatestPPCVersionResponse>("DownloadLatestPPCVersion", request); + } + } } diff --git a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.Common/packages.config b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.Common/packages.config index f871776f5..6fd5091a8 100644 --- a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.Common/packages.config +++ b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.Common/packages.config @@ -9,4 +9,5 @@ <package id="MahApps.Metro" version="1.5.0" targetFramework="net46" /> <package id="MaterialDesignColors" version="1.1.2" targetFramework="net46" /> <package id="MaterialDesignThemes" version="2.3.1.953" targetFramework="net46" /> + <package id="Newtonsoft.Json" version="9.0.1" targetFramework="net461" /> </packages>
\ No newline at end of file diff --git a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Tango.MachineStudio.UI.csproj b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Tango.MachineStudio.UI.csproj index 991eb9da6..c6cf624b3 100644 --- a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Tango.MachineStudio.UI.csproj +++ b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Tango.MachineStudio.UI.csproj @@ -448,6 +448,10 @@ <Project>{8491d07b-c1f6-4b62-a412-41b9fd2d6538}</Project> <Name>Tango.SharedUI</Name> </ProjectReference> + <ProjectReference Include="..\..\Tango.SQLExaminer\Tango.SQLExaminer.csproj"> + <Project>{e1e66ed9-597d-45fa-8048-de90a6930484}</Project> + <Name>Tango.SQLExaminer</Name> + </ProjectReference> <ProjectReference Include="..\..\Tango.TFS\Tango.TFS.csproj"> <Project>{998f8471-dc1b-41b6-9d96-354e1b4e7a32}</Project> <Name>Tango.TFS</Name> @@ -661,7 +665,7 @@ if $(ConfigurationName) == Release RD /S /Q "$(TargetDir)ProtoCompilers\"</PostB </Target> <ProjectExtensions> <VisualStudio> - <UserProperties BuildVersion_UseGlobalSettings="False" BuildVersion_DetectChanges="True" BuildVersion_UpdateFileVersion="True" BuildVersion_BuildVersioningStyle="None.None.Increment.DeltaBaseYearDayOfYear" BuildVersion_AssemblyInfoFilename="Properties\AssemblyInfo.cs" BuildVersion_UpdateAssemblyVersion="True" BuildVersion_StartDate="2000/1/1" /> + <UserProperties BuildVersion_StartDate="2000/1/1" BuildVersion_UpdateAssemblyVersion="True" BuildVersion_AssemblyInfoFilename="Properties\AssemblyInfo.cs" BuildVersion_BuildVersioningStyle="None.None.Increment.DeltaBaseYearDayOfYear" BuildVersion_UpdateFileVersion="True" BuildVersion_DetectChanges="True" BuildVersion_UseGlobalSettings="False" /> </VisualStudio> </ProjectExtensions> </Project>
\ No newline at end of file diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/MachineUpdate/IMachineUpdateManager.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/MachineUpdate/IMachineUpdateManager.cs index 3655aa462..e11eab3a5 100644 --- a/Software/Visual_Studio/PPC/Tango.PPC.Common/MachineUpdate/IMachineUpdateManager.cs +++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/MachineUpdate/IMachineUpdateManager.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Text; using System.Threading.Tasks; using Tango.PMR.Synchronization; +using Tango.PPC.Common.Publish; using Tango.PPC.Common.UpdatePackages; using Tango.PPC.Common.Web; @@ -43,14 +44,14 @@ namespace Tango.PPC.Common.MachineUpdate /// <param name="setupFirmware">if set to <c>true</c> updates the embedded device firmware.</param> /// <param name="setupFPGA">if set to <c>true</c> updates the embedded device FPGA version and other parameters.</param> /// <returns></returns> - Task<MachineUpdateResult> Update(String serialNumber, bool setupFirmware, bool setupFPGA); + Task<MachineUpdateResult> Update(bool setupFirmware, bool setupFPGA); /// <summary> /// Performs a machine update using the specified software update package path. /// </summary> /// <param name="fileName">Name of the file.</param> /// <returns></returns> - Task<MachineUpdateResult> UpdateFromTUP(String fileName); + Task<MachineUpdateResult> UpdateFromTUP(String fileName, bool setupFirmware, bool setupFPGA); /// <summary> /// Checks if any update are available for the specified machine serial number. @@ -77,7 +78,7 @@ namespace Tango.PPC.Common.MachineUpdate /// </summary> /// <param name="filePath">The file path.</param> /// <returns></returns> - Task<UpdatePackageFile> GetUpdatePackageFileInfo(String filePath); + Task<PublishInfo> GetUpdatePackageFileInfo(String filePath); /// <summary> /// Checks whether any post update packages needs to be installed. diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/MachineUpdate/MachineUpdateManager.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/MachineUpdate/MachineUpdateManager.cs index 5296a9f34..7e742ceb6 100644 --- a/Software/Visual_Studio/PPC/Tango.PPC.Common/MachineUpdate/MachineUpdateManager.cs +++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/MachineUpdate/MachineUpdateManager.cs @@ -22,6 +22,7 @@ using Tango.PMR.Synchronization; using Tango.PPC.Common.Application; using Tango.PPC.Common.Connection; using Tango.PPC.Common.Navigation; +using Tango.PPC.Common.Publish; using Tango.PPC.Common.UpdatePackages; using Tango.PPC.Common.Web; using Tango.Settings; @@ -117,7 +118,10 @@ namespace Tango.PPC.Common.MachineUpdate private void LogManager_NewLog(object sender, LogItemBase e) { - _logs.Add(e); + if (_isUpdating) + { + _logs.Add(e); + } } #endregion @@ -133,37 +137,80 @@ namespace Tango.PPC.Common.MachineUpdate }); } - private async void OnFailed(Exception ex, TaskCompletionSource<MachineUpdateResult> completionSource, DownloadUpdateResponse response, bool performDatabaseRollback, String dbBackupFile, Tango.Core.DataSource localDataSource) + private async void OnFailed(Exception ex, TaskCompletionSource<MachineUpdateResult> completionSource, DownloadUpdateResponse response, bool performDatabaseRollback, String dbBackupFile, String backupsFolder, String tempDbName, Tango.Core.DataSource localDataSource, String tempUpdatePackageFolder = null) { LogManager.Log(ex, "An error occurred in machine update."); - if (performDatabaseRollback) + await Task.Factory.StartNew(() => { - LogManager.Log("Rolling back database changes..."); - using (DbManager db = DbManager.FromDataSource(localDataSource)) + if (performDatabaseRollback) + { + LogManager.Log("Rolling back database changes..."); + + using (DbManager db = DbManager.FromDataSource(localDataSource)) + { + try + { + UpdateProgress("Rollback", "Rolling back database changes..."); + db.Restore(localDataSource.Catalog, dbBackupFile); + LogManager.Log("Database restored successfully."); + } + catch (Exception e) + { + LogManager.Log(e, "Could not rollback the database."); + } + finally + { + try + { + File.Delete(dbBackupFile); + } + catch { } + } + } + } + + if (tempDbName != null) { try { - UpdateProgress("Rollback", "Rolling back database changes..."); - await Task.Factory.StartNew(() => db.Restore(localDataSource.Catalog, dbBackupFile)); - LogManager.Log("Database restored successfully."); + LogManager.Log($"Removing temporary database '{tempDbName}'..."); + using (DbManager dbManager = DbManager.FromDataSource(localDataSource)) + { + dbManager.SetOffline(tempDbName); + dbManager.SetOnline(tempDbName); + dbManager.Delete(tempDbName); + } } - catch (Exception e) + catch (Exception exx) { - LogManager.Log(e, "Could not rollback the database."); - throw ex; + LogManager.Log(exx, "Error removing temporary database."); } - finally + } + + try + { + Directory.Delete(backupsFolder, true); + } + catch (Exception ee) + { + LogManager.Log(ee, $"Error deleting backups folder '{backupsFolder}'."); + } + + if (tempUpdatePackageFolder != null) + { + try { - try - { - File.Delete(dbBackupFile); - } - catch { } + Directory.Delete(tempUpdatePackageFolder, true); + } + catch (Exception eee) + { + LogManager.Log(eee, "Error removing temporary package folder."); } } - } + + }); completionSource.SetException(ex); @@ -188,13 +235,50 @@ namespace Tango.PPC.Common.MachineUpdate _isUpdating = false; } - private async void OnCompleted(MachineUpdateResult result, TaskCompletionSource<MachineUpdateResult> completionSource, DownloadUpdateResponse response, String dbBackupFile) + private async void OnCompleted(MachineUpdateResult result, TaskCompletionSource<MachineUpdateResult> completionSource, DownloadUpdateResponse response, String tempDbName, String backupsFolder, Core.DataSource localDataSource) { - try + await Task.Factory.StartNew(() => { - File.Delete(dbBackupFile); - } - catch { } + if (tempDbName != null) + { + try + { + LogManager.Log($"Removing temporary database '{tempDbName}'..."); + using (DbManager dbManager = DbManager.FromDataSource(localDataSource)) + { + dbManager.SetOffline(tempDbName); + dbManager.SetOnline(tempDbName); + dbManager.Delete(tempDbName); + } + } + catch (Exception ex) + { + LogManager.Log(ex, "Error removing temporary database."); + } + } + + try + { + Directory.Delete(backupsFolder, true); + } + catch (Exception ex) + { + LogManager.Log(ex, $"Error deleting backups folder '{backupsFolder}'."); + } + + if (!result.RequiresBinariesUpdate) + { + try + { + Directory.Delete(result.UpdatePackagePath, true); + } + catch (Exception ex) + { + LogManager.Log(ex, "Error removing temporary package folder."); + } + } + + }); completionSource.SetResult(result); @@ -315,7 +399,7 @@ namespace Tango.PPC.Common.MachineUpdate /// or /// </exception> /// <exception cref="System.InvalidProgramException">Database tango does not exists.</exception> - public async Task<MachineUpdateResult> Update(String serialNumber, bool setupFirmware, bool setupFPGA) + public async Task<MachineUpdateResult> Update(bool setupFirmware, bool setupFPGA) { _logs.Clear(); @@ -325,6 +409,13 @@ namespace Tango.PPC.Common.MachineUpdate bool performDatabaseRollback = false; String dbBackupFile = null; DownloadUpdateResponse update_response = null; + String backupsFolder = "C:\\Backups"; + + //Create temporary folders for packages. + var _newPackageTempFolder = TemporaryManager.CreateFolder(); + _newPackageTempFolder.Persist = true; + + String serialNumber = _machineProvider.Machine.SerialNumber; try { @@ -371,10 +462,6 @@ namespace Tango.PPC.Common.MachineUpdate LogManager.Log($"Machine update response received: {Environment.NewLine}{update_response.ToJsonString()}"); - //Create temporary folders for packages. - var _newPackageTempFolder = TemporaryManager.CreateFolder(); - _newPackageTempFolder.Persist = true; - LogManager.Log($"Temporary package folder created: {_newPackageTempFolder}."); //Download software package. @@ -413,8 +500,12 @@ namespace Tango.PPC.Common.MachineUpdate UpdateProgress("Downloading software package", "Extracting package..."); LogManager.Log("Extracting downloaded zip file..."); - //Extract software package. - ZipFile.ExtractToDirectory(tempFile, _newPackageTempFolder); + + await Task.Factory.StartNew(() => + { + //Extract software package. + ZipFile.ExtractToDirectory(tempFile, _newPackageTempFolder); + }); LogManager.Log("Copying latest updater utility to application path..."); //Copy new updater utility to app path. @@ -464,8 +555,8 @@ namespace Tango.PPC.Common.MachineUpdate //Create Database Backup try { - Directory.CreateDirectory("C:\\Backups"); - dbBackupFile = $"C:\\Backups\\{Path.GetRandomFileName()}.bak"; + Directory.CreateDirectory(backupsFolder); + dbBackupFile = $"{backupsFolder}\\{Path.GetRandomFileName()}.bak"; LogManager.Log($"Creating database backup to '{dbBackupFile}'..."); await Task.Factory.StartNew(() => db.Backup(localDataSource.Catalog, dbBackupFile)); performDatabaseRollback = true; @@ -548,7 +639,7 @@ namespace Tango.PPC.Common.MachineUpdate handler.Failed += (_, ex) => { stream.Dispose(); - OnFailed(ex, result, update_response, performDatabaseRollback, dbBackupFile, localDataSource); + OnFailed(ex, result, update_response, performDatabaseRollback, dbBackupFile, backupsFolder, null, localDataSource, _newPackageTempFolder); }; handler.Completed += (_, __) => { @@ -557,12 +648,12 @@ namespace Tango.PPC.Common.MachineUpdate OnCompleted(new MachineUpdateResult() { UpdatePackagePath = _newPackageTempFolder, - }, result, update_response, dbBackupFile); + }, result, update_response, null, backupsFolder, localDataSource); }; handler.Canceled += (_, __) => { stream.Dispose(); - OnFailed(new Exception("The operation has been canceled."), result, update_response, performDatabaseRollback, dbBackupFile, localDataSource); + OnFailed(new Exception("The operation has been canceled."), result, update_response, performDatabaseRollback, dbBackupFile, backupsFolder, null, localDataSource, _newPackageTempFolder); }; handler.Progress += (_, e) => { @@ -574,12 +665,12 @@ namespace Tango.PPC.Common.MachineUpdate OnCompleted(new MachineUpdateResult() { UpdatePackagePath = _newPackageTempFolder, - }, result, update_response, dbBackupFile); + }, result, update_response, null, backupsFolder, localDataSource); } } catch (Exception ex) { - OnFailed(ex, result, update_response, performDatabaseRollback, dbBackupFile, localDataSource); + OnFailed(ex, result, update_response, performDatabaseRollback, dbBackupFile, backupsFolder, null, localDataSource, _newPackageTempFolder); } return await result.Task; @@ -875,31 +966,284 @@ namespace Tango.PPC.Common.MachineUpdate /// </summary> /// <param name="fileName">Name of the file.</param> /// <returns></returns> - public Task<MachineUpdateResult> UpdateFromTUP(string fileName) + public async Task<MachineUpdateResult> UpdateFromTUP(string fileName, bool setupFirmware, bool setupFPGA) { - return Task.Factory.StartNew<MachineUpdateResult>(() => + _logs.Clear(); + + TaskCompletionSource<MachineUpdateResult> result = new TaskCompletionSource<MachineUpdateResult>(); + + var localDataSource = SettingsManager.Default.GetOrCreate<CoreSettings>().DataSource; + bool performDatabaseRollback = false; + String dbBackupFile = null; + String tempDbName = "Tango_TUP"; + String tempDbFileName = tempDbName + ".bak"; + String backupsFolder = "C:\\Backups"; + bool replaceBinaries = false; + + String serialNumber = _machineProvider.Machine.SerialNumber; + + //Create temporary folders for packages. + var _newPackageTempFolder = TemporaryManager.CreateFolder(); + _newPackageTempFolder.Persist = true; + + try { - LogManager.Log($"Starting machine update from update package '{fileName}'..."); + _isUpdating = true; + + LogManager.Log($"Starting machine update for serial number {serialNumber}..."); - //Create temporary folders for packages. - var _newPackageTempFolder = TemporaryManager.CreateFolder(); - _newPackageTempFolder.Persist = true; + //Connecting to machine... + LogManager.Log("Verifying machine connection and state..."); - LogManager.Log("Extracting downloaded zip file..."); - //Extract software package. - ZipFile.ExtractToDirectory(fileName, _newPackageTempFolder); + UpdateProgress("Verifying machine state", "Initializing..."); + + await Task.Delay(1000); + + IMachineOperator op = _machineProvider.MachineOperator; + + if (setupFirmware) + { + LogManager.Log("Machine is configured to update firmware..."); + + if (op.State != Transport.TransportComponentState.Connected) + { + throw LogManager.Log(new InvalidOperationException("Could not perform an update while the machine is not connected.")); + } + if (op.Status != MachineStatuses.ReadyToDye) + { + throw LogManager.Log(new InvalidOperationException($"Could not perform an update while the machine is in {op.Status} status.")); + } + } + + UpdateProgress("Exploring package", "Extracting..."); + LogManager.Log("Extracting package..."); + + LogManager.Log($"Temporary package folder created: {_newPackageTempFolder}."); + + await Task.Factory.StartNew(() => + { + //Extract software package. + ZipFile.ExtractToDirectory(fileName, _newPackageTempFolder); + }); + + //Extracting publish info + UpdateProgress("Exploring package", "Verifying..."); + PublishInfo publishInfo = PublishInfo.FromJson(File.ReadAllText(Path.Combine(_newPackageTempFolder, "version.json"))); + + if (!publishInfo.IsMachineTupPackage) + { + throw new InvalidOperationException("The specified tup file is invalid. Updating a machine from a tup file requires a custom generated package."); + } + + if (publishInfo.MachineSerialNumber != serialNumber) + { + throw new InvalidOperationException("The specified tup file is invalid. The package was generated for a different machine."); + } + + if (publishInfo.MachineDeploymentSlot != SettingsManager.Default.GetOrCreate<PPCSettings>().DeploymentSlot) + { + throw new InvalidOperationException("The specified tup file is invalid. The package was generated on a different environment."); + } + + replaceBinaries = _app_manager.Version.ToString() != publishInfo.ApplicationVersion; LogManager.Log("Copying latest updater utility to application path..."); + //Copy new updater utility to app path. File.Copy(Path.Combine(_newPackageTempFolder, "Tango.PPC.Updater.exe"), Path.Combine(PathHelper.GetStartupPath(), "Tango.PPC.Updater.exe"), true); - LogManager.Log("Update operation completed!"); + //Run pre-update packages. + try + { + UpdateProgress("Preparing", "Running update packages..."); + LogManager.Log("Running pre-update packages..."); + var packagesFolder = Path.Combine(_newPackageTempFolder, "Packages"); - return new MachineUpdateResult() + Version updateVersion = new Version(1, 0, 0, 0); + try + { + updateVersion = Version.Parse(publishInfo.ApplicationVersion); + } + catch (Exception ex) + { + LogManager.Log(ex, "Error parsing new version string for package runner."); + } + + await _packageRunner.Run(PackageType.Pre, updateVersion, packagesFolder); + } + catch (Exception ex) + { + LogManager.Log(ex, "Error running pre-update packages..."); + } + + //Synchronize database + UpdateProgress("Updating Database", "Initializing..."); + + UpdateProgress("Updating Database", "Connecting to local database..."); + LogManager.Log("Initializing database manager..."); + DbManager db = DbManager.FromDataSource(localDataSource); + + LogManager.Log("Checking Tango database exists on the local machine..."); + if (!db.Exists(localDataSource.Catalog)) + { + throw new InvalidProgramException("Database tango does not exists."); + } + + UpdateProgress("Updating Database", "Creating database backup..."); + + //Create Database Backup + try + { + Directory.CreateDirectory(backupsFolder); + dbBackupFile = $"{backupsFolder}\\{Path.GetRandomFileName()}.bak"; + LogManager.Log($"Creating database backup to '{dbBackupFile}'..."); + await Task.Factory.StartNew(() => db.Backup(localDataSource.Catalog, dbBackupFile)); + performDatabaseRollback = true; + LogManager.Log("Database backup created successfully."); + } + catch (Exception ex) + { + throw LogManager.Log(ex, "Update manager error while trying to create a database backup."); + } + + LogManager.Log("Extracting database file from package..."); + File.Copy(Path.Combine(_newPackageTempFolder, tempDbFileName), Path.Combine(backupsFolder, tempDbFileName)); + + LogManager.Log("Restoring package database as a new database..."); + db.RestoreAsNew(tempDbName, Path.Combine(backupsFolder, tempDbFileName), backupsFolder); + + Core.DataSource tempDbDataSource = new Core.DataSource(); + tempDbDataSource.Address = localDataSource.Address; + tempDbDataSource.IntegratedSecurity = localDataSource.IntegratedSecurity; + tempDbDataSource.Type = localDataSource.Type; + tempDbDataSource.Catalog = tempDbName; + + LogManager.Log("Disposing database manager."); + db.Dispose(); + + LogManager.Log($"Initializing {nameof(ExaminerSequenceConfigurationRunner)}..."); + + UpdateProgress("Updating Database", "Initializing update sequence..."); + + ExaminerSequenceConfigurationRunner runner = new ExaminerSequenceConfigurationRunner( + Path.Combine(_newPackageTempFolder, "Update Scripts", "config.xml"), + Path.Combine(_newPackageTempFolder, "Update Scripts"), + tempDbDataSource, + localDataSource, + serialNumber); + + runner.Log += (x, msg) => { - UpdatePackagePath = _newPackageTempFolder, + LogManager.Log(msg); + ProgressLog?.Invoke(this, msg); }; - }); + + runner.ScriptExecuting += (x, item) => + { + LogManager.Log($"Executing script {item.ToString()}..."); + UpdateProgress("Updating Database", item.Name + "..."); + }; + + LogManager.Log("Starting synchronization process..."); + + try + { + await runner.Run(); + LogManager.Log("Synchronization completed successfully!"); + UpdateProgress("Updating Database", "Database synchronization completed successfully."); + } + catch (Exception ex) + { + throw LogManager.Log(ex, "Update manager error while trying to synchronize database."); + } + + LogManager.Log("Getting setup firmware/fpga directly from db.."); + + using (var dbManager = DbManager.FromDataSource(localDataSource)) + { + try + { + String firmware = dbManager.GetValue($"SELECT TOP 1 * FROM MACHINES WHERE SERIAL_NUMBER = '{serialNumber}'", "SETUP_FIRMWARE"); + String fpga = dbManager.GetValue($"SELECT TOP 1 * FROM MACHINES WHERE SERIAL_NUMBER = '{serialNumber}'", "SETUP_FPGA"); + + setupFirmware = bool.Parse(firmware); + setupFPGA = bool.Parse(fpga); + } + catch (Exception ex) + { + LogManager.Log(ex, "Error getting new values of SETUP_FIRMWARE and SETUP_FPGA."); + } + } + + //Updating firmware + if (setupFirmware) + { + UpdateProgress("Updating Firmware", "Connecting to firmware device..."); + LogManager.Log(""); + LogManager.Log("-------------------------------------------------------------------------"); + LogManager.Log("Updating Firmware..."); + + UpdateProgress("Updating Firmware", "Loading firmware package..."); + var tfpPath = Path.Combine(_newPackageTempFolder, "firmware_package.tfp"); + var stream = new FileStream(tfpPath, FileMode.Open); + + if (!_machineProvider.Machine.IsDemo) + { + if (setupFPGA) + { + op.FirmwareUpgradeMode = FirmwareUpgradeModes.DFU | FirmwareUpgradeModes.TFP_PACKAGE; + } + else + { + op.FirmwareUpgradeMode = FirmwareUpgradeModes.DFU; + } + } + else + { + op.FirmwareUpgradeMode = FirmwareUpgradeModes.TFP_PACKAGE; + } + + var handler = await op.UpgradeFirmware(stream); + handler.Failed += (_, ex) => + { + stream.Dispose(); + OnFailed(ex, result, null, performDatabaseRollback, dbBackupFile, backupsFolder, tempDbName, localDataSource, _newPackageTempFolder); + }; + handler.Completed += (_, __) => + { + UpdateProgress("Updating Firmware", "Firmware update completed successfully."); + stream.Dispose(); + OnCompleted(new MachineUpdateResult() + { + UpdatePackagePath = _newPackageTempFolder, + RequiresBinariesUpdate = replaceBinaries, + }, result, null, tempDbName, backupsFolder, localDataSource); + }; + handler.Canceled += (_, __) => + { + stream.Dispose(); + OnFailed(new Exception("The operation has been canceled."), result, null, performDatabaseRollback, dbBackupFile, backupsFolder, tempDbName, localDataSource, _newPackageTempFolder); + }; + handler.Progress += (_, e) => + { + UpdateProgress("Updating Firmware", e.Message, false, e.Current, e.Total); + }; + } + else + { + OnCompleted(new MachineUpdateResult() + { + UpdatePackagePath = _newPackageTempFolder, + RequiresBinariesUpdate = replaceBinaries, + }, result, null, tempDbName, backupsFolder, localDataSource); + } + } + catch (Exception ex) + { + OnFailed(ex, result, null, performDatabaseRollback, dbBackupFile, backupsFolder, tempDbName, localDataSource, _newPackageTempFolder); + } + + return await result.Task; } /// <summary> @@ -907,25 +1251,23 @@ namespace Tango.PPC.Common.MachineUpdate /// </summary> /// <param name="filePath">The file path.</param> /// <returns></returns> - public Task<UpdatePackageFile> GetUpdatePackageFileInfo(string filePath) + public Task<PublishInfo> GetUpdatePackageFileInfo(string filePath) { - return Task.Factory.StartNew<UpdatePackageFile>(() => + return Task.Factory.StartNew<PublishInfo>(() => { - UpdatePackageFile file = new UpdatePackageFile(); - var tempFolder = TemporaryManager.CreateFolder(); - using (Ionic.Zip.ZipFile zip = new Ionic.Zip.ZipFile(filePath)) { - var appEntry = zip.Entries.SingleOrDefault(x => x.FileName == "Tango.PPC.UI.exe"); - appEntry.Extract(tempFolder); - } + var appEntry = zip.Entries.SingleOrDefault(x => x.FileName == "version.json"); + var reader = appEntry.OpenReader(); - FileVersionInfo info = FileVersionInfo.GetVersionInfo(Path.Combine(tempFolder, "Tango.PPC.UI.exe")); - file.Version = Version.Parse(info.ProductVersion); - - tempFolder.Delete(); + using (StreamReader stReader = new StreamReader(reader)) + { + String json = stReader.ReadToEnd(); + reader.Dispose(); - return file; + return PublishInfo.FromJson(json); + } + } }); } diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/MachineUpdate/MachineUpdateResult.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/MachineUpdate/MachineUpdateResult.cs index 17ae394ee..85dd5b7d2 100644 --- a/Software/Visual_Studio/PPC/Tango.PPC.Common/MachineUpdate/MachineUpdateResult.cs +++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/MachineUpdate/MachineUpdateResult.cs @@ -12,5 +12,18 @@ namespace Tango.PPC.Common.MachineUpdate /// Gets or sets the temporary update package path from which to get the last downloaded software version. /// </summary> public String UpdatePackagePath { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether the application should replace it's binaries. + /// </summary> + public bool RequiresBinariesUpdate { get; set; } + + /// <summary> + /// Initializes a new instance of the <see cref="MachineUpdateResult"/> class. + /// </summary> + public MachineUpdateResult() + { + RequiresBinariesUpdate = true; + } } } diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/MachineUpdate/UpdatePackageFile.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/MachineUpdate/UpdatePackageFile.cs deleted file mode 100644 index df496c3be..000000000 --- a/Software/Visual_Studio/PPC/Tango.PPC.Common/MachineUpdate/UpdatePackageFile.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Tango.PPC.Common.MachineUpdate -{ - public class UpdatePackageFile - { - public Version Version { get; set; } - } -} diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/Publish/PublishInfo.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/Publish/PublishInfo.cs index 77717254e..df5690a05 100644 --- a/Software/Visual_Studio/PPC/Tango.PPC.Common/Publish/PublishInfo.cs +++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/Publish/PublishInfo.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Text; using System.Threading.Tasks; using Tango.PMR.FirmwareUpgrade; +using Tango.Web; namespace Tango.PPC.Common.Publish { @@ -13,6 +14,9 @@ namespace Tango.PPC.Common.Publish public String ApplicationVersion { get; set; } public VersionPackageDescriptor Firmware { get; set; } public String Comments { get; set; } + public bool IsMachineTupPackage { get; set; } + public String MachineSerialNumber { get; set; } + public DeploymentSlot MachineDeploymentSlot { get; set; } public PublishInfo() { @@ -24,7 +28,7 @@ namespace Tango.PPC.Common.Publish return JsonConvert.SerializeObject(this); } - public PublishInfo FromJson(String json) + public static PublishInfo FromJson(String json) { return JsonConvert.DeserializeObject<PublishInfo>(json); } 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 e3a23903e..d38b9c8e9 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 @@ -194,7 +194,6 @@ <Compile Include="Web\PPCWebClientBase.cs" /> <Compile Include="Web\UpdateDBRequest.cs" /> <Compile Include="Web\UpdateDBResponse.cs" /> - <Compile Include="MachineUpdate\UpdatePackageFile.cs" /> <Compile Include="Messages\JobRemovedMessage.cs" /> <Compile Include="Messages\JobSavedMessage.cs" /> <Compile Include="Messages\MachineSettingsSavedMessage.cs" /> diff --git a/Software/Visual_Studio/PPC/Tango.PPC.UI/Dialogs/UpdateFromFileView.xaml b/Software/Visual_Studio/PPC/Tango.PPC.UI/Dialogs/UpdateFromFileView.xaml index 231f5dabb..a175b655e 100644 --- a/Software/Visual_Studio/PPC/Tango.PPC.UI/Dialogs/UpdateFromFileView.xaml +++ b/Software/Visual_Studio/PPC/Tango.PPC.UI/Dialogs/UpdateFromFileView.xaml @@ -6,16 +6,16 @@ xmlns:local="clr-namespace:Tango.PPC.UI.Dialogs" xmlns:touch="clr-namespace:Tango.Touch.Controls;assembly=Tango.Touch" mc:Ignorable="d" - Background="{StaticResource TangoPrimaryBackgroundBrush}" d:DesignHeight="555" d:DesignWidth="560" Width="550" Height="450" d:DataContext="{d:DesignInstance Type=local:UpdateFromFileViewVM, IsDesignTimeCreatable=False}"> + Background="{StaticResource TangoPrimaryBackgroundBrush}" d:DesignHeight="555" d:DesignWidth="560" Width="570" Height="700" d:DataContext="{d:DesignInstance Type=local:UpdateFromFileViewVM, IsDesignTimeCreatable=False}"> <Grid Margin="20"> <DockPanel> - <StackPanel DockPanel.Dock="Bottom" HorizontalAlignment="Right" Orientation="Horizontal"> - <touch:TouchButton Command="{Binding CloseCommand}" CornerRadius="25" Style="{StaticResource TangoMessageBoxButton}" HorizontalContentAlignment="Center" DockPanel.Dock="Right" Width="120" Height="50" VerticalAlignment="Bottom">CANCEL</touch:TouchButton> - <touch:TouchButton Command="{Binding OKCommand}" CornerRadius="25" Style="{StaticResource TangoMessageBoxButton}" Foreground="{StaticResource TangoPrimaryAccentBrush}" HorizontalContentAlignment="Center" DockPanel.Dock="Right" Width="120" Height="50" VerticalAlignment="Bottom">UPDATE</touch:TouchButton> - </StackPanel> + <Grid DockPanel.Dock="Bottom"> + <touch:TouchButton HorizontalAlignment="Left" CornerRadius="25" Command="{Binding CloseCommand}" Style="{StaticResource TangoHollowButton}" Width="150" Height="50" VerticalAlignment="Bottom">CANCEL</touch:TouchButton> + <touch:TouchButton HorizontalAlignment="Right" CornerRadius="25" Command="{Binding OKCommand}" Style="{StaticResource TangoHollowButton}" Width="150" Height="50" VerticalAlignment="Bottom">UPDATE</touch:TouchButton> + </Grid> <StackPanel> <Image Source="../Images/update.png" Stretch="Uniform" Height="120"></Image> - <TextBlock HorizontalAlignment="Center" Margin="0 20 0 0" FontSize="{StaticResource TangoHeaderFontSize}">UPDATE PACKAGE</TextBlock> + <TextBlock HorizontalAlignment="Center" Margin="0 20 0 0" FontSize="{StaticResource TangoHeaderFontSize}">Tango Update Package</TextBlock> <TextBlock Margin="20 10" HorizontalAlignment="Center" TextWrapping="Wrap" TextAlignment="Center">The selected file contains a software update package. Press 'UPDATE' to start updating your system.</TextBlock> <TextBlock HorizontalAlignment="Center" Margin="0 40 0 0" FontSize="{StaticResource TangoTitleFontSize}" Foreground="{StaticResource TangoGrayTextBrush}"> diff --git a/Software/Visual_Studio/PPC/Tango.PPC.UI/ViewModels/MachineUpdateViewVM.cs b/Software/Visual_Studio/PPC/Tango.PPC.UI/ViewModels/MachineUpdateViewVM.cs index 0371e94da..c0654f643 100644 --- a/Software/Visual_Studio/PPC/Tango.PPC.UI/ViewModels/MachineUpdateViewVM.cs +++ b/Software/Visual_Studio/PPC/Tango.PPC.UI/ViewModels/MachineUpdateViewVM.cs @@ -10,6 +10,7 @@ using Tango.Core.Helpers; using Tango.Explorer; using Tango.PPC.Common; using Tango.PPC.Common.MachineUpdate; +using Tango.PPC.Common.Publish; using Tango.PPC.Common.Web; using Tango.PPC.UI.Dialogs; using Tango.PPC.UI.Notifications.NotificationItems; @@ -209,7 +210,7 @@ namespace Tango.PPC.UI.ViewModels try { - _update_result = await MachineUpdateManager.Update(MachineProvider.Machine.SerialNumber, _checkUpdateResponse.SetupFirmware, _checkUpdateResponse.SetupFPGA); + _update_result = await MachineUpdateManager.Update(_checkUpdateResponse.SetupFirmware, _checkUpdateResponse.SetupFPGA); LogManager.Log("Machine update completed."); await NavigateTo(MachineUpdateView.UpdateCompletedView); } @@ -248,15 +249,15 @@ namespace Tango.PPC.UI.ViewModels { LogManager.Log("Completing machine update..."); - if (!IsDbUpdate) + if (IsDbUpdate || !_update_result.RequiresBinariesUpdate) { - String updater_exe = Path.Combine(_update_result.UpdatePackagePath, "Tango.PPC.Updater.exe"); - ApplicationManager.UpdateApplication(updater_exe, PathHelper.GetStartupPath()); + LogManager.Log("Restarting Application..."); + ApplicationManager.Restart(); } else { - LogManager.Log("Restarting Application..."); - ApplicationManager.Restart(); + String updater_exe = Path.Combine(_update_result.UpdatePackagePath, "Tango.PPC.Updater.exe"); + ApplicationManager.UpdateApplication(updater_exe, PathHelper.GetStartupPath()); } } @@ -375,7 +376,7 @@ namespace Tango.PPC.UI.ViewModels private async void HandleSoftwareUpdatePackageLoaded(ExplorerFileItem fileItem) { - UpdatePackageFile packageFile = null; + PublishInfo packageFile = null; try { @@ -383,38 +384,33 @@ namespace Tango.PPC.UI.ViewModels } catch (Exception ex) { - LogManager.Log(ex, $"Error loading update package file from {fileItem.Path}."); + LogManager.Log(ex, $"Error loading publish info from {fileItem.Path}."); await NotificationProvider.ShowError("An error occurred while trying to load the selected software update package. Please make sure the package is valid."); return; } - if (ApplicationManager.Version <= packageFile.Version) - { - await NotificationProvider.ShowError($"The selected update package (v{packageFile.Version.ToString()}) contains an older software version."); - return; - } - UpdateFromFileViewVM vm = new UpdateFromFileViewVM(); - vm.Version = packageFile.Version.ToString(); + vm.Version = packageFile.ApplicationVersion; await NotificationProvider.ShowDialog(vm); if (vm.DialogResult) { await NavigationManager.NavigateTo(Common.Navigation.NavigationView.MachineUpdateView); - await NavigateTo(MachineUpdateView.UpdateFromPackageView); + await NavigateTo(MachineUpdateView.UpdateProgressView); LogManager.Log("Starting machine update from package..."); try { - _update_result = await MachineUpdateManager.UpdateFromTUP(fileItem.Path); + _update_result = await MachineUpdateManager.UpdateFromTUP(fileItem.Path, MachineProvider.Machine.SetupFirmware, MachineProvider.Machine.SetupFpga); LogManager.Log("Machine update from package completed."); await NavigateTo(MachineUpdateView.UpdateCompletedView); } catch (Exception ex) { LogManager.Log(ex, "Machine update from package failed."); + FailedError = ex.FlattenMessage(); await NavigateTo(MachineUpdateView.UpdateFailedFromPackageView); } } diff --git a/Software/Visual_Studio/PPC/Tango.PPC.UI/Views/MachineUpdateView.xaml b/Software/Visual_Studio/PPC/Tango.PPC.UI/Views/MachineUpdateView.xaml index fba8a599d..beb09be0d 100644 --- a/Software/Visual_Studio/PPC/Tango.PPC.UI/Views/MachineUpdateView.xaml +++ b/Software/Visual_Studio/PPC/Tango.PPC.UI/Views/MachineUpdateView.xaml @@ -194,8 +194,9 @@ <touch:TouchButton Padding="20" Width="300" Margin="0 0 0 130" CornerRadius="35" Command="{Binding CloseCommand}">CLOSE</touch:TouchButton> </StackPanel> <StackPanel HorizontalAlignment="Center" VerticalAlignment="Top" Margin="0 50 0 0"> - <touch:TouchIcon Icon="AlertOctagon" Foreground="{StaticResource TangoErrorBrush}" Width="70" Height="70" /> - <TextBlock VerticalAlignment="Center" Margin="0 10 0 0" Foreground="{StaticResource TangoErrorBrush}" FontSize="{StaticResource TangoTitleFontSize}">An error occurred while trying to update the machine.</TextBlock> + <touch:TouchIcon Icon="AlertOutline" Foreground="{StaticResource TangoErrorBrush}" Width="70" Height="70" /> + <TextBlock HorizontalAlignment="Center" Margin="0 10 0 0" FontSize="{StaticResource TangoTitleFontSize}">Update Failed</TextBlock> + <TextBlock HorizontalAlignment="Center" Margin="0 5 0 0" Foreground="{StaticResource TangoErrorBrush}" TextAlignment="Center" Text="{Binding FailedError,FallbackValue='Unexpected error'}"></TextBlock> </StackPanel> </DockPanel> </Grid> diff --git a/Software/Visual_Studio/Tango.Core/DB/DbManager.cs b/Software/Visual_Studio/Tango.Core/DB/DbManager.cs index 1d415fdb1..8601d67a0 100644 --- a/Software/Visual_Studio/Tango.Core/DB/DbManager.cs +++ b/Software/Visual_Studio/Tango.Core/DB/DbManager.cs @@ -65,9 +65,15 @@ namespace Tango.Core.DB #region Public Methods - public void Create(String name) + public void Create(String name, String filePath = null) { String command = String.Format("CREATE DATABASE {0}", name); + + if (filePath != null) + { + command = $"CREATE DATABASE {name} ON (name='{name}', filename='{filePath}')"; + } + SqlCommand cmd = new SqlCommand(command, _connection); cmd.ExecuteNonQuery(); } @@ -160,6 +166,13 @@ namespace Tango.Core.DB SetOnline(name); } + public void RestoreAsNew(String name, String file, String dbFolder) + { + String command = $"RESTORE DATABASE {name} FROM DISK='{file}' WITH MOVE '{name}' TO '{Path.Combine(dbFolder, name)}.mdf', MOVE '{name}_log' TO '{Path.Combine(dbFolder, name)}.ldf'"; + SqlCommand cmd = new SqlCommand(command, _connection); + cmd.ExecuteNonQuery(); + } + public void ClearDb() { if (!_connection.ConnectionString.ToLower().Contains("initial catalog")) @@ -265,6 +278,24 @@ EXEC sp_executesql @statement return cred; } + public String GetValue(String query, String columnName) + { + string sql = query; + var cm = new SqlCommand(sql, _connection); + var dr = cm.ExecuteReader(); + if (dr.Read()) + { + var value = dr[columnName]; + + if (value != null) + { + return value.ToString(); + } + } + + return null; + } + #endregion #region IDisposable diff --git a/Software/Visual_Studio/Tango.PMR/FirmwareUpgrade/VersionFileDescriptor.cs b/Software/Visual_Studio/Tango.PMR/FirmwareUpgrade/VersionFileDescriptor.cs index 98a6c2efc..96b0a8525 100644 --- a/Software/Visual_Studio/Tango.PMR/FirmwareUpgrade/VersionFileDescriptor.cs +++ b/Software/Visual_Studio/Tango.PMR/FirmwareUpgrade/VersionFileDescriptor.cs @@ -23,16 +23,15 @@ namespace Tango.PMR.FirmwareUpgrade { byte[] descriptorData = global::System.Convert.FromBase64String( string.Concat( "ChtWZXJzaW9uRmlsZURlc2NyaXB0b3IucHJvdG8SGVRhbmdvLlBNUi5GaXJt", - "d2FyZVVwZ3JhZGUaHFZlcnNpb25GaWxlRGVzdGluYXRpb24ucHJvdG8ilAEK", + "d2FyZVVwZ3JhZGUaHFZlcnNpb25GaWxlRGVzdGluYXRpb24ucHJvdG8iggEK", "FVZlcnNpb25GaWxlRGVzY3JpcHRvchIQCghGaWxlTmFtZRgBIAEoCRIPCgdW", "ZXJzaW9uGAIgASgJEkYKC0Rlc3RpbmF0aW9uGAMgASgOMjEuVGFuZ28uUE1S", - "LkZpcm13YXJlVXBncmFkZS5WZXJzaW9uRmlsZURlc3RpbmF0aW9uEhAKCENo", - "ZWNrU3VtGAQgASgMQiUKI2NvbS50d2luZS50YW5nby5wbXIuZmlybXdhcmV1", - "cGdyYWRlYgZwcm90bzM=")); + "LkZpcm13YXJlVXBncmFkZS5WZXJzaW9uRmlsZURlc3RpbmF0aW9uQiUKI2Nv", + "bS50d2luZS50YW5nby5wbXIuZmlybXdhcmV1cGdyYWRlYgZwcm90bzM=")); descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData, new pbr::FileDescriptor[] { global::Tango.PMR.FirmwareUpgrade.VersionFileDestinationReflection.Descriptor, }, new pbr::GeneratedClrTypeInfo(null, new pbr::GeneratedClrTypeInfo[] { - new pbr::GeneratedClrTypeInfo(typeof(global::Tango.PMR.FirmwareUpgrade.VersionFileDescriptor), global::Tango.PMR.FirmwareUpgrade.VersionFileDescriptor.Parser, new[]{ "FileName", "Version", "Destination", "CheckSum" }, null, null, null) + new pbr::GeneratedClrTypeInfo(typeof(global::Tango.PMR.FirmwareUpgrade.VersionFileDescriptor), global::Tango.PMR.FirmwareUpgrade.VersionFileDescriptor.Parser, new[]{ "FileName", "Version", "Destination" }, null, null, null) })); } #endregion @@ -66,7 +65,6 @@ namespace Tango.PMR.FirmwareUpgrade { fileName_ = other.fileName_; version_ = other.version_; destination_ = other.destination_; - checkSum_ = other.checkSum_; } [global::System.Diagnostics.DebuggerNonUserCodeAttribute] @@ -107,17 +105,6 @@ namespace Tango.PMR.FirmwareUpgrade { } } - /// <summary>Field number for the "CheckSum" field.</summary> - public const int CheckSumFieldNumber = 4; - private pb::ByteString checkSum_ = pb::ByteString.Empty; - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - public pb::ByteString CheckSum { - get { return checkSum_; } - set { - checkSum_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); - } - } - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] public override bool Equals(object other) { return Equals(other as VersionFileDescriptor); @@ -134,7 +121,6 @@ namespace Tango.PMR.FirmwareUpgrade { if (FileName != other.FileName) return false; if (Version != other.Version) return false; if (Destination != other.Destination) return false; - if (CheckSum != other.CheckSum) return false; return true; } @@ -144,7 +130,6 @@ namespace Tango.PMR.FirmwareUpgrade { if (FileName.Length != 0) hash ^= FileName.GetHashCode(); if (Version.Length != 0) hash ^= Version.GetHashCode(); if (Destination != 0) hash ^= Destination.GetHashCode(); - if (CheckSum.Length != 0) hash ^= CheckSum.GetHashCode(); return hash; } @@ -167,10 +152,6 @@ namespace Tango.PMR.FirmwareUpgrade { output.WriteRawTag(24); output.WriteEnum((int) Destination); } - if (CheckSum.Length != 0) { - output.WriteRawTag(34); - output.WriteBytes(CheckSum); - } } [global::System.Diagnostics.DebuggerNonUserCodeAttribute] @@ -185,9 +166,6 @@ namespace Tango.PMR.FirmwareUpgrade { if (Destination != 0) { size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) Destination); } - if (CheckSum.Length != 0) { - size += 1 + pb::CodedOutputStream.ComputeBytesSize(CheckSum); - } return size; } @@ -205,9 +183,6 @@ namespace Tango.PMR.FirmwareUpgrade { if (other.Destination != 0) { Destination = other.Destination; } - if (other.CheckSum.Length != 0) { - CheckSum = other.CheckSum; - } } [global::System.Diagnostics.DebuggerNonUserCodeAttribute] @@ -230,10 +205,6 @@ namespace Tango.PMR.FirmwareUpgrade { destination_ = (global::Tango.PMR.FirmwareUpgrade.VersionFileDestination) input.ReadEnum(); break; } - case 34: { - CheckSum = input.ReadBytes(); - break; - } } } } diff --git a/Software/Visual_Studio/Tango.SQLExaminer/ExaminerSequenceConfigurationRunner.cs b/Software/Visual_Studio/Tango.SQLExaminer/ExaminerSequenceConfigurationRunner.cs index de146ead3..b9ee0cf7a 100644 --- a/Software/Visual_Studio/Tango.SQLExaminer/ExaminerSequenceConfigurationRunner.cs +++ b/Software/Visual_Studio/Tango.SQLExaminer/ExaminerSequenceConfigurationRunner.cs @@ -24,6 +24,15 @@ namespace Tango.SQLExaminer public ExaminerSequenceConfiguration Configuration { get; private set; } + public ExaminerSequenceConfigurationRunner(ExaminerSequenceConfiguration sequenceConfiguration, String scriptsFolder, Core.DataSource source, Core.DataSource target, String machineSerialNumber) + { + ScriptsFolder = scriptsFolder; + Source = source; + Target = target; + MachineSerialNumber = machineSerialNumber; + Configuration = sequenceConfiguration; + } + public ExaminerSequenceConfigurationRunner(String sequenceFile, String scriptsFolder, Core.DataSource source, Core.DataSource target, String machineSerialNumber) { SequenceFile = sequenceFile; diff --git a/Software/Visual_Studio/Tango.SQLExaminer/ExtensionMethods.cs b/Software/Visual_Studio/Tango.SQLExaminer/ExtensionMethods.cs new file mode 100644 index 000000000..09a830ab3 --- /dev/null +++ b/Software/Visual_Studio/Tango.SQLExaminer/ExtensionMethods.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Tango.SQLExaminer; + +public static class ExtensionMethods +{ + public static String GetFilePath(this ExaminerConfigurationType type) + { + return Path.Combine(Helper.SQL_EXAMINER_CONFIG_FOLDER, type.ToString() + ".xml"); + } + + public static String GetFileName(this ExaminerConfigurationType type) + { + return type.ToString() + ".xml"; + } +} + diff --git a/Software/Visual_Studio/Tango.SQLExaminer/Helper.cs b/Software/Visual_Studio/Tango.SQLExaminer/Helper.cs index 5eb09b98c..3b4e238a7 100644 --- a/Software/Visual_Studio/Tango.SQLExaminer/Helper.cs +++ b/Software/Visual_Studio/Tango.SQLExaminer/Helper.cs @@ -8,7 +8,7 @@ using Tango.Core.Helpers; namespace Tango.SQLExaminer { - internal static class Helper + public static class Helper { public static String SQL_EXAMINER_FOLDER = Path.Combine(AssemblyHelper.GetCurrentAssemblyFolder(), "SQLExaminer"); public static String SQL_EXAMINER_CONFIG_FOLDER = Path.Combine(SQL_EXAMINER_FOLDER, "Configurations"); diff --git a/Software/Visual_Studio/Tango.SQLExaminer/Tango.SQLExaminer.csproj b/Software/Visual_Studio/Tango.SQLExaminer/Tango.SQLExaminer.csproj index b71a30dc0..17dcb1b22 100644 --- a/Software/Visual_Studio/Tango.SQLExaminer/Tango.SQLExaminer.csproj +++ b/Software/Visual_Studio/Tango.SQLExaminer/Tango.SQLExaminer.csproj @@ -69,6 +69,7 @@ <Compile Include="ExaminerSequenceItemDirection.cs" /> <Compile Include="ExaminerSequenceItemType.cs" /> <Compile Include="ExaminerSerializedObject.cs" /> + <Compile Include="ExtensionMethods.cs" /> <Compile Include="Filter.cs" /> <Compile Include="Helper.cs" /> <Compile Include="IncludeItem.cs" /> diff --git a/Software/Visual_Studio/Tango.UnitTesting/SQLExaminer/SQLExaminer_TST.cs b/Software/Visual_Studio/Tango.UnitTesting/SQLExaminer/SQLExaminer_TST.cs index 9b0385c23..e3e49ac5e 100644 --- a/Software/Visual_Studio/Tango.UnitTesting/SQLExaminer/SQLExaminer_TST.cs +++ b/Software/Visual_Studio/Tango.UnitTesting/SQLExaminer/SQLExaminer_TST.cs @@ -391,5 +391,76 @@ namespace Tango.UnitTesting.SQLExaminer //Should have no differences! Assert.IsFalse(data_report.HasDifferences); } + + [TestMethod] + public void Perform_Machine_Update_From_Backup() + { + String tempDbName = "Tango_TUP"; + + var source_db = GetSource("Tango"); + var target_db = GetSource(tempDbName); + + DbManager dbManager = DbManager.FromDataSource(source_db); + + //Create the backup for the tup file. + dbManager.Create(tempDbName); + + var configuration = GetFullConfiguration(); + + ExaminerSequenceConfigurationRunner runner = + new ExaminerSequenceConfigurationRunner( + configuration, + Tango.SQLExaminer.Helper.SQL_EXAMINER_CONFIG_FOLDER, + source_db, + target_db, + "1111"); + + runner.Run().GetAwaiter().GetResult(); + + String backupFile = "C:\\DB_Backups\\Tango_TEMP.bak"; + + dbManager.Backup(tempDbName, backupFile); + dbManager.SetOffline(tempDbName); + dbManager.SetOnline(tempDbName); + dbManager.Delete(tempDbName); + + //Now restore as different name + dbManager.RestoreAsNew(tempDbName, backupFile, "C:\\DB_Backups"); + } + + private ExaminerSequenceConfiguration GetFullConfiguration() + { + ExaminerSequenceConfiguration configuration = new ExaminerSequenceConfiguration(); + + configuration.Items.Add(new ExaminerSequenceItem() + { + Type = ExaminerSequenceItemType.Schema, + Direction = ExaminerSequenceItemDirection.SourceToTarget, + FileName = ExaminerConfigurationType.Schema.GetFileName(), + Index = 0, + Name = "Updating Schema", + }); + + configuration.Items.Add(new ExaminerSequenceItem() + { + Type = ExaminerSequenceItemType.Data, + Direction = ExaminerSequenceItemDirection.SourceToTarget, + FileName = ExaminerConfigurationType.OverrideData.GetFileName(), + Index = 1, + Name = "Updating Collections", + }); + + configuration.Items.Add(new ExaminerSequenceItem() + { + Type = ExaminerSequenceItemType.Data, + Direction = ExaminerSequenceItemDirection.SourceToTarget, + FileName = ExaminerConfigurationType.ProvisionMachine.GetFileName(), + Index = 2, + Name = "Configuring Machine", + RequiresSerialNumber = true, + }); + + return configuration; + } } } diff --git a/Software/Visual_Studio/Web/Tango.MachineService/Controllers/MachineStudioController.cs b/Software/Visual_Studio/Web/Tango.MachineService/Controllers/MachineStudioController.cs index 2eeaa6e0e..378fc3eea 100644 --- a/Software/Visual_Studio/Web/Tango.MachineService/Controllers/MachineStudioController.cs +++ b/Software/Visual_Studio/Web/Tango.MachineService/Controllers/MachineStudioController.cs @@ -23,6 +23,10 @@ using Tango.Web.ActiveDirectory; using Tango.MachineService.Filters; using Tango.MachineService.Security; using Tango.Web.SQLServer; +using Tango.Core; +using Tango.Web.SMO; +using Tango.Core.DB; +using System.Threading.Tasks; namespace Tango.MachineService.Controllers { @@ -425,6 +429,67 @@ namespace Tango.MachineService.Controllers }; } + [HttpPost] + [JwtTokenFilter] + public DownloadLatestPPCVersionResponse DownloadLatestPPCVersion(DownloadLatestPPCVersionRequest request) + { + DownloadLatestPPCVersionResponse response = new DownloadLatestPPCVersionResponse(); + + using (ObservablesContext db = ObservablesContextHelper.CreateContext()) + { + var machine = db.Machines.SingleOrDefault(x => x.SerialNumber == request.SerialNumber); + + if (machine == null) + { + throw new AuthenticationException("The specified serial number could not be found."); + } + + var machine_version = db.MachineVersions.SingleOrDefault(x => x.Guid == machine.MachineVersionGuid); + + var latest_machine_version = db.TangoVersions.Where(x => x.MachineVersionGuid == machine_version.Guid).ToList().OrderByDescending(x => Version.Parse(x.Version)).FirstOrDefault(); + + response.Version = latest_machine_version.Version; + + var manager = new BlobStorageManager(); + var container = manager.GetContainer(MachineServiceConfig.TANGO_VERSIONS_CONTAINER); + var blob = container.GetBlockBlobReference(latest_machine_version.BlobName); + + response.BlobAddress = blob.GenerateReadSignature(TimeSpan.FromMinutes(60)); + + if (!String.IsNullOrWhiteSpace(MachineServiceConfig.CDN_ENDPOINT)) + { + response.CdnAddress = MachineServiceConfig.CDN_ENDPOINT + blob.Uri.AbsolutePath; + } + + DbCredentials credentials = new DbCredentials(); + + using (SmoManager smo = new SmoManager()) + { + credentials = smo.CreateRandomLoginAndUser(); + + Task.Delay(TimeSpan.FromMinutes(PPCController.SQL_TEMP_CREDENTIALS_EXP_MINUTS)).ContinueWith((x) => + { + using (SmoManager m = new SmoManager()) + { + m.DeleteLoginAndUser(credentials.UserName); + } + }); + } + + response.DataSource = new DataSource() + { + Address = MachineServiceConfig.DB_ADDRESS, + Catalog = MachineServiceConfig.DB_CATALOG, + UserName = credentials.UserName, + Password = credentials.Password, + IntegratedSecurity = false, + Type = DataSourceType.SQLServer, + }; + } + + return response; + } + #endregion } } diff --git a/Software/Visual_Studio/Web/Tango.MachineService/Controllers/PPCController.cs b/Software/Visual_Studio/Web/Tango.MachineService/Controllers/PPCController.cs index 2dee09e69..0d9a2993b 100644 --- a/Software/Visual_Studio/Web/Tango.MachineService/Controllers/PPCController.cs +++ b/Software/Visual_Studio/Web/Tango.MachineService/Controllers/PPCController.cs @@ -32,7 +32,7 @@ namespace Tango.MachineService.Controllers private static List<PPCPendingUpload> _pendingUploads; private static List<PPCPendingUpdate> _pendingUpdates; private ActiveDirectoryManager _ad_manager; - private const int SQL_TEMP_CREDENTIALS_EXP_MINUTS = 20; + public const int SQL_TEMP_CREDENTIALS_EXP_MINUTS = 20; public class TokenObject { |
