aboutsummaryrefslogtreecommitdiffstats
path: root/Software/Visual_Studio/PPC/Tango.PPC.UI/Images/ThreadLoading/TheDryer/3.jpg
diff options
context:
space:
mode:
authorShlomo Hecht <shlomo@twine-s.com>2018-09-02 18:57:49 +0300
committerShlomo Hecht <shlomo@twine-s.com>2018-09-02 18:57:49 +0300
commitc3741bba6733fa5ba4a0ec5856efddf2fc57297a (patch)
tree9e1bdf2e7c0baf5dc24350a46cc7e6d6584180e6 /Software/Visual_Studio/PPC/Tango.PPC.UI/Images/ThreadLoading/TheDryer/3.jpg
parent0ad4d65c969f62476d4bf2eeaea6f3ddfc375580 (diff)
downloadTango-c3741bba6733fa5ba4a0ec5856efddf2fc57297a.tar.gz
Tango-c3741bba6733fa5ba4a0ec5856efddf2fc57297a.zip
removed debug structure
Diffstat (limited to 'Software/Visual_Studio/PPC/Tango.PPC.UI/Images/ThreadLoading/TheDryer/3.jpg')
0 files changed, 0 insertions, 0 deletions
using FluentFTP;
using Microsoft.WindowsAzure.Storage.Blob;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Tango.Core;
using Tango.Core.DB;
using Tango.Core.ExtensionMethods;
using Tango.Core.Helpers;
using Tango.Core.IO;
using Tango.Integration.Operation;
using Tango.Integration.Upgrade;
using Tango.PMR.Synchronization;
using Tango.PPC.Common.Application;
using Tango.PPC.Common.Connection;
using Tango.PPC.Common.Web;
using Tango.Settings;
using Tango.SharedUI.Helpers;
using Tango.SQLExaminer;
using Tango.Transport.Web;

namespace Tango.PPC.Common.MachineUpdate
{
    public class MachineUpdateManager : ExtendedObject, IMachineUpdateManager
    {
        private IPPCApplicationManager _app_manager;
        private IMachineProvider _machineProvider;
        private PPCWebClient _client;

        #region Events

        /// <summary>
        /// Occurs when there is a text log message available.
        /// </summary>
        public event EventHandler<string> ProgressLog;

        /// <summary>
        /// Occurs when the update has made some progress.
        /// </summary>
        public event EventHandler<MachineUpdateProgress> Progress;

        #endregion

        #region Properties

        private MachineUpdateProgress _status;
        public MachineUpdateProgress Status
        {
            get { return _status; }
            private set { _status = value; RaisePropertyChangedAuto(); }
        }

        #endregion

        #region Constructors

        /// <summary>
        /// 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)
        {
            _client = ppcWebClient;
            _machineProvider = machineProvider;
            _app_manager = applicationManager;
        }

        #endregion

        #region Private Methods

