aboutsummaryrefslogtreecommitdiffstats
path: root/Software/Visual_Studio/PPC/Tango.PPC.Common/MachineSetup/MachineSetupManager.cs
blob: 45fd6c1b8137cbb50514af375d1d63f19f8f4836 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
pre { line-height: 125%; }
td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
<UserControl x:Class="Tango.SharedUI.Controls.ScriptEditorControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:avalonEdit="http://icsharpcode.net/sharpdevelop/avalonedit"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:fa="http://schemas.fontawesome.io/icons/"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" 
             d:DesignHeight="400" d:DesignWidth="600" Background="#151515">

    <UserControl.InputBindings>
        <KeyBinding Key="F5" Command="{Binding RelativeSource={RelativeSource AncestorType=UserControl},Path=RunCommand}" />
    </UserControl.InputBindings>

    <Grid>
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="40"/>
                <RowDefinition Height="1*"/>
            </Grid.RowDefinitions>
            <Border BorderThickness="0 0 0 1" BorderBrush="#545454">
                <ToolBar Background="#202020">
                    <StackPanel Margin="20 0 0 0" Orientation="Horizontal">
                        <Button Cursor="Hand" Command="{Binding RelativeSource={RelativeSource AncestorType=UserControl},Path=SaveCommand}" ToolTip="Save" Style="{DynamicResource MetroCircleButtonStyle}" Width="16" Height="16">
                            <fa:ImageAwesome Icon="Save" Foreground="LightGray"></fa:ImageAwesome>
                        </Button>
                        <Rectangle Margin="20 7 10 5" HorizontalAlignment="Center" VerticalAlignment="Stretch" Stroke="#3E3E3E" StrokeThickness="2"></Rectangle>
                        <Button Cursor="Hand" Command="Undo" Margin="10 0 0 0" ToolTip="Undo"  Style="{DynamicResource MetroCircleButtonStyle}" Width="16" Height="16">
                            <fa:ImageAwesome Icon="Undo" Foreground="LightGray"></fa:ImageAwesome>
                        </Button>
                        <Button Cursor="Hand" Command="Redo" Margin="10 0 0 0" ToolTip="Redo"  Style="{DynamicResource MetroCircleButtonStyle}" Width="16" Height="16">
                            <fa:ImageAwesome Icon="Repeat" Foreground="LightGray"></fa:ImageAwesome>
                        </Button>
                        <Rectangle Margin="20 7 10 5" HorizontalAlignment="Center" VerticalAlignment="Stretch" Stroke="#3E3E3E" StrokeThickness="2"></Rectangle>
                        <Button Cursor="Hand" Command="Cut" Margin="10 0 0 0" ToolTip="Cut"  Style="{DynamicResource MetroCircleButtonStyle}" Width="16" Height="16">
                            <fa:ImageAwesome Icon="Cut" Foreground="LightGray"></fa:ImageAwesome>
                        </Button>
                        <Button Cursor="Hand" Command="Copy" Margin="10 0 0 0" ToolTip="Copy"  Style="{DynamicResource MetroCircleButtonStyle}" Width="20" Height="16">
                            <fa:ImageAwesome Icon="Copy" Foreground="LightGray"></fa:ImageAwesome>
                        </Button>
                        <Button Cursor="Hand" Command="Paste" Margin="10 0 0 0" ToolTip="Paste"  Style="{DynamicResource MetroCircleButtonStyle}" Width="16" Height="20">
                            <fa:ImageAwesome Icon="Paste" Foreground="LightGray"></fa:ImageAwesome>
                        </Button>
                        <Rectangle Margin="20 7 10 5" HorizontalAlignment="Center" VerticalAlignment="Stretch" Stroke="#3E3E3E" StrokeThickness="2"></Rectangle>
                        <Button Cursor="Hand" Margin="10 0 0 0" ToolTip="Run (F5)" Command="{Binding RelativeSource={RelativeSource AncestorType=UserControl},Path=RunCommand}" Style="{DynamicResource MetroCircleButtonStyle}" Width="16" Height="16">
                            <fa:ImageAwesome Icon="Play">
                                <fa:ImageAwesome.Style>
                                    <Style TargetType="fa:ImageAwesome">
                                        <Setter Property="Foreground" Value="#8DD28A"></Setter>
                                        <Style.Triggers>
                                            <Trigger Property="IsEnabled" Value="False">
                                                <Setter Property="Foreground" Value="Gray"></Setter>
                                            </Trigger>
                                        </Style.Triggers>
                                    </Style>
                                </fa:ImageAwesome.Style>
                            </fa:ImageAwesome>
                        </Button>
                        <Button Cursor="Hand" Margin="15 0 0 0" ToolTip="Stop" Command="{Binding RelativeSource={RelativeSource AncestorType=UserControl},Path=StopCommand}" Style="{DynamicResource MetroCircleButtonStyle}" Width="14" Height="14">
                            <fa:ImageAwesome Icon="Stop">
                                <fa:ImageAwesome.Style>
                                    <Style TargetType="fa:ImageAwesome">
                                        <Setter Property="Foreground" Value="#F38B76"></Setter>
                                        <Style.Triggers>
                                            <Trigger Property="IsEnabled" Value="False">
                                                <Setter Property="Foreground" Value="Gray"></Setter>
                                            </Trigger>
                                        </Style.Triggers>
                                    </Style>
                                </fa:ImageAwesome.Style>
                            </fa:ImageAwesome>
                        </Button>
                    </StackPanel>
                </ToolBar>
            </Border>

            <Border Grid.Row="1" CornerRadius="5"  BorderThickness="0" BorderBrush="#404040">
                <avalonEdit:TextEditor Padding="5" TextChanged="textEditor_TextChanged" Background="#151515" Foreground="Gainsboro" Margin="5 5 0 0" ScrollViewer.HorizontalScrollBarVisibility="Auto"
				Name="textEditor"
				FontFamily="Consolas"
				FontSize="10pt"
				SyntaxHighlighting="C#"
                ShowLineNumbers="True">
                    <avalonEdit:TextEditor.ContextMenu>
                
