aboutsummaryrefslogtreecommitdiffstats
path: root/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Developer/Views/MachineJobSelectionView.xaml
blob: 1cb8536e0b00e87422d1a0c7bb12ede498f0fc05 (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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
<UserControl x:Class="Tango.MachineStudio.Developer.Views.MachineJobSelectionView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
             xmlns:global="clr-namespace:Tango.MachineStudio.Developer"
             xmlns:designer="clr-namespace:Tango.MachineStudio.MachineDesigner.Views;assembly=Tango.MachineStudio.MachineDesigner"
             xmlns:mahapps="http://metro.mahapps.com/winfx/xaml/controls"
             xmlns:autoComplete="clr-namespace:Tango.AutoComplete.Editors;assembly=Tango.AutoComplete"
             xmlns:automation="clr-namespace:Tango.MachineStudio.Common.Automation;assembly=Tango.MachineStudio.Common"
             xmlns:localConverters="clr-namespace:Tango.MachineStudio.Developer.Converters"
             xmlns:vm="clr-namespace:Tango.MachineStudio.Developer.ViewModels"
             xmlns:controls="clr-namespace:Tango.SharedUI.Controls;assembly=Tango.SharedUI"
             xmlns:converters="clr-namespace:Tango.SharedUI.Converters;assembly=Tango.SharedUI"
             xmlns:observables="clr-namespace:Tango.BL.Entities;assembly=Tango.BL"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:commonControls="clr-namespace:Tango.MachineStudio.Common.Controls;assembly=Tango.MachineStudio.Common"
             xmlns:local="clr-namespace:Tango.MachineStudio.Developer.Views"
             mc:Ignorable="d" 
             x:Name="control" d:DesignHeight="1080" d:DesignWidth="1920" Background="Transparent" d:DataContext="{d:DesignInstance Type=vm:MainViewVM, IsDesignTimeCreatable=False}" DataContext="{x:Static global:ViewModelLocator.MainViewVM}">

    <UserControl.Resources>
        <localConverters:SegmentToBrushConverterMulti x:Key="SegmentToBrushConverterMulti" />
        <converters:DateTimeUTCToShortDateTimeConverter x:Key="DateTimeUTCToShortDateTimeConverter"/>
        <localConverters:MillisecondsToTimeSpanConverter x:Key="MillisecondsToTimeSpanConverter" />
        <converters:GreaterThanToBooleanConverter x:Key="GreaterThanToBooleanConverter" />
        <converters:MathOperatorConverter x:Key="MathOperatorConverter" />
        <converters:BooleanInverseConverter x:Key="BooleanInverseConverter" />
    </UserControl.Resources>

    <Grid>
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="600"/>
                <ColumnDefinition Width="1*"/>
            </Grid.ColumnDefinitions>

            <Grid Background="#B1FFFFFF">
                <StackPanel>
                    <TextBlock Margin="40 20" FontSize="30" FontWeight="SemiBold" FontStyle="Italic">TARGET MACHINE</TextBlock>

                    <DockPanel Margin="40 0 40 0" IsEnabled="{Binding CanWork}">
                        <Button Command="{Binding ReloadMachinesCommand}" DockPanel.Dock="Right" ToolTip="Reload" Foreground="#404040" Margin="20 0 0 0" Padding="0" Width="40" Height="Auto" Style="{StaticResource MaterialDesignFlatButton}">
                            <materialDesign:PackIcon Kind="Refresh" Width="24" Height="24" />
                        </Button>
                        <autoComplete:AutoCompleteTextBox Provider="{Binding MachinesProvider}" LoadingContent="Loading..." FontSize="20" SelectedItem="{Binding SelectedMachine,Mode=TwoWay}" materialDesign:HintAssist.Hint="Serial Number" DisplayMember="SerialNumber">
                            <autoComplete:AutoCompleteTextBox.ItemTemplate>
                                <DataTemplate>
                                    <StackPanel>
                                        <TextBlock Text="{Binding SerialNumber}" FontWeight="Bold" FontStyle="Italic"></TextBlock>
                                        <TextBlock FontSize="11" Text="{Binding Name}" Foreground="Gray"></TextBlock>
                                    </StackPanel>
                                </DataTemplate>
                            </autoComplete:AutoCompleteTextBox.ItemTemplate>
                        </autoComplete:AutoCompleteTextBox>
                    </DockPanel>
                    <designer:MachineView Width="600" IsHitTestVisible="False" Margin="0 40 0 0" DataContext="{Binding SelectedMachine}" />
                    <Button Command="{Binding EditMachineCommand}" HorizontalAlignment="Right" Margin="0 10 20 20" Style="{StaticResource MaterialDesignFlatButton}">
                        <StackPanel Orientation="Horizontal">
                            <materialDesign:PackIcon VerticalAlignment="Center" Kind="Pencil"></materialDesign:PackIcon>
                            <TextBlock Margin="10 0 0 0">EDIT</TextBlock>
                        </StackPanel>
                    </Button>
                </StackPanel>
            </Grid>

            <Grid Grid.Column="1">
                <Grid HorizontalAlignment="Center" VerticalAlignment="Center" Width="1100" Height="700">
                    <DockPanel>
                        <Grid DockPanel.Dock="Top">
                            <StackPanel>
                                <StackPanel Orientation="Horizontal">
                                    <Image Source="../Images/machine-trans.png" RenderOptions.BitmapScalingMode="Fant" Width="50"></Image>
                                    <TextBlock VerticalAlignment="Center" FontWeight="SemiBold" Margin="30 0 0 0" FontSize="30" FontStyle="Italic">MACHINE JOBS</TextBlock>
                                </StackPanel>
                                <Rectangle HorizontalAlignment="Stretch" Margin="0 10 0 0" VerticalAlignment="Bottom" StrokeThickness="1" Height="3">
                                    <Rectangle.Fill>
                                        <LinearGradientBrush StartPoint="0,1" EndPoint="1,1">
                                            <GradientStop Offset="0" Color="Transparent" />
                                            <GradientStop Offset="0.5" Color="{StaticResource AccentColor}" />
                                            <GradientStop Offset="1.1" Color="Transparent" />
                                        </LinearGradientBrush>
                                    </Rectangle.Fill>
                                    <Rectangle.Stroke>
                                        <LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1">
                                            <GradientStop Offset="0" Color="Transparent" />
                                            <GradientStop Offset="0.5" Color="White" />
                                            <GradientStop Offset="1" Color="Transparent" />
                                        </LinearGradientBrush>
                                    </Rectangle.Stroke>
                                </Rectangle>
                            </StackPanel>

                            <StackPanel Orientation="Horizontal" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="0 0 10 0">
                                <materialDesign:PackIcon Kind="Magnify" Width="26" Height="26"/>
                                <TextBox Width="300" materialDesign:HintAssist.Hint="Search name, user, length..." Text="{Binding JobFilter,UpdateSourceTrigger=PropertyChanged}"></TextBox>
                            </StackPanel>
                        </Grid>

                        <Grid DockPanel.Dock="Bottom" Margin="0 20 0 0">

                            <StackPanel VerticalAlignment="Center" Orientation="Horizontal" HorizontalAlignment="Left" Margin="20 0 0 0">
                                <Button AutomationProperties.AutomationId="{x:Static automation:Developer.DeleteJobButton}" Margin="0 0 10 0" MinWidth="160" Height="50" Background="#FF7575" BorderBrush="#FF7575" Command="{Binding RemoveJobCommand}">
                                    <StackPanel Orientation="Horizontal">
                                        <materialDesign:PackIcon Kind="Delete" Width="20" Height="20" />
                                        <TextBlock Margin="5 0 0 0" FontSize="16">DELETE</TextBlock>
                                    </StackPanel>
                                </Button>
                                <Button AutomationProperties.AutomationId="{x:Static automation:Developer.DuplicateJobButton}" Margin="0 0 10 0" MinWidth="160" Height="50" Background="#FF995A" BorderBrush="#FF995A" Command="{Binding DuplicateJobCommand}">
                                    <StackPanel Orientation="Horizontal">
                                        <materialDesign:PackIcon Kind="ContentCopy" Width="20" Height="20" />
                                        <TextBlock Margin="5 0 0 0" FontSize="16">DUPLICATE</TextBlock>
                                    </StackPanel>
                                </Button>
                                <Button AutomationProperties.AutomationId="{x:Static automation:Developer.NewJobButton}" Margin="0 0 10 0" MinWidth="160" Height="50" Background="#65C682" BorderBrush="#65C682" Command="{Binding AddJobCommand}">
                                    <StackPanel Orientation="Horizontal">
                                        <materialDesign:PackIcon Kind="Plus" Width="20" Height="20" />
                                        <TextBlock Margin="5 0 0 0" FontSize="16">NEW JOB</TextBlock>
                                    </StackPanel>
                                </Button>
                            </StackPanel>
                            <StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
                                <Button Margin="50 0 10 0" MinWidth="200" Height="60" Command="{Binding LoadJobCommand}">
                                    <StackPanel Orientation="Horizontal">
                                        <TextBlock FontSize="18" VerticalAlignment="Center">LOAD JOB</TextBlock>
                                        <materialDesign:PackIcon Margin="5 0 0 0" Kind="ChevronRight" Width="30" Height="30" />
                                    </StackPanel>
                                </Button>
                            </StackPanel>
                        </Grid>

                        <Grid Margin="0 20 0 0">
                            <commonControls:LoadingPanel IsLoading="{Binding CanWork,Converter={StaticResource BooleanInverseConverter}}">
                                <controls:MultiSelectDataGrid MouseDoubleClick="MultiSelectDataGrid_MouseDoubleClick" AutomationProperties.AutomationId="{x:Static automation:Developer.JobsDataGrid}" Style="{StaticResource {x:Type DataGrid}}" CanUserAddRows="False" CanUserDeleteRows="False" CanUserResizeColumns="True" CanUserSortColumns="True" AutoGenerateColumns="False" Background="Transparent" ItemsSource="{Binding JobsCollectionView}" SelectedItem="{Binding SelectedMachineJob}" SelectedItemsList="{Binding SelectedJobs,Mode=TwoWay}">
                                    <DataGrid.CellStyle>
                                        <Style TargetType="DataGridCell" BasedOn="{StaticResource {x:Type DataGridCell}}">
                                            <Setter Property="BorderThickness" Value="0"/>
                                            <Setter Property="FocusVisualStyle" Value="{x:Null}"/>
                                            <Setter Property="VerticalContentAlignment" Value="Center"></Setter>
                                        </Style>
                                    </DataGrid.CellStyle>
                                    <DataGrid.Columns>
                                        <DataGridTemplateColumn>
                                            <DataGridTemplateColumn.CellTemplate>
                                                <DataTemplate DataType="{x:Type observables:Job}">
                                                    <ContentControl>
                                                        <ContentControl.Style>
                                                            <Style TargetType="ContentControl">
                                                                <Setter Property="Content">
                                                                    <Setter.Value>
                                                                        <Image Source="../Images/rgb.png" Width="40" Margin="5"></Image>
                                                                    </Setter.Value>
                                                                </Setter>
                                                                <Style.Triggers>
                                                                    <DataTrigger Binding="{Binding HasEmbroideryFile}" Value="True">
                                                                        <Setter Property="Content">
                                                                            <Setter.Value>
                                                                                <Button ToolTip="Press to display the attached embroidery file information" Style="{StaticResource emptyButton}" Cursor="Hand" Command="{Binding DataContext.DisplayJobEmbroideryFileCommand, Source={x:Reference control}}" CommandParameter="{Binding}">
                                                                                    <Image Source="../Images/job-emb.png" Width="40" Margin="5"></Image>
                                                                                </Button>
                                                                            </Setter.Value>
                                                                        </Setter>
                                                                    </DataTrigger>
                                                                </Style.Triggers>
                                                            </Style>
                                                        </ContentControl.Style>
                                                    </ContentControl>
                                                </DataTemplate>
                                            </DataGridTemplateColumn.CellTemplate>
                                        </DataGridTemplateColumn>
                                        <DataGridTemplateColumn Header="NAME" CanUserSort="True" SortMemberPath="Name" MaxWidth="100">
                                            <DataGridTemplateColumn.CellTemplate>
                                                <DataTemplate>
                                                    <TextBlock Text="{Binding Name}" ToolTip="{Binding Name}"  TextWrapping="Wrap" MaxHeight="40" TextTrimming="CharacterEllipsis" VerticalAlignment="Center" FontSize="14"></TextBlock>
                                                </DataTemplate>
                                            </DataGridTemplateColumn.CellTemplate>
                                        </DataGridTemplateColumn>
                                        <DataGridTemplateColumn Header="CREATION DATE" Width="150" CanUserSort="True" SortMemberPath="CreationDate">
                                            <DataGridTemplateColumn.CellTemplate>
                                                <DataTemplate>
                                                    <TextBlock Text="{Binding CreationDate,Converter={StaticResource DateTimeUTCToShortDateTimeConverter}}" VerticalAlignment="Center" FontSize="14"></TextBlock>
                                                </DataTemplate>
                                            </DataGridTemplateColumn.CellTemplate>
                                        </DataGridTemplateColumn>
                                        <DataGridTemplateColumn Header="LAST MODIFIED" Width="150" CanUserSort="True" SortMemberPath="LastUpdated">
                                            <DataGridTemplateColumn.CellTemplate>
                                                <DataTemplate>
                                                    <TextBlock Text="{Binding LastUpdated,Converter={StaticResource DateTimeUTCToShortDateTimeConverter}}" VerticalAlignment="Center" FontSize="14"></TextBlock>
                                                </DataTemplate>
                                            </DataGridTemplateColumn.CellTemplate>
                                        </DataGridTemplateColumn>
                                        <DataGridTemplateColumn Header="LAST RUN" Width="150" CanUserSort="True" SortMemberPath="LastRun">
                                            <DataGridTemplateColumn.CellTemplate>
                                                <DataTemplate>
                                                    <TextBlock Text="{Binding LastRun,Converter={StaticResource DateTimeUTCToShortDateTimeConverter},FallbackValue='Never',TargetNullValue='Never'}" VerticalAlignment="Center" FontSize="14"></TextBlock>
                                                </DataTemplate>
                                            </DataGridTemplateColumn.CellTemplate>
                                        </DataGridTemplateColumn>
                                        <DataGridTemplateColumn Header="USER" Width="100" CanUserSort="True" SortMemberPath="User">
                                            <DataGridTemplateColumn.CellTemplate>
                                                <DataTemplate>
                                                    <TextBlock Text="{Binding User.Contact.FirstName}" VerticalAlignment="Center" FontSize="14"></TextBlock>
                                                </DataTemplate>
                                            </DataGridTemplateColumn.CellTemplate>
                                        </DataGridTemplateColumn>
                                        <DataGridTemplateColumn Header="LENGTH" Width="100" CanUserSort="True" SortMemberPath="Length">
                                            <DataGridTemplateColumn.CellTemplate>
                                                <DataTemplate>
                                                    <TextBlock VerticalAlignment="Center" FontSize="14">
                                                    <Run Text="{Binding Length,Mode=OneWay,StringFormat=N2}"></Run>
                                                    <Run Text="m" Foreground="Gray"></Run>
                                                    </TextBlock>
                                                </DataTemplate>
                                            </DataGridTemplateColumn.CellTemplate>
                                        </DataGridTemplateColumn>
                                        <DataGridTemplateColumn Header="DURATION" Width="100" CanUserSort="True" SortMemberPath="EstimatedDurationMili">
                                            <DataGridTemplateColumn.CellTemplate>
                                                <DataTemplate>
                                                    <TextBlock VerticalAlignment="Center" FontSize="14">
                                                    <Run Text="{Binding EstimatedDurationMili,Converter={StaticResource MillisecondsToTimeSpanConverter},Mode=OneWay,StringFormat=hh\\:mm\\:ss,TargetNullValue='Unknown'}"></Run>
                                                    </TextBlock>
                                                </DataTemplate>
                                            </DataGridTemplateColumn.CellTemplate>
                                        </DataGridTemplateColumn>
                                        <DataGridTemplateColumn Width="1*" Header="SEGMENTS" CanUserSort="True" SortMemberPath="Segments.Count">
                                            <DataGridTemplateColumn.CellTemplate>
                                                <DataTemplate>
                                                    <StackPanel Orientation="Horizontal">
                                                        <ItemsControl HorizontalAlignment="Right" ItemsSource="{Binding Segments}">
                                                            <ItemsControl.ItemsPanel>
                                                                <ItemsPanelTemplate>
                                                                    <StackPanel Orientation="Horizontal" HorizontalAlignment="Left" MaxWidth="110">

                                                                    </StackPanel>
                                                                </ItemsPanelTemplate>
                                                            </ItemsControl.ItemsPanel>
                                                            <ItemsControl.ItemTemplate>
                                                                <DataTemplate>
                                                                    <Border Width="25" Height="25" Margin="10 0 0 0" BorderThickness="1" BorderBrush="DimGray" CornerRadius="3" Background="{Binding SegmentBrush}">

                                                                    </Border>
                                                                </DataTemplate>
                                                            </ItemsControl.ItemTemplate>
                                                        </ItemsControl>
                                                        <StackPanel Orientation="Horizontal" Margin="0 0 0 0" VerticalAlignment="Center">
                                                            <StackPanel.Style>
                                                                <Style TargetType="StackPanel">
                                                                    <Setter Property="Visibility" Value="Collapsed"></Setter>
                                                                    <Style.Triggers>
                                                                        <DataTrigger Binding="{Binding Segments.Count,Converter={StaticResource GreaterThanToBooleanConverter},ConverterParameter=3}" Value="True">
                                                                            <Setter Property="Visibility" Value="Visible"></Setter>
                                                                        </DataTrigger>
                                                                    </Style.Triggers>
                                                                </Style>
                                                            </StackPanel.Style>
                                                            <materialDesign:PackIcon Kind="Plus" Width="18" Height="18" VerticalAlignment="Center" />
                                                            <TextBlock Margin="0 -3 0 0" Text="{Binding Segments.Count,Converter={StaticResource MathOperatorConverter},ConverterParameter='-3'}" FontSize="16" VerticalAlignment="Center" FontWeight="SemiBold" FontStyle="Italic"></TextBlock>
                                                        </StackPanel>
                                                    </StackPanel>
                                                </DataTemplate>
                                            </DataGridTemplateColumn.CellTemplate>
                                        </DataGridTemplateColumn>
                                    </DataGrid.Columns>
                                </controls:MultiSelectDataGrid>
                            </commonControls:LoadingPanel>
                        </Grid>
                    </DockPanel>

                    <Button Command="{Binding ImportEmbroideryFileCommand}" Margin="20 0 0 -100" Foreground="Black" FontSize="16" VerticalAlignment="Bottom" HorizontalAlignment="Left" Style="{StaticResource emptyButton}" Cursor="Hand">
                        <StackPanel Orientation="Horizontal">
                            <Image Source="../Images/sewing-machine.png" Width="32"></Image>
                            <TextBlock VerticalAlignment="Center" Margin="10 0 0 0" TextDecorations="Underline">Import Embroidery File</TextBlock>
                        </StackPanel>
                    </Button>
                </Grid>
            </Grid>
        </Grid>
    </Grid>
</UserControl>
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.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.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;

namespace Tango.PPC.Common.MachineUpdate
{
    public class MachineUpdateManager : ExtendedObject, IMachineUpdateManager
    {
        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;

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

        #endregion

        #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

        /// <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, IPackageRunner packageRunner)
        {
            _client = ppcWebClient;
            _machineProvider = machineProvider;
            _app_manager = applicationManager;
            _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.Start();
        }

        #endregion

        #region Event Handlers

        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)
        {
            return _client.Login(new LoginRequest()
            {
                Mode = LoginMode.Machine,
                SerialNumber = serialNumber,
            });
        }

        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;
        }

        #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(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 serialNumber = _machineProvider.Machine.SerialNumber;

            try
            {
                _isUpdating = true;

                var machineServiceAddress = _settings.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.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.
                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;

                update_response = await _client.MachineUpdate(request);

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

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

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

                using (AutoFileDownloader downloader = new AutoFileDownloader(update_response.BlobAddress, update_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...");

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

                //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(update_response.Version);
                    }
                    catch (Exception ex)
                    {
                        LogManager.Log(ex, "Error parsing new version string for package runner.");
                    }

                    await _packageRunner.Run(PackageType.Pre, updateVersion, packagesFolder);
                }
                catch (Exception ex)
                {
                    LogManager.Log(ex, "Error running pre-update packages...");
                }

                //Synchronize database
                UpdateProgress("Updating Database", "Initializing...");

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

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

                //Updating firmware
                if (setupFirmware)
                {
                    UpdateProgress("Updating Firmware", "Connecting to firmware device...");
                    LogManager.Log("");
                    LogManager.Log("-------------------------------------------------------------------------");
                    LogManager.Log("Updating Firmware...");

                    UpdateProgress("Updating Firmware", "Loading firmware package...");
                    var tfpPath = Path.Combine(_newPackageTempFolder, "firmware_package.tfp");
                    var stream = new FileStream(tfpPath, FileMode.Open);

                    if (!_machineProvider.Machine.IsDemo)
                    {
                        if (setupFPGA)
                        {
                            op.FirmwareUpgradeMode = FirmwareUpgradeModes.DFU | FirmwareUpgradeModes.TFP_PACKAGE;
                        }
                        else
                        {
                            op.FirmwareUpgradeMode = FirmwareUpgradeModes.DFU;
                        }
                    }
                    else
                    {
                        op.FirmwareUpgradeMode = FirmwareUpgradeModes.TFP_PACKAGE;
                    }

                    var handler = await op.UpgradeFirmware(stream);
                    handler.Failed += (_, ex) =>
                    {
                        stream.Dispose();
                        OnFailed(ex, result, update_response, performDatabaseRollback, dbBackupFile, backupsFolder, null, localDataSource, _newPackageTempFolder);
                    };
                    handler.Completed += (_, __) =>
                    {
                        UpdateProgress("Updating Firmware", "Firmware update completed successfully.");
                        stream.Dispose();
                        OnCompleted(new MachineUpdateResult()
                        {
                            UpdatePackagePath = _newPackageTempFolder,
                        }, result, update_response, null, backupsFolder, localDataSource);
                    };
                    handler.Canceled += (_, __) =>
                    {
                        stream.Dispose();
                        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);
                    };
                }
                else
                {
                    OnCompleted(new MachineUpdateResult()
                    {
                        UpdatePackagePath = _newPackageTempFolder,
                    }, result, update_response, null, backupsFolder, localDataSource);
                }
            }
            catch (Exception ex)
            {
                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.
        /// </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>(() =>
            {
                _isUpdating = true;

                var machineServiceAddress = _settings.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();

                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;

                update_response = _client.CheckForUpdates(request).Result;

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

                _isUpdating = false;

                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)
        {
            _updateStartDate = DateTime.UtcNow;
            _logs.Clear();

            return Task.Factory.StartNew(() =>
            {
                _isUpdating = true;
                UpdateDBResponse update_response = null;
                var localDataSource = SettingsManager.Default.GetOrCreate<CoreSettings>().DataSource;
                bool performDatabaseRollback = false;
                String dbBackupFile = null;

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

                    update_response = dbCompareResult.UpdateDBResponse;

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

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

                    ExaminerSequenceConfiguration config_sequence = ExaminerSequenceConfiguration.FromFile(config_file);

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

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

                    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)
        {
            return Task.Factory.StartNew<DbCompareResult>(() =>
            {
                var machineServiceAddress = _settings.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;
                request.ApplicationVersion = _app_manager.Version.ToString();
                request.FirmwareVersion = _app_manager.FirmwareVersion.ToString();

                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)
                    {
                        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,
                    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 async Task<MachineUpdateResult> UpdateFromTUP(string fileName, 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;
            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
            {
                _isUpdating = true;

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

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

                    await _packageRunner.Run(PackageType.Pre, updateVersion, packagesFolder);
                }
                catch (Exception ex)
                {
                    LogManager.Log(ex, "Error running pre-update packages...");
                }

                //Synchronize database
                UpdateProgress("Updating Database", "Initializing...");

                UpdateProgress("Updating Database", "Connecting to local database...");
                LogManager.Log("Initializing database manager...");
                DbManager db = DbManager.FromDataSource(localDataSource);

                LogManager.Log("Checking Tango database exists on the local machine...");
                if (!db.Exists(localDataSource.Catalog))
                {
                    throw new InvalidProgramException("Database tango does not exists.");
                }

                UpdateProgress("Updating Database", "Creating database backup...");

                //Create Database Backup
                try
                {
                    Directory.CreateDirectory(backupsFolder);
                    dbBackupFile = $"{backupsFolder}\\{Path.GetRandomFileName()}.bak";
                    LogManager.Log($"Creating database backup to '{dbBackupFile}'...");
                    await Task.Factory.StartNew(() => db.Backup(localDataSource.Catalog, dbBackupFile));
                    performDatabaseRollback = true;
                    LogManager.Log("Database backup created successfully.");
                }
                catch (Exception ex)
                {
                    throw LogManager.Log(ex, "Update manager error while trying to create a database backup.");
                }

                LogManager.Log("Extracting database file from package...");
                File.Copy(Path.Combine(_newPackageTempFolder, tempDbFileName), Path.Combine(backupsFolder, tempDbFileName));

                LogManager.Log("Restoring package database as a new database...");
                db.RestoreAsNew(tempDbName, Path.Combine(backupsFolder, tempDbFileName), backupsFolder);

                Core.DataSource tempDbDataSource = new Core.DataSource();
                tempDbDataSource.Address = localDataSource.Address;
                tempDbDataSource.IntegratedSecurity = localDataSource.IntegratedSecurity;
                tempDbDataSource.Type = localDataSource.Type;
                tempDbDataSource.Catalog = tempDbName;

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

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

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

                ExaminerSequenceConfigurationRunner runner = new ExaminerSequenceConfigurationRunner(
                    Path.Combine(_newPackageTempFolder, "Update Scripts", "config.xml"),
                    Path.Combine(_newPackageTempFolder, "Update Scripts"),
                        tempDbDataSource,
                        localDataSource,
                        serialNumber);

                runner.Log += (x, msg) =>
                {
                    LogManager.Log(msg);
                    ProgressLog?.Invoke(this, msg);
                };

                runner.ScriptExecuting += (x, item) =>
                {
                    LogManager.Log($"Executing script {item.ToString()}...");
                    UpdateProgress("Updating Database", item.Name + "...");
                };

                LogManager.Log("Starting synchronization process...");

                try
                {
                    await runner.Run();
                    LogManager.Log("Synchronization completed successfully!");
                    UpdateProgress("Updating Database", "Database synchronization completed successfully.");
                }
                catch (Exception ex)
                {
                    throw LogManager.Log(ex, "Update manager error while trying to synchronize database.");
                }

                LogManager.Log("Getting setup firmware/fpga directly from db..");

                using (var dbManager = DbManager.FromDataSource(localDataSource))
                {
                    try
                    {
                        String firmware = dbManager.GetValue($"SELECT TOP 1 * FROM MACHINES WHERE SERIAL_NUMBER = '{serialNumber}'", "SETUP_FIRMWARE");
                        String fpga = dbManager.GetValue($"SELECT TOP 1 * FROM MACHINES WHERE SERIAL_NUMBER = '{serialNumber}'", "SETUP_FPGA");

                        setupFirmware = bool.Parse(firmware);
                        setupFPGA = bool.Parse(fpga);
                    }
                    catch (Exception ex)
                    {
                        LogManager.Log(ex, "Error getting new values of SETUP_FIRMWARE and SETUP_FPGA.");
                    }
                }

                //Updating firmware
                if (setupFirmware)
                {
                    UpdateProgress("Updating Firmware", "Connecting to firmware device...");
                    LogManager.Log("");
                    LogManager.Log("-------------------------------------------------------------------------");
                    LogManager.Log("Updating Firmware...");

                    UpdateProgress("Updating Firmware", "Loading firmware package...");
                    var tfpPath = Path.Combine(_newPackageTempFolder, "firmware_package.tfp");
                    var stream = new FileStream(tfpPath, FileMode.Open);

                    if (!_machineProvider.Machine.IsDemo)
                    {
                        if (setupFPGA)
                        {
                            op.FirmwareUpgradeMode = FirmwareUpgradeModes.DFU | FirmwareUpgradeModes.TFP_PACKAGE;
                        }
                        else
                        {
                            op.FirmwareUpgradeMode = FirmwareUpgradeModes.DFU;
                        }
                    }
                    else
                    {
                        op.FirmwareUpgradeMode = FirmwareUpgradeModes.TFP_PACKAGE;
                    }

                    var handler = await op.UpgradeFirmware(stream);
                    handler.Failed += (_, ex) =>
                    {
                        stream.Dispose();
                        OnFailed(ex, result, null, performDatabaseRollback, dbBackupFile, backupsFolder, tempDbName, localDataSource, _newPackageTempFolder, 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, false, 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...");

                if (!_machineProvider.Machine.IsDemo)
                {
                    op.FirmwareUpgradeMode = FirmwareUpgradeModes.DFU | FirmwareUpgradeModes.TFP_PACKAGE;
                }
                else
                {
                    op.FirmwareUpgradeMode = FirmwareUpgradeModes.TFP_PACKAGE;
                }

                var handler = await op.UpgradeFirmware(stream);
                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, false, e.Current, e.Total);
                };
            }
            catch (Exception ex)
            {
                try
                {
                    if (stream != null)
                    {
                        stream.Dispose();
                    }
                }
                catch { }

                OnFailed(ex, result, version);
            }

            await result.Task;
        }

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

            Version previousVersion = null;
            String str = _settings.PreviousApplicationVersion;

            if (Version.TryParse(str, out previousVersion))
            {
                return _packageRunner.Run(PackageType.Post, previousVersion, packagesFolder);
            }
            else
            {
                throw new InvalidCastException($"Error parsing the previous version string '{str}'.");
            }
        }

        #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

        #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(_machineProvider.Machine.SerialNumber);
                    if (response.IsUpdateAvailable || response.IsDatabaseUpdateAvailable)
                    {
                        _checkForUpdateTimer.Interval = TimeSpan.FromMinutes(60).TotalMilliseconds;
                        LogManager.Log($"New {(response.IsDatabaseUpdateAvailable ? "database updates" : "application version")} detected ({response.Version}). Raising event...");
                        UpdateAvailable?.Invoke(this, response);
                    }
                }
                catch { }

                _checkForUpdateTimer.Start();
            }
        }

        #endregion
    }
}