        private Task Login(String serialNumber)
        {
            return _client.Login(new LoginRequest()
            {
                Mode = LoginMode.Machine,
                SerialNumber = serialNumber,
            });
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// Performs a machine update using the specified serial number and machine service address.
        /// </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>
        /// <exception cref="System.InvalidOperationException">
        /// Could not perform an update while the machine is not connected.
        /// or
        /// or
        /// </exception>
        /// <exception cref="System.InvalidProgramException">Database tango does not exists.</exception>
        public async Task<MachineUpdateResult> Update(String serialNumber, bool setupFirmware, bool setupFPGA)
        {
            TaskCompletionSource<MachineUpdateResult> result = new TaskCompletionSource<MachineUpdateResult>();

            var localDataSource = SettingsManager.Default.GetOrCreate<CoreSettings>().DataSource;
            bool performDatabaseRollback = false;
            String dbBackupFile = null;

            try
            {
                var machineServiceAddress = SettingsManager.Default.GetOrCreate<PPCSettings>().GetMachineServiceAddress();

                LogManager.Log($"Starting machine update for serial number {serialNumber}...");

                //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.IsPrinting)
                    {
                        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.
                UpdateProgress("Downloading software package", "Connecting to machine service...");

                LogManager.Log($"Connecting to machine service on {machineServiceAddress}...");

                await Login(serialNumber);

                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.
                var tempFile = TemporaryManager.CreateFile(".zip");

                LogManager.Log($"Temporary package zip file created: {tempFile}.");

                LogManager.Log("Downloading software package...");

                long fileSize = 0;
                UpdateProgress("Downloading software package", "Downloading...", false);

                using (FileStreamWrapper fs = new FileStreamWrapper(tempFile.Path, FileMode.Create, (current) =>
                {
                    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);
                }

                UpdateProgress("Downloading software package", "Extracting package...");

                LogManager.Log("Extracting downloaded zip file...");
                //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))
                {
                    throw new InvalidProgramException("Database tango does not exists.");
                }

                if (setupFirmware)
                {
                    LogManager.Log("Setup firmware is active so a database rollback procedure should be configured.");
                    UpdateProgress("Updating Database", "Creating database backup...");

                    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.");
                    }
                    catch (Exception ex)
                    {
                        throw LogManager.Log(ex, "Setup manager error while trying to create a database backup.");
                    }
                }

                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"),
                        update_response.DataSource,
                        localDataSource,
                        serialNumber);

                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, "Setup manager error while trying to synchronize database.");
                }

                //Updating firmware
                if (setupFirmware)
                {
                    performDatabaseRollback = true;

                    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);
                    handler.Failed += (_, ex) =>
                    {
                        stream.Dispose();
                        throw ex;
                    };
                    handler.Completed += (_, __) =>
                    {
                        UpdateProgress("Updating Firmware", "Firmware update completed successfully.");
                        stream.Dispose();
                        result.SetResult(new MachineUpdateResult()
                        {
                            UpdatePackagePath = _newPackageTempFolder,
                        });
                    };
                    handler.Canceled += (_, __) =>
                    {
                        stream.Dispose();
                        throw new Exception("The operation has been canceled.");
                    };
                    handler.Progress += (_, e) =>
                    {
                        UpdateProgress("Updating Firmware", e.Message, false, e.Current, e.Total);
                    };
                }
                else
                {
                    result.SetResult(new MachineUpdateResult()
                    {
                        UpdatePackagePath = _newPackageTempFolder,
                    });
                }
            }
            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 { }
            }

            return await result.Task;
        }

        /// <summary>
        /// Checks if any update are available for the specified machine serial number.
        /// </summary>
        /// <param name="serialNumber">The serial number.</param>
        /// <param name="machineServiceAddress">The machine service address.</param>
        /// <returns></returns>
        public Task<CheckForUpdateResponse> CheckForUpdate(string serialNumber)
        {
            return Task.Factory.StartNew<CheckForUpdateResponse>(() =>
            {
                var machineServiceAddress = SettingsManager.Default.GetOrCreate<PPCSettings>().GetMachineServiceAddress();

                LogManager.Log($"Connecting to machine service on {machineServiceAddress}...");

                Login(serialNumber).GetAwaiter().GetResult();

                LogManager.Log($"Checking if updates available...");

                CheckForUpdateRequest request = new CheckForUpdateRequest();
                request.SerialNumber = serialNumber;
                request.Version = _app_manager.Version.ToString();

                CheckForUpdateResponse update_response = null;

                update_response = _client.CheckForUpdates(request).Result;

                LogManager.Log($"Check for update response received: {Environment.NewLine}{update_response.ToJsonString()}");

                return update_response;
            });
        }

        /// <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)
        {
            return Task.Factory.StartNew(() =>
            {
                LogManager.Log("Starting database update...");

                UpdateProgress("Updating Database", "Initializing...");

                LogManager.Log("Looking for update scripts configuration on application path...");

                String config_file = Path.Combine(PathHelper.GetStartupPath(), "Update Scripts", "config.xml");

                if (!File.Exists(config_file))
                {
                    throw LogManager.Log(new FileNotFoundException($"Could not locate '{config_file}' file on application folder."));
                }

                UpdateDBResponse update_response = dbCompareResult.UpdateDBResponse;

                var localDataSource = SettingsManager.Default.GetOrCreate<CoreSettings>().DataSource;

                LogManager.Log($"Updating database '{update_response.DataSource.ToString()}' => '{localDataSource.ToString()}'...");

                UpdateProgress("Updating Database", "Initializing update sequence...");

                ExaminerSequenceConfiguration config_sequence = ExaminerSequenceConfiguration.FromFile(config_file);

                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}...'");

                    ExaminerConfigurationBuilder builder = new ExaminerConfigurationBuilder(Path.Combine(Path.GetDirectoryName(config_file), item.FileName));
                    builder.SetSource(update_response.DataSource);
                    builder.SetTarget(localDataSource);

                    if (item.RequiresSerialNumber)
                    {
                        builder.SetMachineSerialNumber(serialNumber);
                    }

                    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, "Setup manager error while trying to update the database.");
                    }
                }

                UpdateProgress("Updating Database", "Database synchronization completed successfully.");
                LogManager.Log("Update completed successfully.");
            });
        }

        /// <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)
        {
            return Task.Factory.StartNew<DbCompareResult>(() =>
            {
                var machineServiceAddress = SettingsManager.Default.GetOrCreate<PPCSettings>().GetMachineServiceAddress();

                LogManager.Log($"Checking if database update is required for serial number {serialNumber}...");

                LogManager.Log("Looking for update scripts configuration on application path...");

                String config_file = Path.Combine(PathHelper.GetStartupPath(), "Update Scripts", "config.xml");

                if (!File.Exists(config_file))
                {
                    throw LogManager.Log(new FileNotFoundException($"Could not locate '{config_file}' file on application folder."));
                }

                LogManager.Log($"Connecting to machine service on {machineServiceAddress}...");

                Login(serialNumber).Wait();

                UpdateDBRequest request = new UpdateDBRequest();
                request.SerialNumber = serialNumber;

                UpdateDBResponse update_response = null;

                update_response = _client.UpdateDB(request).Result;

                LogManager.Log($"Update DB response received: {Environment.NewLine}{update_response.ToJsonString()}");

                var localDataSource = SettingsManager.Default.GetOrCreate<CoreSettings>().DataSource;

                LogManager.Log($"Comparing database '{update_response.DataSource.ToString()}' => '{localDataSource.ToString()}'...");

                var report_file = TemporaryManager.CreateFile(".xml");

                ExaminerSequenceConfiguration config_sequence = ExaminerSequenceConfiguration.FromFile(config_file);

                bool has_differences = false;

                foreach (var item in config_sequence.Items.Where(x => x.Type == ExaminerSequenceItemType.Data).OrderBy(x => x.Index))
                {
                    LogManager.Log($"Executing update script '{item.FileName}...'");

                    ExaminerConfigurationBuilder builder = new ExaminerConfigurationBuilder(Path.Combine(Path.GetDirectoryName(config_file), item.FileName));
                    builder.SetSource(update_response.DataSource);
                    builder.SetTarget(localDataSource);
                    builder.SetReportFile(report_file);

                    if (item.RequiresSerialNumber)
                    {
                        builder.SetMachineSerialNumber(serialNumber);
                    }

                    var config = builder.Build();

                    ExaminerProcess process = new ExaminerProcess(config, ExaminerProcessType.Data);
                    process.Progress += (x, msg) =>
                    {
                        LogManager.Log(msg);
                    };

                    LogManager.Log("Starting comparison process...");
                    LogManager.Log("Generating report on " + report_file);

                    try
                    {
                        var result = process.Execute().Result;

                        if (result.ExitCode != ExaminerProcessExitCode.Success)
                        {
                            throw LogManager.Log(new InvalidDataException(String.Format("Update script has terminated with exit code '{0}'.", result.ExitCode)));
                        }

                        LogManager.Log("Comparison completed successfully!");
                        LogManager.Log("Loading report file...");

                        ExaminerDataReport report = ExaminerDataReport.FromFile(report_file);
                        report_file.Delete();

                        LogManager.Log("Comparison summary: \n" + report.Totals.ToJsonString());

                        if (report.HasDifferences)
                        {
                            has_differences = true;
                            break;
                        }
                    }
                    catch (Exception ex)
                    {
                        throw LogManager.Log(ex, "Update manager error while trying to compare the database.");
                    }
                }

                LogManager.Log("Comparison completed successfully.");

                return new DbCompareResult()
                {
                    RequiresUpdate = has_differences,
                    UpdateDBResponse = update_response,
                };
            });
        }

        /// <summary>
        /// Performs a machine update using the specified software update package path.
        /// </summary>
        /// <param name="fileName">Name of the file.</param>
        /// <returns></returns>
        public Task<MachineUpdateResult> UpdateFromTUP(string fileName)
        {
            return Task.Factory.StartNew<MachineUpdateResult>(() =>
            {
                LogManager.Log($"Starting machine update from update package '{fileName}'...");

                //Create temporary folders for packages.
                var _newPackageTempFolder = TemporaryManager.CreateFolder();
                _newPackageTempFolder.Persist = true;

                LogManager.Log("Extracting downloaded zip file...");
                //Extract software package.
                ZipFile.ExtractToDirectory(fileName, _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);

                LogManager.Log("Update operation completed!");

                return new MachineUpdateResult()
                {
                    UpdatePackagePath = _newPackageTempFolder,
                };
            });
        }

        /// <summary>
        /// Gets the update package file information.
        /// </summary>
        /// <param name="filePath">The file path.</param>
        /// <returns></returns>
        public Task<UpdatePackageFile> GetUpdatePackageFileInfo(string filePath)
        {
            return Task.Factory.StartNew<UpdatePackageFile>(() =>
            {
                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);
                }

                FileVersionInfo info = FileVersionInfo.GetVersionInfo(Path.Combine(tempFolder, "Tango.PPC.UI.exe"));
                file.Version = Version.Parse(info.ProductVersion);

                tempFolder.Delete();

                return file;
            });
        }

        #endregion

        #region Protected Methods

        protected virtual void UpdateProgress(String name, String message = "", bool isIntermediate = true, double progress = 0, double total = 0)
        {
            InvokeUINow(() =>
            {
                Status = new MachineUpdateProgress()
                {
                    Name = name,
                    Message = message,
                    IsIntermediate = isIntermediate,
                    Progress = progress,
                    Total = total,
                };

                Progress?.Invoke(this, Status);
            });

            UIHelper.DoEvents();
        }

        #endregion
    }
}