using FluentFTP;
using Microsoft.WindowsAzure.Storage.Blob;
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
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;
using Tango.Core.Helpers;
using Tango.Core.IO;
using Tango.Integration.Operation;
using Tango.Logging;
using Tango.PMR.Synchronization;
using Tango.PPC.Common.Application;
using Tango.PPC.Common.Connection;
using Tango.PPC.Common.OS;
using Tango.PPC.Common.RemoteAssistance;
using Tango.PPC.Common.UWF;
using Tango.PPC.Common.Web;
using Tango.Settings;
using Tango.SharedUI.Helpers;
using Tango.SQLExaminer;
using Tango.Transport.Web;
using System.Data.Entity;

namespace Tango.PPC.Common.MachineSetup
{
    /// <summary>
    /// Represents the PPC machine setup manager.
    /// </summary>
    /// <seealso cref="Tango.Core.ExtendedObject" />
    /// <seealso cref="Tango.PPC.Common.MachineSetup.IMachineSetupManager" />
    public class MachineSetupManager : ExtendedObject, IMachineSetupManager
    {
        private IRemoteAssistanceProvider _remoteAssistance;
        private IUnifiedWriteFilterManager _uwf;
        private IOperationSystemManager _windows_manager;
        private PPCWebClient _client;
        private List<LogItemBase> _logs;
        private bool _isUpdating;
        private DateTime _setupStartDate;

        #region Events

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

        /// <summary>
        /// Occurs when the <see cref="CurrentStep" /> has changed.
        /// </summary>
        public event EventHandler<MachineSetupProgress> Progress;

        #endregion

        #region Properties

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

        #endregion

        #region Constructor

        /// <summary>
        /// Initializes a new instance of the <see cref="MachineSetupManager"/> class.
        /// </summary>
        /// <param name="remoteAssistance">The remote assistance.</param>
        public MachineSetupManager(PPCWebClient ppcWebClient, IRemoteAssistanceProvider remoteAssistance, IUnifiedWriteFilterManager unifiedWriterFilterManager, IOperationSystemManager operationSystemManager)
        {
            _client = ppcWebClient;
            _remoteAssistance = remoteAssistance;
            _uwf = unifiedWriterFilterManager;
            _windows_manager = operationSystemManager;

            _logs = new List<LogItemBase>();
            LogManager.NewLog += LogManager_NewLog;
        }

