using System; using System.Collections.Generic; using System.Data.Entity; using System.Diagnostics; 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.Commands; using Tango.Core.ExtensionMethods; using Tango.Core.Helpers; using Tango.Core.Threading; using Tango.Explorer; using Tango.Integration.ExternalBridge; using Tango.PMR.FirmwareUpgrade; using Tango.PPC.Common; using Tango.PPC.Common.ExternalBridge; using Tango.PPC.Common.MachineUpdate; using Tango.PPC.Common.Publish; using Tango.PPC.Common.Web; using Tango.PPC.Shared.RemoteUpgrade; using Tango.PPC.UI.Dialogs; using Tango.PPC.UI.Notifications.NotificationItems; using Tango.PPC.UI.ViewsContracts; using Tango.Transport; namespace Tango.PPC.UI.ViewModels { public class MachineUpdateViewVM : PPCViewModel, IExternalBridgeRequestHandler { public enum MachineUpdateView { UpdateCheckView, UpdateCheckErrorView, UpdateAvailableView, UpToDateView, UpdateProgressView, UpdateDbProgressView, UpdateCompletedView, UpdateFailedView, UpdateFromPackageView, UpdateFailedFromPackageView, } private MachineUpdateResult _update_result; private DbCompareResult _db_compare_result; private bool _isChecking; private CheckForUpdateResponse _checkUpdateResponse; private UpdateAvailableNotificationItem _updateNotificationItem; #region Properties /// /// Gets or sets the machine update manager. /// public IMachineUpdateManager MachineUpdateManager { get; set; } private String _latestVersion; /// /// Gets or sets the latest version. /// public String LatestVersion { get { return _latestVersion; } set { _latestVersion = value; RaisePropertyChangedAuto(); } } private bool _isDbUpdate; /// /// Gets or sets a value indicating whether this instance is database update. /// public bool IsDbUpdate { get { return _isDbUpdate; } set { _isDbUpdate = value; RaisePropertyChangedAuto(); } } private String _failedError; /// /// Gets or sets the setup failed error. /// public String FailedError { get { return _failedError; } set { _failedError = value; RaisePropertyChangedAuto(); } } #endregion #region Commands /// /// Gets or sets the complete command. /// public RelayCommand CompleteCommand { get; set; } /// /// Gets or sets the install command. /// public RelayCommand UpdateCommand { get; set; } /// /// Gets or sets the restart command. /// public RelayCommand RestartCommand { get; set; } /// /// Gets or sets the close command. /// public RelayCommand CloseCommand { get; set; } /// /// Gets or sets to application command. /// public RelayCommand ToApplicationCommand { get; set; } #endregion #region Constructors public MachineUpdateViewVM(IMachineUpdateManager machineUpdateManager, IPPCExternalBridgeService externalBridge) { MachineUpdateManager = machineUpdateManager; externalBridge.RegisterRequestHandler(this); CompleteCommand = new RelayCommand(CompleteUpdate); UpdateCommand = new RelayCommand(Update); RestartCommand = new RelayCommand(CheckForUpdates); CloseCommand = new RelayCommand(() => { NavigationManager.NavigateTo(Common.Navigation.NavigationView.HomeModule); NavigateTo(MachineUpdateView.UpdateCheckView); }); ToApplicationCommand = new RelayCommand(() => { NavigationManager.NavigateTo(Common.Navigation.NavigationView.HomeModule); NavigateTo(MachineUpdateView.UpdateCheckView); }); machineUpdateManager.UpdateAvailable += MachineUpdateManager_UpdateAvailable; } #endregion #region Update public async void CheckForUpdates() { await NavigateTo(MachineUpdateView.UpdateCheckView); if (_isChecking) return; try { _isChecking = true; IsDbUpdate = false; await Task.Delay(2000); if (!await ConnectivityProvider.CheckInternetConnection()) { _isChecking = false; await NavigateTo(MachineUpdateView.UpdateCheckErrorView); return; } var response = await MachineUpdateManager.CheckForUpdate(MachineProvider.Machine.SerialNumber); try { if (response.UsedNotExistingRmlsGuids.Count > 0) { using (ObservablesContext db = ObservablesContext.CreateDefault()) { var arr = response.UsedNotExistingRmlsGuids.ToArray(); var jobs = await db.Jobs.Where(x => arr.Contains(x.RmlGuid)).ToListAsync(); FailedError = $"The following jobs must be removed or change thread type before the system can be updated:\n{String.Join("\n", jobs.Select(x => x.Name))}"; _isChecking = false; await NavigateTo(MachineUpdateView.UpdateFailedView); return; } } } catch (Exception ex) { LogManager.Log(ex, "Error on used RML check procedure."); } _checkUpdateResponse = response; if (response.IsUpdateAvailable) { LatestVersion = response.Version; await NavigateTo(MachineUpdateView.UpdateAvailableView); } else if (response.IsDatabaseUpdateAvailable) { IsDbUpdate = true; _db_compare_result = new DbCompareResult() { RequiresUpdate = true, UpdateDBResponse = response.UpdateDBResponse }; await NavigateTo(MachineUpdateView.UpdateAvailableView); } else { _db_compare_result = await MachineUpdateManager.UpdateDBCheck(MachineProvider.Machine.SerialNumber); if (_db_compare_result.RequiresUpdate) { IsDbUpdate = true; await NavigateTo(MachineUpdateView.UpdateAvailableView); } else { await NavigateTo(MachineUpdateView.UpToDateView); } } } catch (Exception ex) { FailedError = ex.FlattenMessage(); LogManager.Log(ex, "Error while trying to check for updates."); await NavigateTo(MachineUpdateView.UpdateFailedView); } finally { _isChecking = false; } } private async void Update() { if (!IsDbUpdate) { await NavigateTo(MachineUpdateView.UpdateProgressView); LogManager.Log("Starting machine update..."); try { _update_result = await MachineUpdateManager.Update(_checkUpdateResponse.SetupFirmware, _checkUpdateResponse.SetupFPGA); LogManager.Log("Machine update completed."); await NavigateTo(MachineUpdateView.UpdateCompletedView); } catch (Exception ex) { FailedError = ex.FlattenMessage(); LogManager.Log(ex, "Machine update failed."); await NavigateTo(MachineUpdateView.UpdateFailedView); } } else { await NavigateTo(MachineUpdateView.UpdateDbProgressView); LogManager.Log("Starting database update..."); try { await MachineUpdateManager.UpdateDB(_db_compare_result, MachineProvider.Machine.SerialNumber); LogManager.Log("Database update completed."); await NavigateTo(MachineUpdateView.UpdateCompletedView); } catch (Exception ex) { FailedError = ex.FlattenMessage(); LogManager.Log(ex, "Database update failed."); await NavigateTo(MachineUpdateView.UpdateFailedView); } } } #endregion #region Complete private void CompleteUpdate() { LogManager.Log("Completing machine update..."); if (IsDbUpdate || !_update_result.RequiresBinariesUpdate) { LogManager.Log("Restarting Application..."); ApplicationManager.Restart(); } else { String updater_exe = Path.Combine(_update_result.UpdatePackagePath, "Tango.PPC.Updater.exe"); ApplicationManager.UpdateApplication(updater_exe, PathHelper.GetStartupPath()); } } #endregion #region Override Methods /// /// Called when the application has been started. /// public override void OnApplicationStarted() { } /// /// Navigates to the specified view. /// /// The view. private Task NavigateTo(MachineUpdateView view) { return View.NavigateTo(view); } /// /// Called when the application is ready and all modules views are loaded. /// public override void OnApplicationReady() { base.OnApplicationReady(); StorageProvider.RegisterFileHandler(ExplorerFileDefinition.Update.Extension, HandleSoftwareUpdatePackageLoaded); StorageProvider.RegisterFileHandler(ExplorerFileDefinition.Firmware.Extension, HandleFirmwareUpgradeLoaded); if (ApplicationManager.IsAfterUpdate) { RunPostUpdatePackages(); } else { MachineUpdateManager.EnableAutoCheckForUpdates = true; } } /// /// Called when the navigation system has navigated to this VM view. /// public override void OnNavigatedTo() { base.OnNavigatedTo(); if (_updateNotificationItem != null) { _updateNotificationItem.Close(); _updateNotificationItem = null; } } #endregion #region Post Update Packages private async void RunPostUpdatePackages() { await Task.Delay(1000); LogManager.Log("Application was loaded after an update. Checking for required post-update packages..."); bool required = false; try { required = await MachineUpdateManager.PostUpdatePackagesRequired(); } catch (Exception ex) { LogManager.Log(ex, "Error checking for post-update packages."); } if (required) { LogManager.Log("Post-update packages found and needs to be installed. Navigating to machine update and running post-update packages..."); await NavigationManager.NavigateTo(Common.Navigation.NavigationView.MachineUpdateView); await NavigateTo(MachineUpdateView.UpdateProgressView); try { var result = await MachineUpdateManager.RunPostUpdatePackages(); LogManager.Log("Post-update packages installed successfully."); await Task.Delay(2000); if (result.RestartRequired) { LogManager.Log("Restart required. Restarting..."); ApplicationManager.Restart(); } else { await NavigationManager.NavigateTo(Common.Navigation.NavigationView.LayoutView); } } catch (Exception ex) { LogManager.Log(ex, "Error occurred while running post-update packages."); } } else { LogManager.Log("No post-update packages installation required."); } } #endregion #region Handle USB Update private async void HandleSoftwareUpdatePackageLoaded(List fileItems) { var fileItem = fileItems.FirstOrDefault(); if (fileItem == null) return; PublishInfo packageFile = null; LogManager.Log("TUP file loaded from storage..."); try { packageFile = await MachineUpdateManager.GetUpdatePackageFileInfo(fileItem.Path); } catch (Exception ex) { 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; } UpdateFromFileViewVM vm = new UpdateFromFileViewVM(); vm.PublishInfo = packageFile; LogManager.Log($"TUP publish info:\n{packageFile.ToJson()}"); LogManager.Log("Displaying TUP update dialog..."); await NotificationProvider.ShowDialog(vm); if (vm.DialogResult) { await NavigationManager.NavigateTo(Common.Navigation.NavigationView.MachineUpdateView); await NavigateTo(MachineUpdateView.UpdateProgressView); LogManager.Log("Starting machine update from package..."); try { _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); } } } private async void HandleFirmwareUpgradeLoaded(List fileItems) { var fileItem = fileItems.FirstOrDefault(); if (fileItem == null) return; LogManager.Log("TFP file loaded from storage..."); VersionPackageDescriptor packageInfo; FirmwareUpgradeFromFileViewVM vm = new FirmwareUpgradeFromFileViewVM(); try { using (FileStream st = File.OpenRead(fileItem.Path)) { packageInfo = await MachineProvider.MachineOperator.GetFirmwarePackageInfo(st); } vm.Version = packageInfo.FileDescriptors.SingleOrDefault(x => x.Destination == VersionFileDestination.Mcu).Version; } catch (Exception ex) { LogManager.Log(ex, $"Error loading package info from {fileItem.Path}."); await NotificationProvider.ShowError("An error occurred while trying to load the selected firmware upgrade package. Please make sure the package is valid."); return; } LogManager.Log($"TFP publish info:\n{packageInfo.ToJsonString()}"); LogManager.Log("Displaying TFP update dialog..."); await NotificationProvider.ShowDialog(vm); if (vm.DialogResult) { await NavigationManager.NavigateTo(Common.Navigation.NavigationView.MachineUpdateView); await NavigateTo(MachineUpdateView.UpdateProgressView); LogManager.Log("Starting firmware upgrade from package..."); try { await MachineUpdateManager.UpdateFromTFP(fileItem.Path); LogManager.Log("Firmware upgrade from package completed."); _update_result = new MachineUpdateResult() { RequiresBinariesUpdate = false, }; await NavigateTo(MachineUpdateView.UpdateCompletedView); } catch (Exception ex) { LogManager.Log(ex, "Firmware upgrade from package failed."); FailedError = ex.FlattenMessage(); await NavigateTo(MachineUpdateView.UpdateFailedFromPackageView); } } } #endregion #region Auto Check For Update private void MachineUpdateManager_UpdateAvailable(object sender, CheckForUpdateResponse e) { if (!IsVisible && _updateNotificationItem == null) { LogManager.Log($"New {(e.IsDatabaseUpdateAvailable ? "database updates" : "application version")} detected ({e.Version}). Pushing notification..."); InvokeUI(() => { _updateNotificationItem = new UpdateAvailableNotificationItem(); _updateNotificationItem.Version = Version.Parse(e.Version).ToString(3); _updateNotificationItem.IsDatabaseUpdate = e.IsDatabaseUpdateAvailable && !e.IsUpdateAvailable; _updateNotificationItem.Pressed += (_, __) => { _updateNotificationItem = null; if (!IsVisible) { LogManager.Log("Update available notification pressed. Navigating to update view..."); NavigationManager.NavigateTo(Common.Navigation.NavigationView.MachineUpdateView); CheckForUpdates(); } }; _updateNotificationItem.Closed += (_, __) => { _updateNotificationItem = null; }; NotificationProvider.PushNotification(_updateNotificationItem); }); } } #endregion #region External Bridge Handler [ExternalBridgeRequestHandlerMethod(typeof(StartRemoteApplicationUpgradeRequest), RequestHandlerLoggingMode.LogRequestName)] public async Task OnStartRemoteApplicationUpgradeRequest(StartRemoteApplicationUpgradeRequest request, String token, ExternalBridgeReceiver receiver) { await receiver.SendGenericResponse(new StartRemoteApplicationUpgradeResponse(), token); bool stopReporting = false; try { ThreadFactory.StartNew(async () => { while (!stopReporting) { if (MachineUpdateManager.Status != null) { try { await receiver.SendGenericResponse(new StartRemoteApplicationUpgradeResponse() { Progress = new TangoProgress() { Message = MachineUpdateManager.Status.Message, IsIndeterminate = MachineUpdateManager.Status.IsIntermediate, Maximum = MachineUpdateManager.Status.Total, Value = MachineUpdateManager.Status.Progress }, }, token, new TransportResponseConfig() { Priority = QueuePriority.Low }); } catch (Exception ex) { LogManager.Log(ex, "Error sending remote upgrade progress."); } } Thread.Sleep(500); } }); InvokeUI(() => { NavigationManager.NavigateTo(Common.Navigation.NavigationView.MachineUpdateView); NavigateTo(MachineUpdateView.UpdateProgressView); }); LogManager.Log("Starting machine update from package..."); try { _update_result = await MachineUpdateManager.UpdateFromTUP(request.RemoteTupFilePath, request.SetupFirmware, request.SetupFPGA); LogManager.Log("Machine update from package completed."); InvokeUI(() => { NavigateTo(MachineUpdateView.UpdateCompletedView); }); } catch (Exception ex) { LogManager.Log(ex, "Machine update from package failed."); FailedError = ex.FlattenMessage(); InvokeUI(() => { NavigateTo(MachineUpdateView.UpdateFailedFromPackageView); }); } await receiver.SendGenericResponse(new StartRemoteApplicationUpgradeResponse() { Progress = new TangoProgress("Completed", false, 100, 100), }, token, new TransportResponseConfig() { Completed = true }); try { File.Delete(request.RemoteTupFilePath); } catch { } await Task.Delay(2000); CompleteUpdate(); } catch (Exception ex) { await receiver.SendErrorResponse(ex, token); } finally { try { File.Delete(request.RemoteTupFilePath); } catch { } } } [ExternalBridgeRequestHandlerMethod(typeof(StartRemoteFirmwareUpgradeRequest), RequestHandlerLoggingMode.LogRequestName)] public async Task OnStartRemoteFirmwareUpgradeRequest(StartRemoteFirmwareUpgradeRequest request, String token, ExternalBridgeReceiver receiver) { await receiver.SendGenericResponse(new StartRemoteFirmwareUpgradeResponse(), token); bool stopReporting = false; try { ThreadFactory.StartNew(async () => { while (!stopReporting) { if (MachineUpdateManager.Status != null) { try { await receiver.SendGenericResponse(new StartRemoteFirmwareUpgradeResponse() { Progress = new TangoProgress() { Message = MachineUpdateManager.Status.Message, IsIndeterminate = MachineUpdateManager.Status.IsIntermediate, Maximum = MachineUpdateManager.Status.Total, Value = MachineUpdateManager.Status.Progress } }, token, new TransportResponseConfig() { Priority = QueuePriority.Low }); } catch (Exception ex) { LogManager.Log(ex, "Error sending remote upgrade progress."); } } Thread.Sleep(500); } }); InvokeUI(() => { NavigationManager.NavigateTo(Common.Navigation.NavigationView.MachineUpdateView); NavigateTo(MachineUpdateView.UpdateProgressView); }); LogManager.Log("Starting firmware upgrade from package..."); try { await MachineUpdateManager.UpdateFromTFP(request.RemoteTfpFilePath); LogManager.Log("Firmware upgrade from package completed."); _update_result = new MachineUpdateResult() { RequiresBinariesUpdate = false, }; InvokeUI(() => { NavigateTo(MachineUpdateView.UpdateCompletedView); }); } catch (Exception ex) { LogManager.Log(ex, "Firmware upgrade from package failed."); FailedError = ex.FlattenMessage(); InvokeUI(() => { NavigateTo(MachineUpdateView.UpdateFailedFromPackageView); }); } await receiver.SendGenericResponse(new StartRemoteFirmwareUpgradeResponse() { Progress = new TangoProgress("Completed", false, 100, 100), }, token, new TransportResponseConfig() { Completed = true }); try { File.Delete(request.RemoteTfpFilePath); } catch { } await Task.Delay(2000); CompleteUpdate(); } catch (Exception ex) { await receiver.SendErrorResponse(ex, token); } finally { try { File.Delete(request.RemoteTfpFilePath); } catch { } } } public void OnReceiverDisconnected(ExternalBridgeReceiver receiver) { //Do Nothing. } #endregion } }