diff options
| author | Roy Ben Shabat <Roy@twine-s.com> | 2020-12-30 15:11:34 +0000 |
|---|---|---|
| committer | Roy Ben Shabat <Roy@twine-s.com> | 2020-12-30 15:11:34 +0000 |
| commit | d33c19b3ac6803de4b5c8d475832efef131c1a45 (patch) | |
| tree | ea725abc39def99a755b041c13cba1fe0d594ddc /Software/Visual_Studio/PPC/Tango.PPC.Common/MachineUpdate/MachineUpdateManager.cs | |
| parent | 1bdcaa9f51303bbff682507f31fb3b4414692ca4 (diff) | |
| download | Tango-d33c19b3ac6803de4b5c8d475832efef131c1a45.tar.gz Tango-d33c19b3ac6803de4b5c8d475832efef131c1a45.zip | |
Revert "Hope it is fine"
Diffstat (limited to 'Software/Visual_Studio/PPC/Tango.PPC.Common/MachineUpdate/MachineUpdateManager.cs')
| -rw-r--r-- | Software/Visual_Studio/PPC/Tango.PPC.Common/MachineUpdate/MachineUpdateManager.cs | 1451 |
1 files changed, 1265 insertions, 186 deletions
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 b7573ec60..c115f4f5b 100644 --- a/Software/Visual_Studio/PPC/Tango.PPC.Common/MachineUpdate/MachineUpdateManager.cs +++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/MachineUpdate/MachineUpdateManager.cs @@ -9,6 +9,8 @@ using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; +using Tango.BL; +using Tango.BL.Entities; using Tango.Core; using Tango.Core.DB; using Tango.Core.ExtensionMethods; @@ -16,26 +18,48 @@ using Tango.Core.Helpers; using Tango.Core.IO; using Tango.Integration.Operation; using Tango.Integration.Upgrade; +using Tango.Logging; 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; using Tango.SharedUI.Helpers; using Tango.SQLExaminer; using Tango.Transport.Web; +using System.Data.Entity; +using Tango.PPC.Common.ExternalBridge; +using Tango.Integration.ExternalBridge; +using Tango.BL.DTO; +using Tango.PPC.Shared.Updates; +using Tango.PPC.Shared.RemoteUpgrade; +using Tango.Core.Threading; namespace Tango.PPC.Common.MachineUpdate { - public class MachineUpdateManager : ExtendedObject, IMachineUpdateManager + public class MachineUpdateManager : ExtendedObject, IMachineUpdateManager, IExternalBridgeRequestHandler { private IPPCApplicationManager _app_manager; private IMachineProvider _machineProvider; + private IPackageRunner _packageRunner; private PPCWebClient _client; + private List<LogItemBase> _logs; + private System.Timers.Timer _checkForUpdateTimer; + private bool _isUpdating; + private PPCSettings _settings; + private DateTime _updateStartDate; #region Events /// <summary> + /// Occurs when an application update is available. + /// </summary> + public event EventHandler<CheckForUpdateResponse> UpdateAvailable; + + /// <summary> /// Occurs when there is a text log message available. /// </summary> public event EventHandler<string> ProgressLog; @@ -50,12 +74,25 @@ namespace Tango.PPC.Common.MachineUpdate #region Properties private MachineUpdateProgress _status; + /// <summary> + /// Gets the current machine update progress status. + /// </summary> public MachineUpdateProgress Status { get { return _status; } private set { _status = value; RaisePropertyChangedAuto(); } } + private bool _autoCheckForUpdates; + /// <summary> + /// Gets or sets a value indicating whether to automatically check for new application updates. + /// </summary> + public bool EnableAutoCheckForUpdates + { + get { return _autoCheckForUpdates; } + set { _autoCheckForUpdates = value; RaisePropertyChangedAuto(); } + } + #endregion #region Constructors @@ -64,23 +101,531 @@ namespace Tango.PPC.Common.MachineUpdate /// Initializes a new instance of the <see cref="MachineUpdateManager"/> class. /// </summary> /// <param name="applicationManager">The application manager.</param> - public MachineUpdateManager(PPCWebClient ppcWebClient, IPPCApplicationManager applicationManager, IMachineProvider machineProvider) + public MachineUpdateManager(PPCWebClient ppcWebClient, IPPCApplicationManager applicationManager, IMachineProvider machineProvider, IPackageRunner packageRunner, IPPCExternalBridgeService externalBridge) { _client = ppcWebClient; _machineProvider = machineProvider; _app_manager = applicationManager; + _app_manager.ApplicationReady += _app_manager_ApplicationReady; + _packageRunner = packageRunner; + _packageRunner.PackageProgress += _packageRunner_PackageProgress; + + _logs = new List<LogItemBase>(); + LogManager.NewLog += LogManager_NewLog; + + _settings = SettingsManager.Default.GetOrCreate<PPCSettings>(); + + _checkForUpdateTimer = new System.Timers.Timer(_settings.AutoUpdateCheckInterval.TotalMilliseconds); + _checkForUpdateTimer.Elapsed += _checkForUpdateTimer_Elapsed; + _checkForUpdateTimer.Stop(); + + externalBridge.RegisterRequestHandler(this); + } + + #endregion + + #region Event Handlers + + private void _app_manager_ApplicationReady(object sender, EventArgs e) + { + _checkForUpdateTimer.Start(); + + if (!_app_manager.IsUpdateFailed) + { + ClearLastDatabaseBackup(); + } + } + + private void _packageRunner_PackageProgress(object sender, PackageProgressEventArgs e) + { + UpdateProgress(e.PackageName, e.Message, e.IsIntermediate, e.Progress, e.Total); + } + + private void LogManager_NewLog(object sender, LogItemBase e) + { + if (_isUpdating) + { + _logs.Add(e); + } } #endregion #region Private Methods - private Task Login(String serialNumber) + private Task Login(String machineGuid) { return _client.Login(new LoginRequest() { Mode = LoginMode.Machine, - SerialNumber = serialNumber, + MachineGuid = machineGuid, + }); + } + + 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, PublishInfo tupPublishInfo = null) + { + LogManager.Log(ex, "An error occurred in machine update."); + + await Task.Factory.StartNew(() => + { + + 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 + { + LogManager.Log($"Removing temporary database '{tempDbName}'..."); + using (DbManager dbManager = DbManager.FromDataSource(localDataSource)) + { + dbManager.SetOffline(tempDbName); + dbManager.SetOnline(tempDbName); + dbManager.Delete(tempDbName); + } + } + catch (Exception exx) + { + LogManager.Log(exx, "Error removing temporary database."); + } + } + + try + { + Directory.Delete(backupsFolder, true); + } + catch (Exception ee) + { + LogManager.Log(ee, $"Error deleting backups folder '{backupsFolder}'."); + } + + if (tempUpdatePackageFolder != null) + { + try + { + Directory.Delete(tempUpdatePackageFolder, true); + } + catch (Exception eee) + { + LogManager.Log(eee, "Error removing temporary package folder."); + } + } + + }); + + completionSource.SetException(ex); + + String logs = GetLogsStringAndClear(); + + if (response != null) + { + try + { + var result = await _client.NotifyUpdateCompleted(new MachineUpdateCompletedRequest() + { + Token = response.NotifyCompletedToken, + Status = BL.Enumerations.TangoUpdateStatuses.UpdateFailed, + FailedReason = ex.FlattenMessage(), + FailedLog = logs, + }); + } + catch (Exception xx) + { + LogManager.Log(xx, "Error notifying update failed."); + } + + try + { + using (ObservablesContext db = ObservablesContext.CreateDefault()) + { + TangoUpdate update = new TangoUpdate(); + update.ApplicationVersion = response.Version; + update.FirmwareVersion = response.FirmwareVersion; + update.MachineGuid = _machineProvider.Machine.Guid; + update.UpdateStatus = BL.Enumerations.TangoUpdateStatuses.UpdateFailed; + update.StartDate = _updateStartDate; + update.EndDate = DateTime.UtcNow; + update.FailedReason = ex.FlattenMessage(); + update.FailedLog = logs; + db.TangoUpdates.Add(update); + await db.SaveChangesAsync(); + } + } + catch (Exception xxx) + { + LogManager.Log(xxx, "Error saving tango update information to database."); + } + } + + + if (tupPublishInfo != null) + { + try + { + using (ObservablesContext db = ObservablesContext.CreateDefault()) + { + TangoUpdate update = new TangoUpdate(); + update.ApplicationVersion = tupPublishInfo.ApplicationVersion; + update.FirmwareVersion = tupPublishInfo.GetFirmwareVersion(); + update.MachineGuid = _machineProvider.Machine.Guid; + update.UpdateStatus = BL.Enumerations.TangoUpdateStatuses.OfflineUpdateFailed; + update.StartDate = _updateStartDate; + update.EndDate = DateTime.UtcNow; + update.FailedReason = ex.FlattenMessage(); + update.FailedLog = logs; + db.TangoUpdates.Add(update); + await db.SaveChangesAsync(); + } + } + catch (Exception xxx) + { + LogManager.Log(xxx, "Error saving tango offline update information to database."); + } + } + + _isUpdating = false; + } + + private async void OnCompleted(MachineUpdateResult result, TaskCompletionSource<MachineUpdateResult> completionSource, DownloadUpdateResponse response, String tempDbName, String backupsFolder, Core.DataSource localDataSource, PublishInfo tupPublishInfo = null) + { + await Task.Factory.StartNew(() => + { + 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); + + if (response != null) + { + try + { + var r = await _client.NotifyUpdateCompleted(new MachineUpdateCompletedRequest() + { + Token = response.NotifyCompletedToken, + Status = BL.Enumerations.TangoUpdateStatuses.UpdateCompleted, + }); + } + catch (Exception ex) + { + LogManager.Log(ex, "Error notifying update completed."); + } + + try + { + using (ObservablesContext db = ObservablesContext.CreateDefault()) + { + TangoUpdate update = new TangoUpdate(); + update.ApplicationVersion = response.Version; + update.FirmwareVersion = response.FirmwareVersion; + update.MachineGuid = (await db.Machines.FirstAsync()).Guid; + update.UpdateStatus = BL.Enumerations.TangoUpdateStatuses.UpdateCompleted; + update.StartDate = _updateStartDate; + update.EndDate = DateTime.UtcNow; + db.TangoUpdates.Add(update); + await db.SaveChangesAsync(); + } + } + catch (Exception ex) + { + LogManager.Log(ex, "Error saving tango update information to database."); + } + } + + + if (tupPublishInfo != null) + { + try + { + using (ObservablesContext db = ObservablesContext.CreateDefault()) + { + TangoUpdate update = new TangoUpdate(); + update.ApplicationVersion = tupPublishInfo.ApplicationVersion; + update.FirmwareVersion = tupPublishInfo.GetFirmwareVersion(); + update.MachineGuid = _machineProvider.Machine.Guid; + update.UpdateStatus = BL.Enumerations.TangoUpdateStatuses.OfflineUpdateCompleted; + update.StartDate = _updateStartDate; + update.EndDate = DateTime.UtcNow; + db.TangoUpdates.Add(update); + await db.SaveChangesAsync(); + } + } + catch (Exception xxx) + { + LogManager.Log(xxx, "Error saving tango offline update information to database."); + } + } + + _isUpdating = false; + } + + private void OnFailed(Exception ex, UpdateDBResponse response, bool performDatabaseRollback, String dbBackupFile, Tango.Core.DataSource localDataSource) + { + LogManager.Log(ex, "An error occurred in database update."); + + 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."); + throw ex; + } + finally + { + try + { + File.Delete(dbBackupFile); + } + catch { } + } + } + } + + String logs = GetLogsStringAndClear(); + + if (response != null) + { + try + { + var r = _client.NotifyUpdateCompleted(new MachineUpdateCompletedRequest() + { + Token = response.NotifyCompletedToken, + Status = BL.Enumerations.TangoUpdateStatuses.DatabaseFailed, + FailedReason = ex.FlattenMessage(), + FailedLog = logs, + }).Result; + } + catch (Exception xx) + { + LogManager.Log(xx, "Error notifying database failed."); + } + + try + { + using (ObservablesContext db = ObservablesContext.CreateDefault()) + { + TangoUpdate update = new TangoUpdate(); + update.ApplicationVersion = _app_manager.Version.ToString(); + update.FirmwareVersion = _app_manager.FirmwareVersion.ToString(); + update.MachineGuid = _machineProvider.Machine.Guid; + update.UpdateStatus = BL.Enumerations.TangoUpdateStatuses.DatabaseFailed; + update.StartDate = _updateStartDate; + update.EndDate = DateTime.UtcNow; + update.FailedReason = ex.FlattenMessage(); + update.FailedLog = logs; + db.TangoUpdates.Add(update); + db.SaveChanges(); + } + } + catch (Exception exx) + { + LogManager.Log(exx, "Error saving database update information to database."); + } + } + + _isUpdating = false; + } + + private void OnCompleted(UpdateDBResponse response, bool completedWithNoDifferences = false) + { + if (response != null) + { + try + { + var r = _client.NotifyUpdateCompleted(new MachineUpdateCompletedRequest() + { + Token = response.NotifyCompletedToken, + Status = BL.Enumerations.TangoUpdateStatuses.DatabaseCompleted, + ReportsAboutDbCheckNoDifferences = completedWithNoDifferences, + }).Result; + } + catch (Exception ex) + { + LogManager.Log(ex, "Error notifying database completed."); + } + + if (!completedWithNoDifferences) + { + try + { + using (ObservablesContext db = ObservablesContext.CreateDefault()) + { + TangoUpdate update = new TangoUpdate(); + update.ApplicationVersion = _app_manager.Version.ToString(); + update.FirmwareVersion = _app_manager.FirmwareVersion.ToString(); + update.MachineGuid = _machineProvider.Machine.Guid; + update.UpdateStatus = BL.Enumerations.TangoUpdateStatuses.DatabaseCompleted; + update.StartDate = _updateStartDate; + update.EndDate = DateTime.UtcNow; + db.TangoUpdates.Add(update); + db.SaveChanges(); + } + } + catch (Exception ex) + { + LogManager.Log(ex, "Error saving database update information to database."); + } + } + } + + _isUpdating = false; + } + + private void OnFailed(Exception ex, TaskCompletionSource<object> completionSource, String firmwareVersion) + { + LogManager.Log(ex, "An error occurred in firmware upgrade."); + + completionSource.SetException(ex); + String logs = GetLogsStringAndClear(); + + try + { + using (ObservablesContext db = ObservablesContext.CreateDefault()) + { + TangoUpdate update = new TangoUpdate(); + update.ApplicationVersion = _app_manager.Version.ToString(); + update.FirmwareVersion = firmwareVersion; + update.MachineGuid = _machineProvider.Machine.Guid; + update.UpdateStatus = BL.Enumerations.TangoUpdateStatuses.OfflineFirmwareUpgradeFailed; + update.StartDate = _updateStartDate; + update.EndDate = DateTime.UtcNow; + update.FailedReason = ex.FlattenMessage(); + update.FailedLog = logs; + db.TangoUpdates.Add(update); + db.SaveChanges(); + } + } + catch (Exception exx) + { + LogManager.Log(exx, "Error saving firmware upgrade information to database."); + } + + _isUpdating = false; + } + + private void OnCompleted(TaskCompletionSource<object> completionSource, String firmwareVersion) + { + LogManager.Log("Firmware upgrade completed successfully."); + completionSource.SetResult(true); + + try + { + using (ObservablesContext db = ObservablesContext.CreateDefault()) + { + TangoUpdate update = new TangoUpdate(); + update.ApplicationVersion = _app_manager.Version.ToString(); + update.FirmwareVersion = firmwareVersion; + update.MachineGuid = _machineProvider.Machine.Guid; + update.UpdateStatus = BL.Enumerations.TangoUpdateStatuses.OfflineFirmwareUpgradeCompleted; + update.StartDate = _updateStartDate; + update.EndDate = DateTime.UtcNow; + db.TangoUpdates.Add(update); + db.SaveChanges(); + } + } + catch (Exception exx) + { + LogManager.Log(exx, "Error saving firmware upgrade information to database."); + } + + _isUpdating = false; + } + + private String GetLogsStringAndClear() + { + String logsString = String.Join(Environment.NewLine, _logs.ToList().Select(x => x.ToString())); + _logs.Clear(); + return logsString; + } + + private void ClearLastDatabaseBackup() + { + Task.Factory.StartNew(() => + { + try + { + var lastBackupFile = SettingsManager.Default.GetOrCreate<PPCSettings>().LastDatabaseBackupFile; + + if (File.Exists(lastBackupFile)) + { + File.Delete(lastBackupFile); + } + } + catch (Exception ex) + { + LogManager.Log(ex, "Error removing last database backup file."); + } }); } @@ -89,9 +634,8 @@ namespace Tango.PPC.Common.MachineUpdate #region Public Methods /// <summary> - /// Performs a machine update using the specified serial number and machine service address. + /// Performs a machine update. /// </summary> - /// <param name="serialNumber">The serial number.</param> /// <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> @@ -101,19 +645,32 @@ 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) { + _updateStartDate = DateTime.UtcNow; + _logs.Clear(); + TaskCompletionSource<MachineUpdateResult> result = new TaskCompletionSource<MachineUpdateResult>(); var localDataSource = SettingsManager.Default.GetOrCreate<CoreSettings>().DataSource; 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 machineGuid = _machineProvider.Machine.Guid; try { - var machineServiceAddress = SettingsManager.Default.GetOrCreate<PPCSettings>().GetMachineServiceAddress(); + _isUpdating = true; + + var machineServiceAddress = _settings.GetMachineServiceAddress(); - LogManager.Log($"Starting machine update for serial number {serialNumber}..."); + LogManager.Log($"Starting machine update..."); //Connecting to machine... LogManager.Log("Verifying machine connection and state..."); @@ -132,10 +689,11 @@ namespace Tango.PPC.Common.MachineUpdate { throw LogManager.Log(new InvalidOperationException("Could not perform an update while the machine is not connected.")); } - if (op.IsPrinting) - { - throw LogManager.Log(new InvalidOperationException($"Could not perform an update while the machine is in {op.Status} status.")); - } + } + + if (!op.CanPrint) + { + throw LogManager.Log(new InvalidOperationException($"Could not perform an update while the machine is in {op.Status} status.")); } //Connect to machine service and get matching packages for this machine. @@ -143,21 +701,14 @@ namespace Tango.PPC.Common.MachineUpdate LogManager.Log($"Connecting to machine service on {machineServiceAddress}..."); - await Login(serialNumber); + await Login(machineGuid); DownloadUpdateRequest request = new DownloadUpdateRequest(); - request.SerialNumber = serialNumber; - - DownloadUpdateResponse update_response = null; update_response = await _client.MachineUpdate(request); 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. @@ -167,68 +718,102 @@ namespace Tango.PPC.Common.MachineUpdate LogManager.Log("Downloading software package..."); - long fileSize = 0; UpdateProgress("Downloading software package", "Downloading...", false); - using (FileStreamWrapper fs = new FileStreamWrapper(tempFile.Path, FileMode.Create, (current) => + using (AutoFileDownloader downloader = new AutoFileDownloader(update_response.BlobAddress, update_response.CdnAddress, tempFile)) { - UpdateProgress("Downloading software package", "Downloading...", false, current, fileSize); - })) - { - LogManager.Log($"Connecting to storage blob with address {update_response.BlobAddress}"); - CloudBlockBlob blob = new CloudBlockBlob(new Uri(update_response.BlobAddress)); - LogManager.Log("Fetching blob attributes..."); - blob.FetchAttributes(); - fileSize = blob.Properties.Length; - LogManager.Log("Download size: " + fileSize + " bytes."); - LogManager.Log("Starting blob download..."); - blob.DownloadToStream(fs); + await downloader.ResolveMode(); + + if (downloader.Mode == AutoFileDownloader.DownloadMode.Standard) + { + LogManager.Log($"Connecting to storage CDN with address {downloader.Address}"); + } + else + { + LogManager.Log($"Connecting to storage blob with address {downloader.Address}"); + } + + downloader.Progress += (x, e) => + { + UpdateProgress("Downloading software package", "Downloading...", false, e.Current, e.Total); + }; + + var size = await downloader.GetFileSize(); + LogManager.Log("Download size: " + size + " bytes."); + LogManager.Log("Starting file download..."); + await downloader.Download(); } 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. File.Copy(Path.Combine(_newPackageTempFolder, "Tango.PPC.Updater.exe"), Path.Combine(PathHelper.GetStartupPath(), "Tango.PPC.Updater.exe"), true); - - //Synchronize database - UpdateProgress("Updating Database", "Initializing..."); - - LogManager.Log($"Synchronizing database '{update_response.DataSource.ToString()}' => '{localDataSource.ToString()}'..."); - - 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)) + //Create Database Backup + UpdateProgress("Updating Database", "Creating database backup..."); + try { - throw new InvalidProgramException("Database tango does not exists."); + Directory.CreateDirectory(backupsFolder); + dbBackupFile = $"{backupsFolder}\\{Path.GetRandomFileName()}.bak"; + _settings.LastDatabaseBackupFile = dbBackupFile; + _settings.Save(); + 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."); } - if (setupFirmware) + //Run pre-update packages. + try { - LogManager.Log("Setup firmware is active so a database rollback procedure should be configured."); - UpdateProgress("Updating Database", "Creating database backup..."); + UpdateProgress("Preparing", "Running update packages..."); + LogManager.Log("Running pre-update packages..."); + var packagesFolder = Path.Combine(_newPackageTempFolder, "Packages"); + Version updateVersion = new Version(1, 0, 0, 0); try { - Directory.CreateDirectory("C:\\Backups"); - dbBackupFile = $"C:\\Backups\\{Path.GetRandomFileName()}.bak"; - LogManager.Log($"Creating database backup to '{dbBackupFile}'..."); - await Task.Factory.StartNew(() => db.Backup(localDataSource.Catalog, dbBackupFile)); - LogManager.Log("Database backup created successfully."); + updateVersion = Version.Parse(update_response.Version); } catch (Exception ex) { - throw LogManager.Log(ex, "Setup manager error while trying to create a database backup."); + 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..."); + + LogManager.Log($"Synchronizing database '{update_response.DataSource.ToString()}' => '{localDataSource.ToString()}'..."); + + UpdateProgress("Updating Database", "Connecting to local database..."); + + LogManager.Log("Checking Tango database exists on the local machine..."); + if (!db.Exists(localDataSource.Catalog)) + { + throw new InvalidProgramException("Database tango does not exists."); } LogManager.Log("Disposing database manager."); @@ -243,7 +828,7 @@ namespace Tango.PPC.Common.MachineUpdate Path.Combine(_newPackageTempFolder, "Update Scripts"), update_response.DataSource, localDataSource, - serialNumber); + machineGuid); runner.Log += (x, msg) => { @@ -267,14 +852,12 @@ namespace Tango.PPC.Common.MachineUpdate } catch (Exception ex) { - throw LogManager.Log(ex, "Setup manager error while trying to synchronize database."); + throw LogManager.Log(ex, "Update manager error while trying to synchronize database."); } //Updating firmware if (setupFirmware) { - performDatabaseRollback = true; - UpdateProgress("Updating Firmware", "Connecting to firmware device..."); LogManager.Log(""); LogManager.Log("-------------------------------------------------------------------------"); @@ -293,106 +876,88 @@ namespace Tango.PPC.Common.MachineUpdate op.FirmwareUpgradeMode = FirmwareUpgradeModes.DFU; } - var handler = await op.UpgradeFirmware(stream); + var handler = await op.UpgradeFirmware(stream, _machineProvider.Machine.IsDemo); handler.Failed += (_, ex) => { stream.Dispose(); - throw ex; + OnFailed(ex, result, update_response, performDatabaseRollback, dbBackupFile, backupsFolder, null, localDataSource, _newPackageTempFolder); }; handler.Completed += (_, __) => { UpdateProgress("Updating Firmware", "Firmware update completed successfully."); stream.Dispose(); - result.SetResult(new MachineUpdateResult() + OnCompleted(new MachineUpdateResult() { UpdatePackagePath = _newPackageTempFolder, - }); + }, result, update_response, null, backupsFolder, localDataSource); }; handler.Canceled += (_, __) => { stream.Dispose(); - throw new Exception("The operation has been canceled."); + OnFailed(new Exception("The operation has been canceled."), result, update_response, performDatabaseRollback, dbBackupFile, backupsFolder, null, localDataSource, _newPackageTempFolder); }; handler.Progress += (_, e) => { - UpdateProgress("Updating Firmware", e.Message, false, e.Current, e.Total); + UpdateProgress("Updating Firmware", e.Message, e.IsIndeterminate, e.Current, e.Total); }; } else { - result.SetResult(new MachineUpdateResult() + OnCompleted(new MachineUpdateResult() { UpdatePackagePath = _newPackageTempFolder, - }); + }, result, update_response, null, backupsFolder, localDataSource); } } catch (Exception ex) { - LogManager.Log(ex, "An error occurred in machine update."); - - if (performDatabaseRollback) - { - LogManager.Log("Rolling back database changes..."); - - using (DbManager db = DbManager.FromDataSource(localDataSource)) - { - try - { - UpdateProgress("Rollback", "Rolling back database changes..."); - await Task.Factory.StartNew(() => db.Restore(localDataSource.Catalog, dbBackupFile)); - LogManager.Log("Database restored successfully."); - } - catch (Exception e) - { - LogManager.Log(e, "Could not rollback the database."); - throw ex; - } - finally - { - try - { - File.Delete(dbBackupFile); - } - catch { } - } - } - } - - result.SetException(ex); - } - finally - { - try - { - File.Delete(dbBackupFile); - } - catch { } + OnFailed(ex, result, update_response, performDatabaseRollback, dbBackupFile, backupsFolder, null, localDataSource, _newPackageTempFolder); } return await result.Task; } /// <summary> - /// Checks if any update are available for the specified machine serial number. + /// Checks if any update are available for the specified machine. /// </summary> - /// <param name="serialNumber">The serial number.</param> /// <param name="machineServiceAddress">The machine service address.</param> /// <returns></returns> - public Task<CheckForUpdateResponse> CheckForUpdate(string serialNumber) + public Task<CheckForUpdateResponse> CheckForUpdate() { return Task.Factory.StartNew<CheckForUpdateResponse>(() => { - var machineServiceAddress = SettingsManager.Default.GetOrCreate<PPCSettings>().GetMachineServiceAddress(); + _isUpdating = true; + + var machineServiceAddress = _settings.GetMachineServiceAddress(); LogManager.Log($"Connecting to machine service on {machineServiceAddress}..."); - Login(serialNumber).GetAwaiter().GetResult(); + String machineGuid = _machineProvider.Machine.Guid; + + Login(machineGuid).GetAwaiter().GetResult(); LogManager.Log($"Checking if updates available..."); CheckForUpdateRequest request = new CheckForUpdateRequest(); - request.SerialNumber = serialNumber; request.Version = _app_manager.Version.ToString(); + request.FirmwareVersion = _app_manager.FirmwareVersion?.ToString(); + + try + { + request.MachineLastUpdated = _machineProvider.Machine.LastUpdated; + + using (ObservablesContext db = ObservablesContext.CreateDefault()) + { + request.Rmls = db.Rmls.ToList().Select(x => new UpdatedEntity(x)).ToList(); + request.HardwareVersions = db.HardwareVersions.ToList().Select(x => new UpdatedEntity(x)).ToList(); + request.Catalogs = db.ColorCatalogs.ToList().Select(x => new UpdatedEntity(x)).ToList(); + request.UsedRmlsGuids = db.Jobs.Select(x => x.RmlGuid).Distinct().ToList(); + } + } + catch (Exception ex) + { + LogManager.Log(ex, "An error occurred while trying to fill the existing database entities before checking for updates."); + } CheckForUpdateResponse update_response = null; @@ -400,6 +965,8 @@ namespace Tango.PPC.Common.MachineUpdate LogManager.Log($"Check for update response received: {Environment.NewLine}{update_response.ToJsonString()}"); + _isUpdating = false; + return update_response; }); } @@ -407,96 +974,145 @@ namespace Tango.PPC.Common.MachineUpdate /// <summary> /// Updates all the "overwrite-able" database tables. /// </summary> - /// <param name="serialNumber">The serial number.</param> /// <param name="machineServiceAddress">The machine service address.</param> /// <returns></returns> - public Task UpdateDB(DbCompareResult dbCompareResult, String serialNumber) + public Task UpdateDB(DbCompareResult dbCompareResult) { + _updateStartDate = DateTime.UtcNow; + _logs.Clear(); + return Task.Factory.StartNew(() => { - LogManager.Log("Starting database update..."); + _isUpdating = true; + UpdateDBResponse update_response = null; + var localDataSource = SettingsManager.Default.GetOrCreate<CoreSettings>().DataSource; + bool performDatabaseRollback = false; + String dbBackupFile = null; - UpdateProgress("Updating Database", "Initializing..."); + try + { + LogManager.Log("Starting database update..."); - LogManager.Log("Looking for update scripts configuration on application path..."); + if (_machineProvider.MachineOperator.IsPrinting) + { + throw LogManager.Log(new InvalidOperationException($"Could not perform a database update while the machine is dyeing.")); + } - String config_file = Path.Combine(PathHelper.GetStartupPath(), "Update Scripts", "config.xml"); + UpdateProgress("Updating Database", "Initializing..."); - if (!File.Exists(config_file)) - { - throw LogManager.Log(new FileNotFoundException($"Could not locate '{config_file}' file on application folder.")); - } + LogManager.Log("Looking for update scripts configuration on application path..."); - UpdateDBResponse update_response = dbCompareResult.UpdateDBResponse; + String config_file = Path.Combine(PathHelper.GetStartupPath(), "Update Scripts", "config.xml"); - var localDataSource = SettingsManager.Default.GetOrCreate<CoreSettings>().DataSource; + if (!File.Exists(config_file)) + { + throw LogManager.Log(new FileNotFoundException($"Could not locate '{config_file}' file on application folder.")); + } - LogManager.Log($"Updating database '{update_response.DataSource.ToString()}' => '{localDataSource.ToString()}'..."); + update_response = dbCompareResult.UpdateDBResponse; - UpdateProgress("Updating Database", "Initializing update sequence..."); + LogManager.Log($"Updating database '{update_response.DataSource.ToString()}' => '{localDataSource.ToString()}'..."); - ExaminerSequenceConfiguration config_sequence = ExaminerSequenceConfiguration.FromFile(config_file); + UpdateProgress("Updating Database", "Initializing update sequence..."); - foreach (var item in config_sequence.Items.Where(x => x.Type == ExaminerSequenceItemType.Data || update_response.PerformSchemaUpdate).OrderBy(x => x.Index)) - { - LogManager.Log($"Executing update script '{item.FileName}...'"); + ExaminerSequenceConfiguration config_sequence = ExaminerSequenceConfiguration.FromFile(config_file); - ExaminerConfigurationBuilder builder = new ExaminerConfigurationBuilder(Path.Combine(Path.GetDirectoryName(config_file), item.FileName)); - builder.SetSource(update_response.DataSource); - builder.SetTarget(localDataSource); + UpdateProgress("Updating Database", "Connecting to local database..."); + LogManager.Log("Initializing database manager..."); + DbManager db = DbManager.FromDataSource(localDataSource); - if (item.RequiresSerialNumber) + LogManager.Log("Checking Tango database exists on the local machine..."); + if (!db.Exists(localDataSource.Catalog)) { - builder.SetMachineSerialNumber(serialNumber); + throw new InvalidProgramException("Database tango does not exists."); } - builder.Synchronize(); - - var config = builder.Build(); + UpdateProgress("Updating Database", "Creating database backup..."); - ExaminerProcess process = new ExaminerProcess(config, item.Type == ExaminerSequenceItemType.Data ? ExaminerProcessType.Data : ExaminerProcessType.Schema); - process.Progress += (x, msg) => + //Create Database Backup + try { - LogManager.Log(msg); - }; + Directory.CreateDirectory("C:\\Backups"); + dbBackupFile = $"C:\\Backups\\{Path.GetRandomFileName()}.bak"; + LogManager.Log($"Creating database backup to '{dbBackupFile}'..."); + 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."); + } - try + LogManager.Log("Disposing database manager."); + db.Dispose(); + + foreach (var item in config_sequence.Items.Where(x => x.Type == ExaminerSequenceItemType.Data || update_response.PerformSchemaUpdate).OrderBy(x => x.Index)) { - UpdateProgress("Updating Database", item.Name + "..."); + LogManager.Log($"Executing update script '{item.FileName}...'"); - var result = process.Execute().Result; + ExaminerConfigurationBuilder builder = new ExaminerConfigurationBuilder(Path.Combine(Path.GetDirectoryName(config_file), item.FileName)); + builder.SetSource(update_response.DataSource); + builder.SetTarget(localDataSource); - if (result.ExitCode != ExaminerProcessExitCode.Success) + if (item.RequiresSerialNumber) { - throw LogManager.Log(new InvalidDataException($"{item.FileName} script has terminated with exit code '{result.ExitCode}'.")); + builder.SetMachineSerialNumber(_machineProvider.Machine.Guid); } - LogManager.Log("Script executed successfully."); - } - catch (Exception ex) - { - throw LogManager.Log(ex, "Setup manager error while trying to update the database."); + builder.Synchronize(); + + var config = builder.Build(); + + ExaminerProcess process = new ExaminerProcess(config, item.Type == ExaminerSequenceItemType.Data ? ExaminerProcessType.Data : ExaminerProcessType.Schema); + process.Progress += (x, msg) => + { + LogManager.Log(msg); + }; + + try + { + UpdateProgress("Updating Database", item.Name + "..."); + + var result = process.Execute().Result; + + if (result.ExitCode != ExaminerProcessExitCode.Success) + { + throw LogManager.Log(new InvalidDataException($"{item.FileName} script has terminated with exit code '{result.ExitCode}'.")); + } + + LogManager.Log("Script executed successfully."); + } + catch (Exception ex) + { + throw LogManager.Log(ex, "Upudate manager error while trying to update the database."); + } } - } - UpdateProgress("Updating Database", "Database synchronization completed successfully."); - LogManager.Log("Update completed successfully."); + UpdateProgress("Updating Database", "Database synchronization completed successfully."); + LogManager.Log("Update completed successfully."); + OnCompleted(update_response); + } + catch (Exception ex) + { + OnFailed(ex, update_response, performDatabaseRollback, dbBackupFile, localDataSource); + throw ex; + } }); } /// <summary> /// Checks whether it is necessary to updates all the "overwrite-able" database tables. /// </summary> - /// <param name="serialNumber">The serial number.</param> /// <param name="machineServiceAddress">The machine service address.</param> /// <returns></returns> - public Task<DbCompareResult> UpdateDBCheck(string serialNumber) + public Task<DbCompareResult> UpdateDBCheck() { return Task.Factory.StartNew<DbCompareResult>(() => { - var machineServiceAddress = SettingsManager.Default.GetOrCreate<PPCSettings>().GetMachineServiceAddress(); + var machineServiceAddress = _settings.GetMachineServiceAddress(); - LogManager.Log($"Checking if database update is required for serial number {serialNumber}..."); + LogManager.Log($"Checking if database update is required..."); LogManager.Log("Looking for update scripts configuration on application path..."); @@ -509,10 +1125,11 @@ namespace Tango.PPC.Common.MachineUpdate LogManager.Log($"Connecting to machine service on {machineServiceAddress}..."); - Login(serialNumber).Wait(); + Login(_machineProvider.Machine.Guid).Wait(); UpdateDBRequest request = new UpdateDBRequest(); - request.SerialNumber = serialNumber; + request.ApplicationVersion = _app_manager.Version.ToString(); + request.FirmwareVersion = _app_manager.FirmwareVersion.ToString(); UpdateDBResponse update_response = null; @@ -541,7 +1158,7 @@ namespace Tango.PPC.Common.MachineUpdate if (item.RequiresSerialNumber) { - builder.SetMachineSerialNumber(serialNumber); + builder.SetMachineSerialNumber(_machineProvider.Machine.Guid); } var config = builder.Build(); @@ -580,12 +1197,18 @@ namespace Tango.PPC.Common.MachineUpdate } catch (Exception ex) { + OnFailed(ex, update_response, false, null, null); throw LogManager.Log(ex, "Update manager error while trying to compare the database."); } } LogManager.Log("Comparison completed successfully."); + if (!has_differences) + { + OnCompleted(update_response, true); + } + return new DbCompareResult() { RequiresUpdate = has_differences, @@ -599,31 +1222,373 @@ 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>(() => + _updateStartDate = DateTime.UtcNow; + _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; + PublishInfo publishInfo = null; + + 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; - //Create temporary folders for packages. - var _newPackageTempFolder = TemporaryManager.CreateFolder(); - _newPackageTempFolder.Persist = true; + LogManager.Log($"Starting machine update (TUP) for serial number {serialNumber}..."); - LogManager.Log("Extracting downloaded zip file..."); - //Extract software package. - ZipFile.ExtractToDirectory(fileName, _newPackageTempFolder); + //Connecting to machine... + LogManager.Log("Verifying machine connection and state..."); + + 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.CanPrint) + { + 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.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 != _settings.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"); + + 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."); + } - return new MachineUpdateResult() + await _packageRunner.Run(PackageType.Pre, updateVersion, packagesFolder); + } + catch (Exception ex) { - UpdatePackagePath = _newPackageTempFolder, + 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, + _machineProvider.Machine.Guid); + + runner.Log += (x, msg) => + { + 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 (setupFPGA) + { + op.FirmwareUpgradeMode = FirmwareUpgradeModes.DFU | FirmwareUpgradeModes.TFP_PACKAGE; + } + else + { + op.FirmwareUpgradeMode = FirmwareUpgradeModes.DFU; + } + + var handler = await op.UpgradeFirmware(stream, _machineProvider.Machine.IsDemo); + handler.Failed += (_, ex) => + { + stream.Dispose(); + OnFailed(ex, result, null, performDatabaseRollback, dbBackupFile, backupsFolder, tempDbName, localDataSource, _newPackageTempFolder, publishInfo); + }; + handler.Completed += (_, __) => + { + UpdateProgress("Updating Firmware", "Firmware update completed successfully."); + stream.Dispose(); + OnCompleted(new MachineUpdateResult() + { + UpdatePackagePath = _newPackageTempFolder, + RequiresBinariesUpdate = replaceBinaries, + }, result, null, tempDbName, backupsFolder, localDataSource, publishInfo); + }; + handler.Canceled += (_, __) => + { + stream.Dispose(); + OnFailed(new Exception("The operation has been canceled."), result, null, performDatabaseRollback, dbBackupFile, backupsFolder, tempDbName, localDataSource, _newPackageTempFolder, publishInfo); + }; + handler.Progress += (_, e) => + { + UpdateProgress("Updating Firmware", e.Message, e.IsIndeterminate, e.Current, e.Total); + }; + } + else + { + OnCompleted(new MachineUpdateResult() + { + UpdatePackagePath = _newPackageTempFolder, + RequiresBinariesUpdate = replaceBinaries, + }, result, null, tempDbName, backupsFolder, localDataSource, publishInfo); + } + } + catch (Exception ex) + { + OnFailed(ex, result, null, performDatabaseRollback, dbBackupFile, backupsFolder, tempDbName, localDataSource, _newPackageTempFolder, publishInfo); + } + + return await result.Task; + } + + /// <summary> + /// Performs a firmware upgrade from the specified TFP file. + /// </summary> + /// <param name="fileName">Name of the file.</param> + /// <returns></returns> + /// <exception cref="InvalidOperationException"> + /// Could not perform a firmware upgrade while the machine is not connected. + /// or + /// </exception> + public async Task UpdateFromTFP(String fileName) + { + _updateStartDate = DateTime.UtcNow; + _logs.Clear(); + + TaskCompletionSource<object> result = new TaskCompletionSource<object>(); + + String version = String.Empty; + Stream stream = null; + + try + { + _isUpdating = true; + + IMachineOperator op = _machineProvider.MachineOperator; + + UpdateProgress("Updating Firmware", "Loading firmware package..."); + stream = new FileStream(fileName, FileMode.Open); + + var packageInfo = await op.GetFirmwarePackageInfo(stream); + stream.Position = 0; + version = packageInfo.FileDescriptors.FirstOrDefault(x => x.Destination == PMR.FirmwareUpgrade.VersionFileDestination.Mcu)?.Version; + + LogManager.Log("Verifying machine connection and state..."); + + UpdateProgress("Verifying machine state", "Initializing..."); + + await Task.Delay(1000); + + if (op.State != Transport.TransportComponentState.Connected) + { + throw LogManager.Log(new InvalidOperationException("Could not perform a firmware upgrade while the machine is not connected.")); + } + if (!op.CanPrint) + { + throw LogManager.Log(new InvalidOperationException($"Could not perform a firmware upgrade while the machine is in {op.Status} status.")); + } + + UpdateProgress("Updating Firmware", "Connecting to firmware device..."); + LogManager.Log(""); + LogManager.Log("-------------------------------------------------------------------------"); + LogManager.Log("Updating Firmware..."); + + op.FirmwareUpgradeMode = FirmwareUpgradeModes.DFU | FirmwareUpgradeModes.TFP_PACKAGE; + + var handler = await op.UpgradeFirmware(stream, _machineProvider.Machine.IsDemo); + handler.Failed += (_, ex) => + { + stream.Dispose(); + OnFailed(ex, result, version); + }; + handler.Completed += (_, __) => + { + UpdateProgress("Updating Firmware", "Firmware update completed successfully."); + stream.Dispose(); + OnCompleted(result, version); + }; + handler.Canceled += (_, __) => + { + stream.Dispose(); + OnFailed(new Exception("The operation has been canceled."), result, version); + }; + handler.Progress += (_, e) => + { + UpdateProgress("Updating Firmware", e.Message, e.IsIndeterminate, e.Current, e.Total); + }; + } + catch (Exception ex) + { + try + { + if (stream != null) + { + stream.Dispose(); + } + } + catch { } + + OnFailed(ex, result, version); + } + + await result.Task; } /// <summary> @@ -631,25 +1596,88 @@ 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(); + + using (StreamReader stReader = new StreamReader(reader)) + { + String json = stReader.ReadToEnd(); + reader.Dispose(); + + return PublishInfo.FromJson(json); + } } + }); + } + + /// <summary> + /// Checks whether any post update packages needs to be installed. + /// </summary> + /// <returns></returns> + public Task<bool> PostUpdatePackagesRequired() + { + String packagesFolder = Path.Combine(AssemblyHelper.GetCurrentAssemblyFolder(), "packages"); + return _packageRunner.IsPackageInstallationRequired(PackageType.Post, packagesFolder); + } + + /// <summary> + /// Runs all post update packages. + /// </summary> + /// <returns></returns> + public Task<PackageRunnerResult> RunPostUpdatePackages() + { + String packagesFolder = Path.Combine(AssemblyHelper.GetCurrentAssemblyFolder(), "packages"); - FileVersionInfo info = FileVersionInfo.GetVersionInfo(Path.Combine(tempFolder, "Tango.PPC.UI.exe")); - file.Version = Version.Parse(info.ProductVersion); + Version previousVersion = null; + String str = _settings.PreviousApplicationVersion; - tempFolder.Delete(); + if (Version.TryParse(str, out previousVersion)) + { + return _packageRunner.Run(PackageType.Post, previousVersion, packagesFolder); + } + else + { + throw new InvalidCastException($"Error parsing the previous version string '{str}'."); + } + } - return file; + public Task RestoreLastDatabaseBackup() + { + return Task.Factory.StartNew(() => + { + LogManager.Log("Rolling back database changes..."); + UpdateProgress("Rollback", "Rolling back database changes..."); + + var localDataSource = SettingsManager.Default.GetOrCreate<CoreSettings>().DataSource; + var lastBackupFile = SettingsManager.Default.GetOrCreate<PPCSettings>().LastDatabaseBackupFile; + + using (DbManager db = DbManager.FromDataSource(localDataSource)) + { + try + { + db.Restore(localDataSource.Catalog, lastBackupFile); + LogManager.Log("Database restored successfully."); + } + catch (Exception ex) + { + LogManager.Log(ex, "Could not rollback the database after a failed updater."); + throw ex; + } + finally + { + try + { + File.Delete(lastBackupFile); + } + catch { } + } + } }); } @@ -677,5 +1705,56 @@ namespace Tango.PPC.Common.MachineUpdate } #endregion + + #region Auto Check For Update + + private async void _checkForUpdateTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) + { + if (EnableAutoCheckForUpdates && _settings.AutoCheckForUpdates && !_isUpdating) + { + _checkForUpdateTimer.Stop(); + + try + { + var response = await CheckForUpdate(); + if (response.IsUpdateAvailable || response.IsDatabaseUpdateAvailable) + { + LogManager.Log($"New {(response.IsDatabaseUpdateAvailable ? "database updates" : "application version")} detected ({response.Version}). Raising event..."); + UpdateAvailable?.Invoke(this, response); + } + } + catch { } + + _checkForUpdateTimer.Start(); + } + } + + #endregion + + #region External Bridge + + public void OnReceiverDisconnected(ExternalBridgeReceiver receiver) + { + //Do nothing. + } + + [ExternalBridgeRequestHandlerMethod(typeof(GetUpdatesAndPackagesRequest), RequestHandlerLoggingMode.LogRequestName)] + public async Task OnGetUpdatesAndPackagesRequest(GetUpdatesAndPackagesRequest request, String token, ExternalBridgeReceiver receiver) + { + using (ObservablesContext db = ObservablesContext.CreateDefault()) + { + var updates = await db.TangoUpdates.OrderByDescending(x => x.StartDate).ToListAsync(); + var updatesDTO = updates.Select(x => TangoUpdateDTO.FromObservable(x)).ToList(); + var packages = (await _packageRunner.GetPackagesFile()).PackageInstallations; + + var response = new GetUpdatesAndPackagesResponse(); + response.Updates.AddRange(updatesDTO); + response.Packages.AddRange(packages); + + await receiver.SendGenericResponse(response, token); + } + } + + #endregion } } |