        #endregion

        #region Event Handlers

        private void LogManager_NewLog(object sender, LogItemBase e)
        {
            if (_isUpdating)
            {
                _logs.Add(e);
            }
        }

        #endregion

        #region Private Methods

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

            });
        }

        private async void OnFailed(Exception ex, TaskCompletionSource<MachineSetupResult> completionSource, MachineSetupResponse response)
        {
            LogManager.Log(ex, "An error occurred in machine setup.");

            completionSource.SetException(ex);

            if (response != null)
            {
                try
                {
                    var result = await _client.NotifyUpdateCompleted(new MachineUpdateCompletedRequest()
                    {
                        Token = response.NotifyCompletedToken,
                        Status = BL.Enumerations.TangoUpdateStatuses.SetupFailed,
                        FailedReason = ex.FlattenMessage(),
                        FailedLog = GetLogsStringAndClear(),
                    });
                }
                catch (Exception xx)
                {
                    LogManager.Log(xx, "Error notifying setup completed.");
                }
            }

            _isUpdating = false;
        }

        private async void OnCompleted(MachineSetupResult result, TaskCompletionSource<MachineSetupResult> completionSource, MachineSetupResponse response)
        {
            completionSource.SetResult(result);

            if (response != null)
            {
                try
                {
                    var r = await _client.NotifyUpdateCompleted(new MachineUpdateCompletedRequest()
                    {
                        Token = response.NotifyCompletedToken,
                        Status = BL.Enumerations.TangoUpdateStatuses.SetupCompleted,
                    });
                }
                catch (Exception xx)
                {
                    LogManager.Log(xx, "Error notifying setup 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.SetupCompleted;
                        update.StartDate = _setupStartDate;
                        update.EndDate = DateTime.UtcNow;
                        await db.SaveChangesAsync();
                    }
                }
                catch (Exception ex)
                {
                    LogManager.Log(ex, "Error saving tango setup information to database.");
                }
            }

            _isUpdating = false;
        }

        private String GetLogsStringAndClear()
        {
            String logsString = String.Join(Environment.NewLine, _logs.ToList().Select(x => x.ToString()));
            _logs.Clear();
            return logsString;
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// Performs a machine setup using the specified serial number and machine service address.
        /// </summary>
        /// <param name="serialNumber">The serial number.</param>
        /// <param name="machineServiceAddress">The machine service address.</param>
        /// <returns></returns>
        public async Task<MachineSetupResult> Setup(string serialNumber)
        {
            _logs.Clear();

            TaskCompletionSource<MachineSetupResult> result = new TaskCompletionSource<MachineSetupResult>();

            MachineSetupResponse setup_response = null;
            _setupStartDate = DateTime.UtcNow;

            try
            {
                _isUpdating = true;

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

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

                IMachineOperator op = null;

                var settings = SettingsManager.Default.GetOrCreate<PPCSettings>();

                //Connect to machine service and get matching packages for this machine.
                UpdateProgress("Validating serial number", "Connecting to machine service...");

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

                Login(serialNumber).Wait();

                String deviceName = $"Tango-{serialNumber}-{settings.DeploymentSlot.ToString()}";
                LogManager.Log($"Settings device name: '{deviceName}'...");
                try
                {
                    await _windows_manager.SetDeviceName(deviceName);
                }
                catch
                {
                    throw new IOException("Error setting device name.");
                }

                MachineSetupRequest request = new MachineSetupRequest();
                request.DeviceID = await _windows_manager.GetDeviceId();
                request.DeviceName = deviceName;

                try
                {
                    LogManager.Log($"Sending setup request...\n{request.ToJsonString()}");
                    setup_response = await _client.MachineSetup(request);
                }
                catch (Exception ex)
                {
                    throw LogManager.Log(ex, $"An error occurred while trying to contact machine service: {ex.FlattenMessage()}");
                }

                LogManager.Log($"Machine setup response received: {Environment.NewLine}{setup_response.ToJsonString()}");

                if (!String.IsNullOrWhiteSpace(setup_response.DeviceComPort))
                {
                    settings.EmbeddedComPort = setup_response.DeviceComPort;
                    settings.Save();
                }

                if (setup_response.SetupFirmware)
                {
                    //Connecting to machine...
                    LogManager.Log("Initiating machine connection...");

                    UpdateProgress("Connecting to machine", "Connecting...");
                    op = await DefaultMachineProvider.CreateMinimalMachineOperator((msg) =>
                    {
                        UpdateProgress("Connecting to machine", msg);
                    });
                }

                if (settings.ApplicationState == ApplicationStates.PreSetup)
                {
                    if (setup_response.SetupActivation)
                    {
                        LogManager.Log("Activating windows license...");
                        UpdateProgress("Activating operation system license", "Activating...");
                        await _windows_manager.Activate(setup_response.OSKey);
                    }
                }

                if (setup_response.SetupRemoteAssistance)
                {
                    LogManager.Log("Installing remote assistance...");
                    UpdateProgress("Installing remote assistance", "Installing...");
                    await _remoteAssistance.InstallRemoteAssistance(serialNumber, setup_response.Organization, settings.DeploymentSlot.ToString());
                }

                if (setup_response.SetupUWF)
                {
                    LogManager.Log("Activating unified write filter...");
                    UpdateProgress("Activating disk protection", "Activating...");
                    await _uwf.Setup();
                }

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

                using (AutoFileDownloader downloader = new AutoFileDownloader(setup_response.BlobAddress, setup_response.CdnAddress, tempFile))
                {
                    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);


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

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

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

                UpdateProgress("Updating Database", "Connecting to local database...");
                LogManager.Log($"Connecting to local database at {localDataSource}...");
                DbManager db = DbManager.FromAddress(localDataSource.Address);

                LogManager.Log($"Ensuring {localDataSource.Catalog} database exists on the local machine...");
                if (!db.Exists(localDataSource.Catalog))
                {
                    UpdateProgress("Updating Database", "Creating new database...");
                    LogManager.Log("Database does not exist. Creating new database...");
                    db.Create(localDataSource.Catalog);
                }
                else
                {
                    LogManager.Log("Database exists.");
                }

                db.Dispose();

                LogManager.Log("Initializing database manager...");
                db = DbManager.FromDataSource(localDataSource);

                UpdateProgress("Updating Database", "Clearing current database...");
                LogManager.Log("Clearing database...");
                db.ClearDb();

                LogManager.Log("Disposing database manager.");
                db.Dispose();

                LogManager.Log($"Initializing {nameof(ExaminerSequenceConfigurationRunner)}...");

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

                ExaminerSequenceConfigurationRunner runner = new ExaminerSequenceConfigurationRunner(
                    Path.Combine(_newPackageTempFolder, "Provision Scripts", "config.xml"),
                    Path.Combine(_newPackageTempFolder, "Provision Scripts"),
                    setup_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.");
                }

                if (setup_response.SetupFirmware)
                {
                    //Updating firmware
                    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);
                    var handler = await op.UpgradeFirmware(stream, setup_response.IsDemo);
                    handler.Failed += (_, ex) =>
                    {
                        stream.Dispose();
                        OnFailed(ex, result, setup_response);
                    };
                    handler.Completed += (_, __) =>
                    {
                        UpdateProgress("Updating Firmware", "Firmware update completed successfully.");
                        stream.Dispose();
                        OnCompleted(new MachineSetupResult()
                        {
                            UpdatePackagePath = _newPackageTempFolder,
                        }, result, setup_response);
                    };
                    handler.Canceled += (_, __) =>
                    {
                        stream.Dispose();
                        OnFailed(new Exception("The operation has been canceled."), result, setup_response);
                    };
                    handler.Progress += (_, e) =>
                    {
                        UpdateProgress("Updating Firmware", e.Message, false, e.Current, e.Total);
                    };
                }
                else
                {
                    OnCompleted(new MachineSetupResult()
                    {
                        UpdatePackagePath = _newPackageTempFolder,
                    }, result, setup_response);
                }
            }
            catch (Exception ex)
            {
                OnFailed(ex, result, setup_response);
            }

            return await result.Task;
        }

        #endregion

        #region Protected Methods

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

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

        #endregion
    }
}